协同过滤(Collaborative Filtering)推荐算法是最经典、最常用的推荐算法。
所谓协同过滤, 基本思想是根据用户之前的喜好以及其他兴趣相近的用户的选择来给用户推荐物品(基于对用户历史行为数据的挖掘发现用户的喜好偏向, 并预测用户可能喜好的产品进行推荐),一般是仅仅基于用户的行为数据(评价、购买、下载等), 而不依赖于项的任何附加信息(物品自身特征)或者用户的任何附加信息(年龄, 性别等)。目前应用比较广泛的协同过滤算法是基于邻域的方法, 而这种方法主要有下面两种算法:
基于用户的协同过滤算法(UserCF): 给用户推荐和他兴趣相似的其他用户喜欢的产品基于物品的协同过滤算法(ItemCF): 给用户推荐和他之前喜欢的物品相似的物品不管是UserCF还是ItemCF算法, 非常重要的步骤之一就是计算用户和用户或者物品和物品之间的相似度,以下介绍常用的相似性度量方法。
若用户和商品数量分别为 m , n m,n m,n的话,交互矩阵 A A A就是一个 m m m行 n n n列的矩阵。此时用户的相似度可以表示为(其中 u ⋅ v u\cdot v u⋅v指的是向量点积): 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
上述用户-商品交互矩阵在现实情况下是非常的稀疏了,为了避免存储这么大的稀疏矩阵,在计算用户相似度的时候一般会采用集合的方式进行计算。理论上向量之间的相似度计算公式都可以用来计算用户之间的相似度,但是会根据实际的情况选择不同的用户相似度度量方法。
这个在具体实现的时候, 可以使用cosine_similarity进行实现:
from sklearn.metrics.pairwise import cosine_similarity i = [1, 0, 0, 0] j = [1, 0.5, 0.5, 0] cosine_similarity([i, j]) 杰卡德相似系数:这个是衡量两个集合的相似度一种指标。两个用户 u u u和 v v v交互商品交集的数量占这两个用户交互商品并集的数量的比例,称为两个集合的杰卡德相似系数,用符号 s i m u v sim_{uv} simuv表示,杰卡德相似系数越大说明相识度越高。 s i m u v = ∣ u ∩ v ∣ ∣ u ∣ ∪ ∣ v ∣ sim_{uv}=\frac{|u \cap v|}{|u| \cup|v|} simuv=∣u∣∪∣v∣∣u∩v∣
由于杰卡德相似系数一般无法反映具体用户的评分喜好信息, 所以常用来评估用户是否会对某商品进行打分, 而不是预估用户会对某商品打多少分。
杰卡德距离:与杰卡德相似系数相反的概念是杰卡德距离(Jaccard Distance),可以用如下公式来表示:j u v = 1 − s i m u v = ∣ u ∣ ∪ ∣ v ∣ − ∣ u ∩ v ∣ ∣ u ∣ ∪ ∣ v ∣ j_{uv}=1-sim_{uv}=\frac{|u| \cup|v|-|u \cap v|}{|u| \cup|v|} juv=1−simuv=∣u∣∪∣v∣∣u∣∪∣v∣−∣u∩v∣
杰卡德距离用两个两个集合中不同元素占所有元素的比例来衡量两个集合的区分度。
jaccard相似度的缺点是值适用于二元数据的集合。
皮尔逊相关系数:用于度量两个变量之间的相关程度,即正相关或负相关 ,其值介于-1与1之间。皮尔逊相关系数计算公式,其中 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交互的所有商品交互数量或者具体评分的平均值。 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) 所以相比余弦相似度,皮尔逊相关系数通过使用用户的平均分对各独立评分进行修正,减小了用户评分偏置的影响。具体实现, 我们也是可以通过调包:
from scipy.stats import pearsonr i = [1, 0, 0, 0] j = [1, 0.5, 0.5, 0] pearsonr(i, j)上述介绍的余弦相似度计算方法比较常用,且一般效果也不会太差,但是对于评分数据不规范的时候,即存在有的用户喜欢打高分,有的用户喜欢打低分情况的时候,有的用户喜欢乱打分的情况,这时候consine相似度算出来的结果可能就不是那么准确了,比如下面这种情况: 这时候,如果用余弦相似度进行计算,会发现用户d和用户f比较相似,而实际上,如果看这个商品喜好的一个趋势的话,其实d和e比较相近,只不过e比较喜欢打低分,d比较喜欢打高分。所以对于这种用户评分偏置的情况,余弦相似度就不是那么好了,可以考虑使用皮尔逊相关系数。
更多的相似度度量方法:8种相似度度量方式的原理及实现
基于用户的协同过滤(以下用UserCF表示),思想其实比较简单,当一个用户A需要个性化推荐的时候, 我们可以先找到和他有相似兴趣的其他用户, 然后把那些用户喜欢的, 而用户A没有听说过的物品推荐给A。
UserCF算法主要包括两个步骤: 找到和目标用户兴趣相似的集合。找到这个集合中的用户喜欢的, 且目标用户没有听说过的物品推荐给目标用户。下面将以一个具体例子理解UserCF 给用户推荐物品的过程可以形象化为一个猜测用户对商品进行打分的任务,上面表格里面是5个用户对于5件物品的一个打分情况,就可以理解为用户对物品的喜欢程度。
应用UserCF算法的两个步骤: 首先根据前面的这些打分情况(或者说已有的用户向量)计算一下Alice和用户1, 2, 3, 4的相似程度, 找出与Alice最相似的n个用户;根据这n个用户对物品5的评分情况和与Alice的相似程度会猜测出Alice对物品5的评分, 如果评分比较高的话, 就把物品5推荐给用户Alice, 否则不推荐。针对上述两个步骤,第一个步骤可以用相似度度量方法计算出用户之间的相似程度,第二个步骤则通过第一步骤中选出与Alice最相近的前N个用户,基于他们对物品5的评测估计出Alice的打分值。
常用的方式之一是利用用户相似度和相似用户的评价加权平均获得用户的评价预测, 用下面式子表示: 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的评分。 还有一种方式如下, 这种方式考虑的更加前面, 依然是用户相似度作为权值, 但后面不单纯的是其他用户对物品的评分, 而是该物品的评分与此用户的所有评分的差值进行加权平均, 这时候考虑到了有的用户内心的评分标准不一的情况, 即有的用户喜欢打高分, 有的用户喜欢打低分的情况。P i , j = R ˉ i + ∑ k = 1 n ( S i , k ( R k , j − R ˉ k ) ) ∑ k = 1 n S j , k P_{i, j}=\bar{R}{i}+\frac{\sum{k=1}^{n}\left(S_{i, k}\left(R_{k, j}-\bar{R}{k}\right)\right)}{\sum{k=1}^{n} S_{j, k}} Pi,j=Rˉi+∑k=1nSj,k∑k=1n(Si,k(Rk,j−Rˉk))
所以这一种计算方式更为推荐。下面的计算将使用这个方式。
在获得用户 u u u对不同物品的评价预测后, 最终的推荐列表根据预测评分进行排序得到。 至此,基于用户的协同过滤算法的推荐过程完成。
1.首先计算Alice与其他用户的相似度(使用皮尔逊相关系数)
# 根据上图表格数据,可知各用户的向量为:Alice(5, 3, 4, 4), 用户1(3, 1, 2, 3), 用户2(4, 3, 4, 3),用户3(3, 3, 1, 5),用户4(1, 5, 5, 2) from scipy.stats import pearsonr i = [5, 3, 4, 4] j = [3, 1, 2, 3] print(pearsonr(i, j)) # 输出: (0.8528028654224415, 0.14719713457755845)这里我们使用皮尔逊相关系数, 也就是Alice与用户1的相似度是0.852。同样的方式, 我们就可以计算与其他用户的相似度, 这里可以使用numpy的相似度函数得到用户的相似性矩阵:
import numpy as np users = np.array([[5, 3, 4, 4], [3, 1, 2, 3], [4, 3, 4, 3], [3, 3, 1, 5], [1, 5, 5, 2]]) print(np.corrcoef((users))) # 输出: [[ 1. 0.85280287 0.70710678 0. -0.79211803] [ 0.85280287 1. 0.30151134 0.42640143 -0.88662069] [ 0.70710678 0.30151134 1. -0.70710678 -0.14002801] [ 0. 0.42640143 -0.70710678 1. -0.59408853] [-0.79211803 -0.88662069 -0.14002801 -0.59408853 1. ]]从上述结果可以看出,Alice用户和用户2,用户3,用户4的相似度是0.7,0,-0.79。 所以如果n=2, 找到与Alice最相近的两个用户是用户1,和Alice的相似度是0.85,用户2,和Alice相似度是0.7。
2.据相似度用户计算Alice对物品5的最终得分,用户1对物品5的评分是3,用户2对物品5的打分是5,那么根据上面的计算公式,可以计算出Alice对物品5的最终得分是4.87。
P A l i c e , 物 品 5 = R ˉ A l i c e + ∑ k = 1 2 ( S 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 S 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(S_{Alice,user k}\left(R_{userk, 物品5}-\bar{R}{userk}\right)\right)}{\sum{k=1}^{2} S_{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=12SAlice,userk∑k=12(SAlice,userk(Ruserk,物品5−Rˉuserk))=4+0.85+0.70.85∗(3−2.4)+0.7∗(5−3.8)=4.87
3.根据用户评分对用户进行推荐 以上,我们就得到了Alice对物品5的得分是4.87,根据Alice的打分对物品排个序从大到小: 物 品 1 > 物 品 5 > 物 品 3 = 物 品 4 > 物 品 2 物品1>物品5>物品3=物品4>物品2 物品1>物品5>物品3=物品4>物品2 这时候,如果要向Alice推荐2款产品的话,我们就可以推荐物品1和物品5给Alice。
由于UserCF技术上的两点缺陷, 导致很多电商平台并没有采用这种算法, 而是采用了ItemCF算法实现最初的推荐系统。
基于物品的协同过滤(ItemCF)的基本思想是预先根据所有用户的历史偏好数据计算物品之间的相似性,然后把与用户喜欢的物品相类似的物品推荐给用户。比如物品a和c非常相似,因为喜欢a的用户同时也喜欢c,而用户A喜欢a,所以把c推荐给用户A。ItemCF算法并不利用物品的内容属性计算物品之间的相似度, 主要通过分析用户的行为记录计算物品之间的相似度, 该算法认为, 物品a和物品c具有很大的相似度是因为喜欢物品a的用户大都喜欢物品c。
ItemCF算法主要包括两个步骤: 计算物品直接的相似度。根据物品的相似度和用户的历史行为给用户生成推荐列表(购买了该商品的用户也经常购买的其他商品)。我们继续引用上面Alice的例子来说明ItemCF吧:
1.先计算物品5与物品1,2,3,4之间的相似性(它们也是向量的形式, 每一列的值就是它们的向量表示, 因为ItemCF认为物品a和物品c具有很大的相似度是因为喜欢物品a的用户大都喜欢物品c, 所以就可以基于每个用户对该物品的打分或者说喜欢程度来向量化物品); import numpy as np # 根据上图表格数据,可知各物品的向量为:物品1(3, 4, 3, 1), 用户1(1, 3, 3, 5), 用户2(2, 4, 1, 5),用户3(3, 3, 5, 2),用户4(3, 5, 4, 1) items = np.array([[3, 4, 3, 1], [1, 3, 3, 5], [2, 4, 1, 5], [3, 3, 5, 2], [3, 5, 4, 1]]) print(np.corrcoef((items))) # 输出 [[ 1. -0.64888568 -0.43528575 0.47368421 0.96945842] [-0.64888568 1. 0.67082039 -0.32444284 -0.47809144] [-0.43528575 0.67082039 1. -0.8705715 -0.42761799] [ 0.47368421 -0.32444284 -0.8705715 1. 0.58167505] [ 0.96945842 -0.47809144 -0.42761799 0.58167505 1. ]] 2.找出与物品5最相近的n个物品(当n=2); 根据皮尔逊相关系数,查看上述输出,可以找到与物品5最相似的2个物品是物品1(0.96)和物品4(0.58), 下面基于上面的公式计算最终得分。3.根据Alice对最相近的n个物品的打分去计算对物品5的打分情况。P A l i c e , 物 品 5 = R ˉ 物 品 5 + ∑ k = 1 2 ( S 物 品 5 , 物 品 k ( R A l i c e , 物 品 k − R ˉ 物 品 k ) ) ∑ k = 1 2 S 物 品 k , 物 品 5 = 13 4 + 0.97 ∗ ( 5 − 3.2 ) + 0.58 ∗ ( 4 − 3.4 ) 0.97 + 0.58 = 4.6 P_{Alice, 物品5}=\bar{R}{物品5}+\frac{\sum{k=1}^{2}\left(S_{物品5,物品 k}\left(R_{Alice, 物品k}-\bar{R}{物品k}\right)\right)}{\sum{k=1}^{2} S_{物品k, 物品5}}=\frac{13}{4}+\frac{0.97*(5-3.2)+0.58*(4-3.4)}{0.97+0.58}=4.6 PAlice,物品5=Rˉ物品5+∑k=12S物品k,物品5∑k=12(S物品5,物品k(RAlice,物品k−Rˉ物品k))=413+0.97+0.580.97∗(5−3.2)+0.58∗(4−3.4)=4.6 因此,我们可以推荐物品5给Alice。
由于UserCF和ItemCF结果评估部分是共性知识点, 所以在这里统一标识。 这里介绍评测指标:
1.召回率。对用户u推荐N个物品记为 R ( u ) R(u) R(u),令用户u在测试集上喜欢的物品集合为 T ( u ) T(u) T(u),那么召回率定义为: Recall = ∑ u ∣ R ( u ) ∩ T ( u ) ∣ ∑ u ∣ T ( u ) ∣ \operatorname{Recall}=\frac{\sum_{u}|R(u) \cap T(u)|}{\sum_{u}|T(u)|} Recall=∑u∣T(u)∣∑u∣R(u)∩T(u)∣这个意思就是说,在用户真实购买或者看过的影片里面,我模型真正预测出了多少,这个考察的是模型推荐的一个全面性。2.准确率。准确率定义为: Precision = ∑ u ∣ R ( u ) ∩ T ( u ) ∣ ∑ u ∣ R ( u ) ∣ \operatorname{Precision}=\frac{\sum_{u} \mid R(u) \cap T(u)|}{\sum_{u}|R(u)|} Precision=∑u∣R(u)∣∑u∣R(u)∩T(u)∣这个意思再说,在我推荐的所有物品中,用户真正看的有多少,这个考察的是我模型推荐的一个准确性。为了提高准确率,模型需要把非常有把握的才对用户进行推荐,所以这时候就减少了推荐的数量,而这往往就损失了全面性,真正预测出来的会非常少,所以实际应用中应该综合考虑两者的平衡。3.覆盖率。覆盖率反映了推荐算法发掘长尾的能力,覆盖率越高,说明推荐算法越能将长尾中的物品推荐给用户。 Coverage = ∣ ⋃ u ∈ U R ( u ) ∣ ∣ I ∣ \text { Coverage }=\frac{\left|\bigcup_{u \in U} R(u)\right|}{|I|} Coverage =∣I∣∣∣⋃u∈UR(u)∣∣4.该覆盖率表示最终的推荐列表中包含多大比例的物品。如果所有物品都被给推荐给至少一个用户, 那么覆盖率是100%。5.新颖度 用推荐列表中物品的平均流行度度量推荐结果的新颖度。 如果推荐出的物品都很热门, 说明推荐的新颖度较低。 由于物品的流行度分布呈长尾分布, 所以为了流行度的平均值更加稳定, 在计算平均流行度时对每个物品的流行度取对数。协同过滤算法存在的问题之一就是泛化能力弱, 即协同过滤无法将两个物品相似的信息推广到其他物品的相似性上。 导致的问题是热门物品具有很强的头部效应, 容易跟大量物品产生相似, 而尾部物品由于特征向量稀疏, 导致很少被推荐。 比如下面这个例子: A, B, C, D是物品, 看右边的物品共现矩阵, 可以发现物品D与A、B、C的相似度比较大, 所以很有可能将D推荐给用过A、B、C的用户。 但是物品D与其他物品相似的原因是因为D是一件热门商品, 系统无法找出A、B、C之间相似性的原因是其特征太稀疏, 缺乏相似性计算的直接数据。 所以这就是协同过滤的天然缺陷:推荐系统头部效应明显, 处理稀疏向量的能力弱。
为了解决这个问题, 同时增加模型的泛化能力,2006年,矩阵分解技术(Matrix Factorization,MF)被提出, 该方法在协同过滤共现矩阵的基础上, 使用更稠密的隐向量表示用户和物品, 挖掘用户和物品的隐含兴趣和隐含特征, 在一定程度上弥补协同过滤模型处理稀疏矩阵能力不足的问题。
此外协同过滤的特点就是完全没有利用到物品本身或者是用户自身的属性,仅仅利用了用户与物品的交互信息就可以实现推荐,比较简单高效,但这也是它的一个短板所在,由于无法有效的引入用户年龄, 性别,商品描述,商品分类,当前时间,地点等一系列用户特征、物品特征和上下文特征,这就造成了有效信息的遗漏,不能充分利用其它特征数据。
为了解决这个问题, 在推荐模型中引用更多的特征,推荐系统慢慢的从以协同过滤为核心到了以逻辑回归模型为核心, 提出了能够综合不同类型特征的机器学习模型。
参考资料
项亮-《推荐系统实践》基于用户的协同过滤来构建推荐系统协同过滤算法概述