近邻推荐
1.基于用户的协同过滤算法
1.1 协同过滤算法
协同过滤算法与推荐系统的关系十分紧密,所以这是一个非常重要的算法。
协同过滤算法是一个比较大的算法范畴。通常划分为两类:
- 基于记忆(memory-Based)的协同过滤算法
此算法就是记住每个用户消费过什么东西,然后给他推荐相似的东西,或者把相似的人喜欢的东西推荐给用户。 - 基于模型(Model-Based)的协同过滤算法
此算法则是从用户和物品的关系中学习一个可以泛化的模型,从而把那些矩阵空白处填满。
1.2 基于用户的协同过滤算法原理
其实简而言之就是:先根据历史消费行为找到一群和你口味很相似的用户,然后这些用户消费了什么新的、自己没见过的物品,就可以推荐给你。
1.2.1 原理
- 1.准备用户向量
- 向量的维度就是物品的个数;
- 向量是稀疏的;
- 向量维度上的取值可以是简单的0或者1。
- 2.根据用户向量,两两计算用户之间的相似度
- 3.生成推荐结果
具体可以汇总成一个公式:
P
u
,
i
=
∑
j
n
(
s
i
m
u
,
j
⋅
R
j
,
i
)
∑
j
n
s
i
m
u
,
j
P_{u,i}=\frac { \sum_j^n(sim_{u,j} \cdot R_{j,i}) } {\sum_j^n sim_{u,j}}
Pu,i=∑jnsimu,j∑jn(simu,j⋅Rj,i)
公式的分母是把和用户u相似的n个用户的相似度加起来,分子是把这n个用户各自对物品i的态度按照相似度加权求和,这里态度最简单就是0或者1。整个公式的结果就是相似用户的态度加权平均值。
1.2.2 实践
原理虽然简单易懂,但是在实际实践过程中,会遇到很多问题。比如只有用户原始行为日志、用户向量很长计算耗时、用户量大两两计算用户相似度也很困难等。
- 关于矩阵表示
在做协同过滤时,所用的矩阵是稀疏的,就是很多矩阵元素不用存,因为是0。有两种典型的稀疏矩阵存储格式:- CSR(Compressed Sparse Row)
- COO(Coordinate):这种存储方式很简单,每个元素用一个三元组表示(行号、列号、数值),只存储有值的元素,缺失值不存储。
把原始行为日志转换成上面的格式,就可以使用常用计算框架的标准输入。
-
相似用户计算
通常降低相似度、计算复杂度是通过采样算法加速用户来进行计算的。
可以对原始行为数据亚采样可以得到和全量数据差不多的次优结果,但是计算效率却可以大大提高。 -
推荐计算
- 由于相似用户列表有截断,所以只有相似用户喜欢过的物品需要计算;
- 可以拆成MapReduce任务,放在集群上进行计算。
- 遍历每个用户喜欢的物品列表
- 获取该用户的相似用户列表
- 把每一个喜欢的物品Map成两个记录发射出去。
- 最后Reduce阶段,求和后输出用<相似用户ID,物品ID,0>的值除以<相似用户ID,物品ID,1>的值。
-
值得注意的小问题
- 惩罚热门商品
- 增加喜欢程度的时间衰减
- 对用户相似度做平滑。行为记录较少的用户偶然喜欢了相同的物品,如果计算得到较高的相似度,是不值得信任的。
1.3 应用场景
基于用户的协同过滤算法有两个产出:
- 相似用户列表
- 基于用户的推荐结果
2.基于物品的协同过滤算法
2.1 常见应用场景
这个古老而又有效的算法是亚马逊在1998年提出来的,在京东或者淘宝,都会看到“看了该商品的人还看了”、或者“关注了TA的人还关注了”、喜欢了这部电影的人还喜欢“,这些都是来自于-基于物品的协同过滤算法。
2.2 算法原理
- 构建用户物品的关系矩阵,矩阵元素可以是用户的消费行为(隐式反馈),也可以是消费后的评分,还可以是对消费行为的某种量化,如时间、次数、费用等),也可以当成评分。
- 假如矩阵的行表示物品,列表示用户,则两两计算行向量之间的相似度。可以得到物品相似度矩阵,行和列都表示物品。
- 根据推荐场景不同,产生两种不同的推荐结果。一种是在某一个物品页面推荐相关物品,另一个是在个人首页产生类似”猜你喜欢“的推荐结果。
2.2.1 计算物品相似度
从用户物品关系矩阵中得到的物品向量:
- 是一个稀疏向量
- 向量的一维代表一个用户,向量的总维度代表总的用户数量。
- 向量各个维度的取值表示用户对这个物品的消费结果。可以是代表行为(隐式反馈)的布尔值,也可以是量化的分数,如评分、时间长短、次数多少、费用高低等,还可以是消费的评价分数。
- 灭有消费过的物品不表示出来,故这是一个稀疏向量。
选择余弦相似度计算物品的相似度:
s
i
m
(
i
,
j
)
=
∑
k
=
1
n
R
i
k
⋅
R
j
k
∑
k
=
1
n
R
i
k
2
∑
k
=
1
n
R
j
k
2
sim(i,j)=\frac{\sum_{k=1}^nR_{ik} \cdot R_{jk} }{\sqrt{\sum_{k=1}^nR_{ik}^2}\sqrt{\sum_{k=1}^nR_{jk}^2}}
sim(i,j)=∑k=1nRik2∑k=1nRjk2∑k=1nRik⋅Rjk
在上面这个公式中,分母表示两个物品向量的长度,求元素值的平方和再开方。分子是两个向量的点积,相同位置的元素值相乘在求和。
物理意义:计算两个向量的夹角余弦值。相似度为1时,对应角度为0;相似度为0时,对应角度为90°,毫不相干。
在这个基本算法基础上,有两个改进方向:
- 物品维度中心化
- 用户维度中心化
其实,这样一看,基于物品的协同过滤算法和基于用户的协同过滤算法在计算相似度这一步是一样的,区别在于一个计算行向量的相似度,另一个计算列向量的相似度,只需要将用户行为矩阵转置一下即可。
2.2.2 计算推荐结果
在得到物品相似度之后,就可以为用户推荐可能感兴趣的物品,一般有两个应用场景:
- 1.Top N推荐
当用户访问首页时,汇总和用户已经消费过的物品相似的物品,按照汇总后分数从高到底推出。
R u i = ∑ j = 1 m s i m ( i , j ) × R u j ∑ j = 1 m s i m ( i , j ) R_{ui}=\frac{\sum_{j=1}^msim(i,j) \times R_{uj}}{\sum_{j=1}^msim(i,j)} Rui=∑j=1msim(i,j)∑j=1msim(i,j)×Ruj
这个公式就是用相似度加乘以用户评分求和后除以总相似度,得到对每一个物品的预测评分,最后去掉用户已经消费了的,然后保留分数最高的K个物品展示出来即可。 - 2.相关推荐
这类推荐不需要提前合并计算,当用户访问一个物品的详情页时,或者完成一个个物品消费的结果页面时,可以直接获取这个物品的相似物品推荐,也就是”看了又看“或者”买了又买“这样的推荐结果。
2.2.3 Slope One算法
对于经典的基于物品的协同过滤算法,相似度矩阵计算无法实时更新,整个过程都是离线完成的,而且计算相似度时没有考虑相似度的置信问题。
而slpoe one算法针对这些问题做了些改进。此算法只针对评分矩阵,不适用于行为矩阵。Slope One算法计算的不是物品之间的相似度,而是物品的距离,即相似度的反面。
S i , j = ∑ k = 1 n C j , k ( S i , k + D j , k ) ∑ k = 1 n C j , k S_{i,j} = \frac{\sum_{k=1}^nC_{j,k}(S_{i,k}+D_{j,k})}{\sum_{k=1}^nC_{j,k}} Si,j=∑k=1nCj,k∑k=1nCj,k(Si,k+Dj,k)
3. 相似度算法
3.1 相似度的本质
推荐算法中有两种,一种是机器学习,一种是近邻。近邻通常是在高维空间而不是在三维空间下谈论,和机器学习采用优化算法的思路不同,近邻推荐的核心是向量的构造以及相似度计算方法的选择。
与近邻配套的一个概念是距离,两者都是用来量化两个物体在高维空间中的亲密程度的。
3.2 相似度计算方法
在真正巡视相似度计算方法前,先对度量对象做一个简单分类。相似度计算对象是向量,或者叫做高维空间下的坐标,所以表示这个向量的数值就有两种:
- 实数值
- 布尔值
3.2.1 欧氏距离
欧氏距离就是在欧式空间下度量距离的方法。
计算公式如下:
E
(
p
,
q
)
=
∑
i
=
1
n
(
p
i
−
q
i
)
2
E(p,q)=\sqrt {\sum_{i=1}^n(p_i-q_i)^2}
E(p,q)=i=1∑n(pi−qi)2
公式中n是空间维度,也可以理解为坐标轴个数,
p
i
p_i
pi和
q
i
q_i
qi就是每一个坐标轴上的取值。显然欧氏距离不适合在布尔向量间使用。
一般通过 1 1 + E ( p , q ) \frac {1} {1+E(p,q)} 1+E(p,q)1把范围为0到正无穷的欧氏距离转换为范围为0到1的相似度。
3.2.2 余弦相似度
余弦相似度测量的是两个向量间的夹角,测量方式是取向量夹角的余弦值。
值得注意的是余弦相似度在测量文本相似度、用户相似度、物品相似度时都很常用。与向量长度无关。
c
o
s
(
p
,
q
)
=
∑
i
p
i
q
i
∑
i
q
i
2
∑
i
p
i
2
cos(p,q)=\frac {\sum_ip_iq_i}{\sqrt{\sum_iq_i^2}\sqrt{\sum_ip_i^2}}
cos(p,q)=∑iqi2∑ipi2∑ipiqi
经过向量长度归一化后的相似度测量方式,背后隐藏着,两个向量只要方向一致,无论长度如何,都被视作相似的。这样有局限性但是也合理。
但是在协同过滤中,如果选择余弦相似度,某种程度上会更加依赖两个物品的共同评价用户数,而不是用户给出的评分是多少,原因就是余弦相似度被向量长度归一化了。
针对这个问题,可以改进余弦相似度。改进的方法很简单,就是先计算向量每个维度上的均值,然后每个向量在各个维度上都减去均值后在计算余弦相似度。
3.2.3 皮尔逊相关系数
皮尔逊相关系数实际上也是一种余弦相似度,它先对向量做了中心化:向量
p
p
p和
q
q
q各自减去向量的均值后再计算余弦相似度,公式如下:
R
(
p
,
q
)
=
∑
i
=
1
n
(
p
i
−
p
‾
)
(
q
i
−
q
‾
)
∑
i
=
1
n
(
p
i
−
p
‾
)
2
∑
i
=
1
n
(
q
i
−
q
‾
)
2
R(p,q)=\frac {\sum_{i=1}^n(p_i-\overline{p})(q_i-\overline{q})}{\sqrt{\sum_{i=1}^n(p_i-\overline{p})^2}\sqrt{\sum_{i=1}^n(q_i-\overline{q})^2}}
R(p,q)=∑i=1n(pi−p)2∑i=1n(qi−q)2∑i=1n(pi−p)(qi−q)
皮尔逊相关系数计算结果范围在-1至1之间。-1表示负相关,1表示正相关。其实测量的是两个随机变量是否在同增同减。不适用于布尔值。
3.2.4 Jaccard相似度
Jaccard相似度表示两个集合的交集元素个数占并集元素个数的比例。
J
(
p
,
q
)
=
∣
P
∩
Q
∣
∣
P
∪
Q
∣
=
∑
i
=
0
n
p
i
q
i
∑
i
=
0
n
p
i
∣
q
i
J(p,q)=\frac {|P \cap Q|}{|P \cup Q|}=\frac {\sum_{i=0}^np_iq_i}{\sum_{i=0}^np_i|q_i}
J(p,q)=∣P∪Q∣∣P∩Q∣=∑i=0npi∣qi∑i=0npiqi
- 分子为两个布尔向量做点积运算,得到的是交集元素的个数
- 分母为两个布尔向量做或运算,再求元素和,即并集元素的个数
余弦相似度适用于评分数据,Jaccard相似度适用于隐式反馈数据。例如:使用用户的收藏行为计算用户之间的相似度。Jaccard非常适合用来完成此类任务。
3.3 向量化计算
相似度计算一般都转化为向量之间的点积计算,编程实现向量点积运算典型做法就是用for循环,但是效率很低,所以取而代之的是更高效的向量化计算。
特别要注意的:
在做推荐系统的算法实现时,要以向量化思维去思考计算过程。
向量化计算的场景有两个特点:
- 高维度向量计算,维度少则几十,多则数百万或数千万。
- 计算结果还是向量,在维度之间的基本运算中,维度和维度之间无先后关系。