Rec 的用户项目矩阵中共同评级项目的矢量化计算。系统
Vectorized calculation of co-rated items in user-item matrix for Rec. System
我正在研究推荐系统,部分计算是将权重作为许多共同评分的项目添加到距离度量中。考虑下面的矩阵:
行代表用户,列代表项目。所以比如第一行这个用户给了产品A 2分,没有评价产品B。然后产品C得了3分,产品D得了2分等等...
ratings = np.array([[2,0,3,2,0,1],
[2,5,3,0,0,2],
[4,3,0,0,0,5],
[3,0,2,4,0,5]])
现在,如果两个用户的共同评价项目少于 3 个,则将共同评价项目的总数除以 3,然后乘以距离度量(虽然不包括这一步)。
下面是我如何创建这些权重,有效:
#Is False when rating is 0 because I want to exclude those
ratings_aux = ratings > 0
co_rated_aux = np.zeros((ratings.shape[0], ratings.shape[0]))
co_rated = co_rated_aux.copy()
for row in xrange(co_rated.shape[0]):
for clmn in xrange(co_rated.shape[1]):
#If both are True, sum them
co_rated_aux[row,clmn] = np.sum((ratings_aux[row,:] == ratings_aux[clmn,:]) & (ratings_aux[row,:] != False) & (ratings_aux[clmn,:] != False))
if co_rated_aux[row,clmn] <= 3:
co_rated[row,clmn] = co_rated_aux[row,clmn]/float(3)
else:
co_rated[row,clmn] = 1
在这里您可以看到共同评价的项目数:
(例如,第一个和第二个用户对三个相同的项目进行了评分)
print co_rated_aux
[[ 4. 3. 2. 4.]
[ 3. 4. 3. 3.]
[ 2. 3. 3. 2.]
[ 4. 3. 2. 4.]]
最后的权重:(如果两个用户有3个以上的共同评分项目,相似性度量将保持不变。如果更少,则减少度量)
print co_rated
[[ 1. 1. 0.66666667 1. ]
[ 1. 1. 1. 1. ]
[ 0.66666667 1. 1. 0.66666667]
[ 1. 1. 0.66666667 1. ]]
但是,这种计算非常难看,并且对于更大的数组会非常慢。我试图仅使用向量运算来摆脱 for 循环,但我真的不知道如何去做。
我很乐意提出任何建议。
一种矢量化方法是 broadcasting
(不过要注意内存使用情况,但作为 booelan 数组对 RAM 的负担相对较小)-
mask = ratings_aux[None,:,:] == ratings_aux[:,None,:]
mask &= (ratings_aux != False)[None,:,:] & (ratings_aux != False)[:,None,:]
co_rated_aux_out = mask.sum(2) # or np.count_nonzero(mask,axis=2)
co_rated_out = np.where(co_rated_aux_out <=3, co_rated_aux_out/3.0,1)
基本上在前两个步骤中,我们保持最后一个轴对齐,"spreading-out" 来自输入数组的第一个轴与自身保持对齐,以进行逐元素比较。这为我们在这两个步骤中的每一步都提供了一个 3D 数组。这个 "spreading-out" 符合 broadcasting
的规则,因为我们在这两个版本的输入数组中分别引入了 singleton
dimensions/axes 和 np.newaxis/None
。
仔细观察会发现,由于我们在第一步已经检查了相等性,所以我们可以在第二步跳过第二项,像这样-
mask &= (ratings_aux != False)[None,:,:]
同样,ratings_aux
是一个布尔数组。因此,ratings_aux != False
本质上是真正的元素,即数组本身。所以,那里有进一步的改进-
mask &= ratings_aux[None,:,:]
我正在研究推荐系统,部分计算是将权重作为许多共同评分的项目添加到距离度量中。考虑下面的矩阵:
行代表用户,列代表项目。所以比如第一行这个用户给了产品A 2分,没有评价产品B。然后产品C得了3分,产品D得了2分等等...
ratings = np.array([[2,0,3,2,0,1],
[2,5,3,0,0,2],
[4,3,0,0,0,5],
[3,0,2,4,0,5]])
现在,如果两个用户的共同评价项目少于 3 个,则将共同评价项目的总数除以 3,然后乘以距离度量(虽然不包括这一步)。
下面是我如何创建这些权重,有效:
#Is False when rating is 0 because I want to exclude those
ratings_aux = ratings > 0
co_rated_aux = np.zeros((ratings.shape[0], ratings.shape[0]))
co_rated = co_rated_aux.copy()
for row in xrange(co_rated.shape[0]):
for clmn in xrange(co_rated.shape[1]):
#If both are True, sum them
co_rated_aux[row,clmn] = np.sum((ratings_aux[row,:] == ratings_aux[clmn,:]) & (ratings_aux[row,:] != False) & (ratings_aux[clmn,:] != False))
if co_rated_aux[row,clmn] <= 3:
co_rated[row,clmn] = co_rated_aux[row,clmn]/float(3)
else:
co_rated[row,clmn] = 1
在这里您可以看到共同评价的项目数: (例如,第一个和第二个用户对三个相同的项目进行了评分)
print co_rated_aux
[[ 4. 3. 2. 4.]
[ 3. 4. 3. 3.]
[ 2. 3. 3. 2.]
[ 4. 3. 2. 4.]]
最后的权重:(如果两个用户有3个以上的共同评分项目,相似性度量将保持不变。如果更少,则减少度量)
print co_rated
[[ 1. 1. 0.66666667 1. ]
[ 1. 1. 1. 1. ]
[ 0.66666667 1. 1. 0.66666667]
[ 1. 1. 0.66666667 1. ]]
但是,这种计算非常难看,并且对于更大的数组会非常慢。我试图仅使用向量运算来摆脱 for 循环,但我真的不知道如何去做。
我很乐意提出任何建议。
一种矢量化方法是 broadcasting
(不过要注意内存使用情况,但作为 booelan 数组对 RAM 的负担相对较小)-
mask = ratings_aux[None,:,:] == ratings_aux[:,None,:]
mask &= (ratings_aux != False)[None,:,:] & (ratings_aux != False)[:,None,:]
co_rated_aux_out = mask.sum(2) # or np.count_nonzero(mask,axis=2)
co_rated_out = np.where(co_rated_aux_out <=3, co_rated_aux_out/3.0,1)
基本上在前两个步骤中,我们保持最后一个轴对齐,"spreading-out" 来自输入数组的第一个轴与自身保持对齐,以进行逐元素比较。这为我们在这两个步骤中的每一步都提供了一个 3D 数组。这个 "spreading-out" 符合 broadcasting
的规则,因为我们在这两个版本的输入数组中分别引入了 singleton
dimensions/axes 和 np.newaxis/None
。
仔细观察会发现,由于我们在第一步已经检查了相等性,所以我们可以在第二步跳过第二项,像这样-
mask &= (ratings_aux != False)[None,:,:]
同样,ratings_aux
是一个布尔数组。因此,ratings_aux != False
本质上是真正的元素,即数组本身。所以,那里有进一步的改进-
mask &= ratings_aux[None,:,:]