基于用户的协同过滤
协同过滤
基本思想
协同过滤(Collaborative Filtering)推荐算法是最经典、最常用的推荐算法。基本思想是:
-
根据用户之前的喜好以及其他兴趣相近的用户的选择来给用户推荐物品。
- 基于对用户历史行为数据的挖掘发现用户的喜好偏向, 并预测用户可能喜好的产品进行推荐。
- 一般是仅仅基于用户的行为数据(评价、购买、下载等), 而不依赖于项的任何附加信息(物品自身特征)或者用户的任何附加信息(年龄, 性别等)。
-
目前应用比较广泛的协同过滤算法是基于邻域的方法,主要有:
- 基于用户的协同过滤算法(UserCF):给用户推荐和他兴趣相似的其他用户喜欢的产品。
- 基于物品的协同过滤算法(ItemCF):给用户推荐和他之前喜欢的物品相似的物品。
不管是 UserCF 还是 ItemCF 算法, 重点是计算用户之间(或物品之间)的相似度。
相似性度量方法
1 杰卡德(Jaccard)相似系数
Jaccard
系数是衡量两个集合的相似度一种指标,计算公式如下:
s
i
m
u
v
=
∣
N
(
u
)
∩
N
(
v
)
∣
∣
N
(
u
)
∣
∪
∣
N
(
v
)
∣
sim_{uv}=\frac{|N(u) \cap N(v)|}{|N(u)| \cup|N(v)|}
simuv=∣N(u)∣∪∣N(v)∣∣N(u)∩N(v)∣
-
其中 N ( u ) N(u) N(u), N ( v ) N(v) N(v) 分别表示用户 u u u 和用户 v v v 交互物品的集合。
-
对于用户 u u u 和 v v v ,该公式反映了两个交互物品交集的数量占这两个用户交互物品并集的数量的比例。
由于杰卡德相似系数一般无法反映具体用户的评分喜好信息,所以常用来评估用户是否会对某物品进行打分, 而不是预估用户会对某物品打多少分。
2 余弦相似度
余弦相似度衡量了两个向量的夹角,夹角越小越相似。
详细可看:【推荐系统->相似度算法】余弦相似度
从向量的角度进行描述,令矩阵 A A A 为用户-物品交互矩阵,矩阵的行表示用户,列表示物品。
1 设用户和物品数量分别为 m , n m,n m,n,交互矩阵 A A A就是一个 m m m 行 n n n 列的矩阵。
2 矩阵中的元素均为 0 / 1 0/1 0/1。若用户 i i i 对物品 j j j 存在交互,那么 A i , j = 1 A_{i,j}=1 Ai,j=1,否则为 0 0 0 。
3 那么,用户之间的相似度可以表示为:
s
i
m
u
v
=
c
o
s
(
u
,
v
)
=
u
⋅
v
∣
u
∣
⋅
∣
v
∣
sim_{uv} = cos(u,v) =\frac{u\cdot v}{|u|\cdot |v|}
simuv=cos(u,v)=∣u∣⋅∣v∣u⋅v
- 向量 u , v u,v u,v 在形式都是 one-hot 类型, u ⋅ v u\cdot v u⋅v 表示向量点积。
在 sklearn
中,余弦相似度的实现:
from sklearn.metrics.pairwise import cosine_similarity
i = [1, 0, 0, 0]
j = [1, 0, 1, 0]
cosine_similarity([i, j])
3 皮尔逊相关系数
在用户之间的余弦相似度计算时,将用户向量的内积展开为各元素乘积和:
s
i
m
u
v
=
∑
i
r
u
i
∗
r
v
i
∑
i
r
u
i
2
∑
i
r
v
i
2
sim_{uv} = \frac{\sum_i r_{ui}*r_{vi}}{\sqrt{\sum_i r_{ui}^2}\sqrt{\sum_i r_{vi}^2}}
simuv=∑irui2∑irvi2∑irui∗rvi
- 其中, r u i , r v i r_{ui},r_{vi} rui,rvi 分别表示用户 u u u 和用户 v v v 对物品 i i i 是否有交互(或具体评分值)。
皮尔逊相关系数与余弦相似度的计算公式非常的类似,如下:
s
i
m
(
u
,
v
)
=
∑
i
∈
I
(
r
u
i
−
r
ˉ
u
)
(
r
v
i
−
r
ˉ
v
)
∑
i
∈
I
(
r
u
i
−
r
ˉ
u
)
2
∑
i
∈
I
(
r
v
i
−
r
ˉ
v
)
2
sim(u,v)=\frac{\sum_{i\in I}(r_{ui}-\bar r_u)(r_{vi}-\bar r_v)}{\sqrt{\sum_{i\in I }(r_{ui}-\bar r_u)^2}\sqrt{\sum_{i\in I }(r_{vi}-\bar r_v)^2}}
sim(u,v)=∑i∈I(rui−rˉu)2∑i∈I(rvi−rˉv)2∑i∈I(rui−rˉu)(rvi−rˉv)
- 其中, r u i , r v i r_{ui},r_{vi} rui,rvi 分别表示用户 u u u 和用户 v v v 对物品 i i i 是否有交互(或具体评分值);
- r ˉ u , r ˉ v \bar r_u, \bar r_v rˉu,rˉv 分别表示用户 u u u 和用户 v v v 交互的所有物品交互数量或者评分的平均值;
相较于余弦相似度,皮尔逊相关系数通过使用用户的平均分对各独立评分进行修正,减小了用户评分偏置的影响。在scipy
中,皮尔逊相关系数的实现:
from scipy.stats import pearsonr
i = [1, 0, 0, 0]
j = [1, 0.5, 0.5, 0]
pearsonr(i, j)
适用场景
- J a c c a r d Jaccard Jaccard 相似度表示两个集合的交集元素个数在并集中所占的比例 ,所以适用于隐式反馈数据(0-1)。
- 余弦相似度在度量文本相似度、用户相似度、物品相似度的时候都较为常用。
- 皮尔逊相关度,实际上也是一种余弦相似度。不过先对向量做了中心化,范围在
−
1
-1
−1 到
1
1
1。
- 相关度量的是两个变量的变化趋势是否一致,两个随机变量是不是同增同减。
- 不适合用作计算布尔值向量(0-1)之间相关度。
基于用户的协同过滤
基本思想
基于用户的协同过滤(UserCF):
- 例如,我们要对用户 A A A 进行物品推荐,可以先找到和他有相似兴趣的其他用户。
- 然后,将共同兴趣用户喜欢的,但用户 A A A 未交互过的物品推荐给 A A A。

