协同过滤算法(基于物品)
基于用户的协同过滤,适用于物品较少,用户也不太多的情况。如果用户太多,针对每个用户的购买情况来计算哪些用户和他品味类似,效率很低下。
如果商品很多,每个用户购买的商品重合的可能性很小,这样判断品味是否相似也就变得比较困难了。
消费者每天都在买买买,行为变化很快,但是物品每天虽然也有变化,但是和物品总量相比变化还是少很多。这样,就可以预先计算物品之间的相似程度,然后再利用顾客实际购买的情况找出相似的物品做推荐。
因此基于物品的协同过滤正式在这总变化的基础上的一种方法。与基于用户的协同过滤相似,只是在计算邻居时采用物品本身,而不是从用户的角度
虽然物品每天也有变化,但是和物品总量相比变化还是少很多。这样就可以不用每天都计算节省资源。
同时我们可以只给某一样商品只备选5个相似商品,推荐时只做这5个相似物品的加权评分,避免对所有商品都进行加权评分,以避免大量计算。
案例解说
还是针对同样的案例,基于物品进行协同过滤的推荐
这一次我们不在计算用户之间的相似,而是计算电影之间的相似。这里我们选用皮尔逊相关相似来计算,具体公式如下(Pearson相关系数)
这个公式看起来比较复杂,实际上来说皮尔逊相关系数就是余弦相似度计算之前两个向量都先进行中心化(centered)
两个连续变量(X,Y)的皮尔逊相关系数等于他们之间的协方差cov(X,Y)除以它们各自标准差的乘积(𝜎xσx,𝜎𝑌σY),系数的取值总是在-1.0到1.0之间,接近0的变量被成为无相关性,接近1被称为具有强正相关性,接近-1被称为具有强负相关性。
皮尔森相关系数是衡量线性关联性的程度,p的一个几何解释是其代表两个变量的取值根据均值集中后构成的向量之间夹角的余弦。
公式看起来复杂,其实可以分成6个部分分别计算就好了,我们选《寻龙诀》(X)和《小门神》(Y)作为例子,来算一下相似度,则
X=(3.5,5.0,3.0)
Y=(3.0,3.5,2.0)
数字就是评分,因为只有三个人同时看了这两个电影,所以X,Y两个向量都只有三个元素。按照公式逐步计算:
1. x和y的乘积再求和:3.5×3.0+5.0×3.5+3.0×2.0 = 34
2. x求和乘以y求和,再除以个数:((3.5+5.0+3.0)×(3.0+3.5+2.0))/ 3 = 32.58
3. x的平方和:3.5^2+5.0^2+3.0^2 = 46.25
4. x和的平方除以个数:((3.5+5.0+3.0)^2)) / 3 = 44.08
5. y的平方和:3.0^2+3.5^2+2.0^2 = 25.25
6. y和的平方除以个数:((3.0+3.5+2.0)^2)) / 3 = 24.08
最终我们整体带入公式,得到《寻龙诀》X和《小门神》Y的皮尔逊相关系数为0.89
按照这种方法,我们两两计算电影间的皮尔逊相关系数,得到的最终结果如下表:
从表中可以看出电影间两两的皮尔逊相关系数值,前面提到过皮尔逊相关系数值范围[-1,1]。
所以我们看到表中有正值有负值,正值表示存在正相关关系,负值表示存在负相关关系。
关系数取值为[-1,1],1表示完全相似,0表示没关系,-1表示完全相反。
结合到电影偏好上,如果相关系数为负数,比如《老炮儿》和《唐人街探案》,意思是说,喜欢《老炮儿》的人,存在厌恶《唐人街探案》的倾向。
然后就可以为A推荐电影了,思路是:A只看过两个电影,然后看根据其他电影与这两个电影的相似程度,进行加权评分,得出应该推荐给A的电影,具体方法可以列一个表
用A看过的电影的评分,和其他电影的相似度相乘(红框),然后再把相乘后的结果加和(绿框),得出最后的推荐度。这里可以看到,应该向A推荐《寻龙诀》,和上一篇文章用基于用户的协同过滤算法结果是一致的。
总结
前面我们已经把协同过滤的两种常用方法都讲过了,下面我们来做一下总结:
1、基于用户的协同过滤推荐,要找出兴趣爱好相似的人来推荐
2、基于物品的协同过滤推荐,要找出与自己喜欢的物品相似的物品来推荐
那还没有其他的推荐呢?
我们可不可以输入关键字,根据关键字推荐呢?很显然是可以的,就像我们在搜索栏搜索一样,变成了搜索算法
还有其他的吗?
可以!我们讲过的这几个都是经典常用的方法,还有其他未讲的,甚至我们可以把这些经典方法结合起来使用,对推荐感兴趣的同学可以多去读一些书籍、论文,深入学习推荐。
不管是基于用户的还是基于物品的,我们发现他们的共通点是什么?不同点在哪里?
共通点在:基于相似,基于相似度高的用户或物品,换一种说法就是基于近邻。
不同点在:相似度的计算方法上,前面的案例中我们使用了欧式距离,皮尔逊相关系数两种不同的方法来计算相似度。那我们还知道有其他的相似度计算方法,比如说余弦相似度(用余弦距离评估)、Jaccard相似度(用Jaccard距离评估)等等。
不同的相似度计算方法有什么的差距呢?有没有优劣之分呢?
这需要我们去通过准确度来判断。也许他们在准确度上差距不到,那就看算法的复杂度和资源的占用率了。
代码实操
主要代码展示
from collections import defaultdict critics = defaultdict(dict) for i in range(df.shape[0]): critics[str(df.iloc[i,0])][df.iloc[i,2]]=df.iloc[i,3] name=input("请输入你的学号:") n=int(input("请输入每个类别推荐的数目:")) result=getRecommendedItems(critics,calculateSimilarItems(critics),name) X1=[] X2=[] X3=[] x1=0 x2=0 x3=0 for i in result: if df.iloc[df[df['产品名称']==i[1]].index[1],1]=='酒店' and x1<n: X1.append(i) x1+=1 if df.iloc[df[df['产品名称']==i[1]].index[1],1]=='餐饮' and x2<n: X2.append(i) x2+=1 if df.iloc[df[df['产品名称']==i[1]].index[1],1]=='景区' and x3<n: X3.append(i) x3+=1 if x1==n and x2==n and x3==n: break print("酒店推荐你选择这几家:") print(X1[:n]) print() print("餐饮推荐你选择这几家:") print(X2[:n]) print() print("景区推荐你选择这几家:") print(X3[:n])