用户行为数据的二分图表示
用户的购买行为很容易可以用二分图(二部图)来表示。并且利用图的算法进行推荐。基于邻域的模型也可以成为基于图的模型,因为基于邻域的模型都是基于图的模型的简单情况。我们可以用二元组((u,i))来表示用户(u)对物品(i)有过购买行为,这样的话数据集可以用一个二分图来表示。我这里尝试画一个二分图(有点丑,不要介意哈):
graph LR A(A) -->a[a] A(A) -->b[b] A(A) -->d[d] B(B) -->b[b] B(B) -->c[c]
左边是用户节点,右边是物品节点。连线代表用户对物品有过购买行为。
基于图的推荐算法
我们把个性化推荐算法放在二分图当中,给用户推荐物品就转变成了度量用户节点和商品节点的相关性,相关性越高的物品在推荐列表当中的权重就越大。一般来说顶点相关性取决于三个方面:
- 两个顶点之间的路径数;
- 两个顶点之间路径的长度;
- 两个顶点之间的路径经过的顶点。
相关性高的一对顶点一般具有如下特征:
- 两个顶点之间有很多路径相连;
- 连接两个顶点之间的路径长度都比较短;
- 连接两个顶点之间的路径不会经过出度比较大的顶点。
基于上面3个主要因素,研究人员设计了很多计算图中顶点之间相关性的方法。下一节将介绍一种基于随机游走的PersonalRank算法。
PersonalRank
假设要给用户(u)进行个性化推荐,可以从用户(u)对应的节点(v_u)开始在用户物品二分图上进行随机游走。游走到任何一个节点时,首先按照概率(alpha)决定是继续游走,还是停止这次游走并从(v_u)节点开始重新游走。如果决定继续游走,那么就从当前节点指向的节点中按照均匀分布随机选择一个节点作为游走下次经过的节点。这样,经过很多次随机游走后,每个物品节点被访问到的概率会收敛到一个数。最终的推荐列表中物品的权重就是物品节点的访问概率。
[begin{equation} mathrm{PR}(v)=left{ begin{aligned} alpha sum_{v' in mathrm{in}(v)}frac{mathrm{PR}(v')}{|mathrm{out}(v')|}quad (v ne v_u)\ (1-alpha) alphasum_{v' in mathrm{in}(v)}frac{mathrm{PR}(v')}{|mathrm{out}(v')|}quad (v=v_u) end{aligned} right. end{equation} ]
用代码来表示:
代码语言:javascript复制def PersonalRank(G, alpha, root):
rank = dict()
rank = {x:0 for x in G.keys()}
rank[root] = 1
for k in range(20):
tmp = {x:0 for x in G.keys()}
for i, ri in G.items():
for j, wij in ri.items():
if j not in tmp:
tmp[j] = 0
tmp[j] = 0.6 * rank[i] / (1.0 * len(ri))
if j == root:
tmp[j] = 1 - alpha
rank = tmp
return rank
虽然PersonalRank算法可以通过随机游走进行比较好的理论解释,但该算法在时间复杂度上有明显的缺点。因为在为每个用户进行推荐时,都需要在整个用户物品二分图上进行迭代,直到整个图上的每个顶点的PR值收敛。这一过程的时间复杂度非常高,不仅无法在线提供实时推荐,甚至离线生成推荐结果也很耗时。为了解决PersonalRank每次都需要在全图迭代并因此造成时间复杂度很高的问题,这里给出 两种解决方案。第一种很容易想到,就是减少迭代次数,在收敛之前就停止。这样会影响最终的 精度,但一般来说影响不会特别大。另一种方法就是从矩阵论出发,重新设计算法。
我们将PR算法设计成矩阵的形式,令(M)为二分图的转移概率矩阵,即
[M(v,v')=frac{1}{mathrm{out}(v)} ]
那么迭代公式修改为:
[r = (1-alpha)r_0 alpha M^T r ]
我们解一下这个方程
[r = (1-alpha)(1-alpha M^T)^{-1}r_0 ]
这里的((1-alpha M^T))是稀疏矩阵,对其快速求逆即可。
后记
最近这两天出去过节了,断更了几天,我又开始继续更新这个系列了。这个系列后续可能就比较快的更新完,再说一个好消息,更新完这个系列之后,王喆编著的《深度学习推荐系统》以及相关的实践课程视频我也买了,将继续更新《深度学习推荐系统》以及实践视频课的读书笔记,大家可以关注一下,敬请期待。