计算过程
以下图为例,给用户推荐物品的过程可以形象化为一个猜测用户对物品进行打分的任务,表格里面是5个用户对于5件物品的一个打分情况,就可以理解为用户对物品的喜欢程度。
UserCF算法的两个步骤:
- 首先,根据前面的这些打分情况(或者说已有的用户向量)计算一下 Alice 和用户1, 2, 3, 4的相似程度, 找出与 Alice 最相似的 n 个用户。
- 根据这 n 个用户对物品 5 的评分情况和与 Alice 的相似程度会猜测出 Alice 对物品5的评分。如果评分比较高的话, 就把物品5推荐给用户 Alice, 否则不推荐。
具体过程:
1 计算用户之间的相似度
- 根据 1.2 节的几种方法, 我们可以计算出各用户之间的相似程度。对于用户 Alice,选取出与其最相近的 N N N 个用户。
2 计算用户对新物品的评分预测
- 常用的方式之一:利用目标用户与相似用户之间的相似度以及相似用户对物品的评分,来预测目标用户对候选物品的评分估计:
R u , p = ∑ s ∈ S ( w u , s ⋅ R s , p ) ∑ s ∈ S w u , s R_{\mathrm{u}, \mathrm{p}}=\frac{\sum_{\mathrm{s} \in S}\left(w_{\mathrm{u}, \mathrm{s}} \cdot R_{\mathrm{s}, \mathrm{p}}\right)}{\sum_{\mathrm{s} \in S} w_{\mathrm{u}, \mathrm{s}}} Ru,p=∑s∈Swu,s∑s∈S(wu,s⋅Rs,p)
- 其中,权重 w u , s w_{u,s} wu,s 是用户 u u u 和用户 s s s 的相似度, R s , p R_{s,p} Rs,p 是用户 s s s 对物品 p p p 的评分。
- 另一种方式:考虑到用户评分的偏置,即有的用户喜欢打高分, 有的用户喜欢打低分的情况。公式如下:
R u , p = R ˉ u + ∑ s ∈ S ( w u , s ⋅ ( R s , p − R ˉ s ) ) ∑ s ∈ S w u , s R_{\mathrm{u}, \mathrm{p}}=\bar{R}_{u} + \frac{\sum_{\mathrm{s} \in S}\left(w_{\mathrm{u}, \mathrm{s}} \cdot \left(R_{s, p}-\bar{R}_{s}\right)\right)}{\sum_{\mathrm{s} \in S} w_{\mathrm{u}, \mathrm{s}}} Ru,p=Rˉu+∑s∈Swu,s∑s∈S(wu,s⋅(Rs,p−Rˉs))
- 其中, R ˉ s \bar{R}_{s} Rˉs 表示用户 s s s 对物品的历史平均评分。
3 对用户进行物品推荐
- 在获得用户 u u u 对不同物品的评价预测后, 最终的推荐列表根据预测评分进行排序得到。
手动计算:
根据上面的问题, 下面手动计算 Alice 对物品 5 的得分:
-
计算 Alice 与其他用户的相似度(基于皮尔逊相关系数)
- 手动计算 Alice 与用户 1 之间的相似度:
用户向量 Alice : ( 5 , 3 , 4 , 4 ) , user1 : ( 3 , 1 , 2 , 3 ) , user2 : ( 4 , 3 , 4 , 3 ) , user3 : ( 3 , 3 , 1 , 5 ) , user4 : ( 1 , 5 , 5 , 2 ) \text {Alice}:(5,3,4,4) , \text{user1}:(3,1,2,3) , \text {user2}:( 4,3,4,3) , \text {user3}:(3,3,1,5) , \text {user4}:(1,5,5,2) Alice:(5,3,4,4),user1:(3,1,2,3),user2:(4,3,4,3),user3:(3,3,1,5),user4:(1,5,5,2)
计算Alice与user1的余弦相似性:
sim ( Alice, user1 ) = cos ( Alice, user 1 ) = 15 + 3 + 8 + 12 sqrt ( 25 + 9 + 16 + 16 ) ∗ sqrt ( 9 + 1 + 4 + 9 ) = 0.975 \operatorname{sim}(\text { Alice, user1 })=\cos (\text { Alice, user } 1)=\frac{15+3+8+12}{\operatorname{sqrt}(25+9+16+16) * \operatorname{sqrt}(9+1+4+9)}=0.975 sim( Alice, user1 )=cos( Alice, user 1)=sqrt(25+9+16+16)∗sqrt(9+1+4+9)15+3+8+12=0.975
计算Alice与user1皮尔逊相关系数:
A l i c e _ a v e = 4 u s e r 1 _ a v e = 2.25 Alice\_ave =4 \quad user1\_ave =2.25 Alice_ave=4user1_ave=2.25
向量减去均值: Alice : ( 1 , − 1 , 0 , 0 ) user1 : ( 0.75 , − 1.25 , − 0.25 , 0.75 ) \text {Alice}:(1,-1, 0,0) \quad \text { user1 }:(0.75,-1.25,-0.25,0.75) Alice:(1,−1,0,0) user1 :(0.75,−1.25,−0.25,0.75)计算这俩新向量的余弦相似度和上面计算过程一致, 结果是 0.852 。
-
基于 sklearn 计算所有用户之间的皮尔逊相关系数。可以看出,与 Alice 相似度最高的用户为用户1和用户2。
-
根据相似度用户计算 Alice对物品5的最终得分
用户1对物品5的评分是3, 用户2对物品5的打分是5, 那么根据上面的计算公式, 可以计算出 Alice 对物品5的最终得分是
P A l i c e , 物 品 5 = R ˉ A l i c e + ∑ k = 1 2 ( w A l i c e , u s e r k ( R u s e r k , 物 品 5 − R ˉ u s e r k ) ) ∑ k = 1 2 w A l i c e , u s e r k = 4 + 0.85 ∗ ( 3 − 2.4 ) + 0.7 ∗ ( 5 − 3.8 ) 0.85 + 0.7 = 4.87 P_{Alice, 物品5}=\bar{R}_{Alice}+\frac{\sum_{k=1}^{2}\left(w_{Alice,user k}\left(R_{userk, 物品5}-\bar{R}_{userk}\right)\right)}{\sum_{k=1}^{2} w_{Alice, userk}}=4+\frac{0.85*(3-2.4)+0.7*(5-3.8)}{0.85+0.7}=4.87 PAlice,物品5=RˉAlice+∑k=12wAlice,userk∑k=12(wAlice,userk(Ruserk,物品5−Rˉuserk))=4+0.85+0.70.85∗(3−2.4)+0.7∗(5−3.8)=4.87- 同样方式,可以计算用户 Alice 对其他物品的评分预测。
-
根据用户评分对用户进行推荐
- 根据 Alice 的打分对物品排个序从大到小: 物 品 1 > 物 品 5 > 物 品 3 = 物 品 4 > 物 品 2 物品1>物品5>物品3=物品4>物品2 物品1>物品5>物品3=物品4>物品2
- 如果要向 Alice 推荐2款产品的话, 我们就可以推荐物品 1 和物品 5 给 Alice。
至此, 基于用户的协同过滤算法原理介绍完毕。
UserCF的编程实现
-
建立实验使用的数据表:
import numpy as np import pandas as pd def loadData(): users = {'Alice': {'A': 5, 'B': 3, 'C': 4, 'D': 4}, 'user1': {'A': 3, 'B': 1, 'C': 2, 'D': 3, 'E': 3}, 'user2': {'A': 4, 'B': 3, 'C': 4, 'D': 3, 'E': 5}, 'user3': {'A': 3, 'B': 3, 'C': 1, 'D': 5, 'E': 4}, 'user4': {'A': 1, 'B': 5, 'C': 5, 'D': 2, 'E': 1} } return users
- 这里使用字典来建立用户-物品的交互表。
- 字典
users
的键表示不同用户的名字,值为一个评分字典,评分字典的键值对表示某物品被当前用户的评分。 - 由于现实场景中,用户对物品的评分比较稀疏。如果直接使用矩阵进行存储,会存在大量空缺值,故此处使用了字典。
- 字典
- 这里使用字典来建立用户-物品的交互表。
-
计算用户相似性矩阵
- 由于训练数据中共包含 5 个用户,所以这里的用户相似度矩阵的维度也为 5 × 5 5 \times 5 5×5。
user_data = loadData() similarity_matrix = pd.DataFrame( np.identity(len(user_data)), # identity函数用于一个n*n的单位矩阵(主对角线元素全为1,其余全为0的矩阵)。 index=user_data.keys(), columns=user_data.keys(), ) # 遍历每条用户-物品评分数据 for u1, items1 in user_data.items(): for u2, items2 in user_data.items(): # 跳过user相同的情况,为1 if u1 == u2: continue vec1, vec2 = [], [] # 遍历user1的所有items for item, rating1 in items1.items(): # 与user2相应的item rating2 = items2.get(item, -1) # dict的get if rating2 == -1: continue vec1.append(rating1) vec2.append(rating2) # 利用user1和user2的items,计算不同用户之间的皮尔逊相关系数 similarity_matrix[u1][u2] = np.corrcoef(vec1, vec2)[0][1] # vec1和vec2的相关性 print(similarity_matrix)
1 2 3 4 5 1 1.000000 0.852803 0.707107 0.000000 -0.792118 2 0.852803 1.000000 0.467707 0.489956 -0.900149 3 0.707107 0.467707 1.000000 -0.161165 -0.466569 4 0.000000 0.489956 -0.161165 1.000000 -0.641503 5 -0.792118 -0.900149 -0.466569 -0.641503 1.000000
-
计算与 Alice 最相似的
num
个用户target_user = ' Alice ' num = 2 # 由于最相似的用户为自己,去除本身 sim_users = similarity_matrix[target_user].sort_values(ascending=False)[1:num+1].index.tolist() print(f'与用户{target_user}最相似的{num}个用户为:{sim_users}')
与用户 Alice 最相似的2个用户为:['user1', 'user2']
-
预测用户 Alice 对物品
E
的评分weighted_scores = 0. corr_values_sum = 0. target_item = 'E' # 基于皮尔逊相关系数预测用户评分 for user in sim_users: corr_value = similarity_matrix[target_user][user] # 目标与当前user的相关系数 user_mean_rating = np.mean(list(user_data[user].values())) # 当前user的评分平均值 weighted_scores += corr_value * (user_data[user][target_item] - user_mean_rating) # 权重*(评分-平均),累加 corr_values_sum += corr_value # 相关系数之和,分母 target_user_mean_rating = np.mean(list(user_data[target_user].values())) # 目标的评分平均值 target_item_pred = target_user_mean_rating + weighted_scores / corr_values_sum # 最后结果 print(f'用户{target_user}对物品{target_item}的预测评分为:{target_item_pred}')
用户 Alice 对物品E的预测评分为:4.871979899370592
参考文献
- funrec