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,:,:]