《推荐系统实战》中介绍了基于图的推荐算法,将用户行为数据表示成图的形式。Standford的Haveliwala于2002年在他《Topic-sensitive pagerank》一文中提出了PersonalRank算法。原理上与PageRank是很相似的,区别在于PageRank 中的链接是有向的,而PersonalRank中人于物品之间的连接是无向的,或者说是双向的。

1.1 用户行为的二分图表示

  假设用户行为数据是由一系列二元数组组成的,每个二元组(u, i)表示用户u对物品i产生过行为。这种数据集可以用一个二分图(Bipartite)表示,又叫二部图。
  令G(V, E)表示用户物品二分图,其中V=Vu∪ViV=V_u \cup V_iV=Vu​∪Vi​由用户顶点集合VuV_uVu​和物品顶点集合ViV_iVi​组成。每个二元组(u, i)对应途中的一条边e(vu,vi)e(v_u, v_i)e(vu​,vi​),其中vu∈VUv_u \in V_Uvu​∈VU​是用户u对应的顶点,vi∈VIv_i \in V_Ivi​∈VI​是物品i对应的顶点。

下图是一个简单的用户物品二分图模型,其中圆形节点代表用户,方形节点代表物品,圆形节点和方形节点之间的边代表用户对物品的行为。比如图中用户节点A和物品节点a、b、d相连,说明用户A对物品a、b、d产生过行为。

1.2 图中顶点的相关性

  将用户行为表示为二分图模型后,下面的任务就是在二分图上给用户进行个性化推荐。如果将个性化推荐算法放到二分图模型上,那么给用户u推荐物品的任务就可以转化为度量用户顶点vuv_uvu​和与vuv_uvu​没有边直接相连的物品节点在图上的相关性,相关性越高的物品在推荐列表中的权重就越高。

度量图中两个顶点之间相关性的方法很多,但一般来说图中顶点的相关性主要取决于下面3个因素:

  • 两个顶点之间的路径数;(路径越多相关性越高)
  • 两个顶点之间路径的长度;(两个顶点之间的路径长度大都比较短,则相关性高)
  • 两个顶点之间的路径经过的顶点。(两顶点间的路径不经过出度比较大的顶点,则相关性高)

举一个简单的例子,如图2-19所示,用户A和物品c、e没有边相连,但是用户A和物品c有1条长度为3的路径相连,用户A和物品e有2条长度为3的路径相连。那么,顶点A与e之间的相关性要高于顶点A与c,因而物品e在用户A的推荐列表中应该排在物品c之前,因为顶点A与e之间有两条路径——(A, b, C, e)和(A, d, D, e)。其中,(A, b, C, e)路径经过的顶点的出度为(3, 2, 2, 2),而(A, d, D, e)路径经过的顶点的出度为(3, 2, 3, 2)。因此,(A, d, D, e)经过了一个出度比较大的顶点D,所以(A, d, D, e)对顶点A与e之间相关性的贡献要小于(A, b, C, e)。

1.3 基于随机游走的PersonalRank算法

假设要给用户u进行个性化推荐,可以从用户u对应的节点vuv_uvu​开始在用户物品二分图上进行随机游走。游走到任何一个节点时,首先按照概率 α 决定是继续游走,还是停止这次游走并从vuv_uvu​节点开始重新游走。如果决定继续游走,那么就从当前节点指向的节点中按照均匀分布随机选择一个节点作为游走下次经过的节点。这样,经过很多次随机游走后,每个物品节点被访问到的概率会收敛到一个数。最终的推荐列表中物品的权重就是物品节点的访问概率。
上面的描述写成公式就是:
    f(n)={α∑v′∈(v)PR(v′)∣out(v′)∣if (v≠vu)(1−α)+α∑v′∈(v)PR(v′)∣out(v′)∣if (v=vu)f(n) = \begin{cases} \alpha \displaystyle\sum_{v'\in (v)}\frac{PR(v')}{|out(v')|} & \quad \text{if }(v \neq v_u)\\ (1- \alpha) + \alpha \displaystyle\sum_{v'\in (v)}\frac{PR(v')}{|out(v')|} & \quad \text{if }(v = v_u) \end{cases}f(n)=⎩⎪⎪⎪⎨⎪⎪⎪⎧​αv′∈(v)∑​∣out(v′)∣PR(v′)​(1−α)+αv′∈(v)∑​∣out(v′)∣PR(v′)​​if (v​=vu​)if (v=vu​)​
其中,α\alphaα表示随机游走的概率;
PR(v’)表示上一次迭代顶点v’的重要度(在PageRank中PR(i)是网页i的访问概率,也就是重要度),赋初值迭代收敛得到;
在PageRank中out(j)表示网页j指向的网页集合,也就是j的出度。这里out(v’)也表示节点v’指向的顶点集合。

1.4 缺点和改进

虽然PersonalRank算法可以通过随机游走进行比较好的理论解释,但该算法在时间复杂度上有明显的缺点。因为在为每个用户进行推荐时,都需要在整个用户物品二分图上进行迭代,直到整个图上的每个顶点的PR值收敛。这一过程的时间复杂度非常高,不仅无法在线提供实时推荐,甚至离线生成推荐结果也很耗时。
为了解决PersonalRank每次都需要在全图迭代并因此造成时间复杂度很高的问题,<<推荐系统实战>>给出两种解决方案。第一种很容易想到,就是减少迭代次数,在收敛之前就停止。这样会影响最终的精度,但一般来说影响不会特别大。另一种方法就是从矩阵论出发,重新设计算法。
令M为用户物品二分图的转移概率矩阵:
    M(v,v′)=1∣out(v)∣M(v, v')=\dfrac{1}{|out(v)|}M(v,v′)=∣out(v)∣1​
那么,迭代公式可以转化为:
    rank=(1−α)r0+αMTrankrank = (1-\alpha)r_0 + \alpha M^T rankrank=(1−α)r0​+αMTrank
用矩阵论的方法解出上面的方程,得到:
    Rank=(1−α)(1−αMT)−1r0Rank = (1-\alpha)(1-\alpha M^T)^{-1}r_0Rank=(1−α)(1−αMT)−1r0​
因此,只需要计算一次(1−αMT)−1(1-\alpha M^T)^{-1}(1−αMT)−1,这里1−αMT1-\alpha M^T1−αMT是稀疏矩阵。可以通过稀疏矩阵快速求逆来得解(比如Generalized_minimal_residual_method)。
在scipy中提供了多种稀疏矩阵的存储方法:coo,lil,dia,dok,csr,csc等,各有各的优缺点,dok可以快速的按下标访问元素,csr和csc适合做矩阵的加法、乘法运算,lil省内存且按下标访问元素也很快。

参考实现:
https://github.com/lpty/recommendation

另外jamest给出了矩阵实现,代码如下:

#-*-coding:utf-8-*-
"""
author:jamest
date:20190310
PersonalRank function with Matrix
"""
import pandas as pd
import numpy as np
import time
import operator
from scipy.sparse import coo_matrix
from scipy.sparse.linalg import gmresclass PersonalRank:def __init__(self,X,Y):X,Y = ['user_'+str(x) for x in X],['item_'+str(y) for y in Y]self.G = self.get_graph(X,Y)def get_graph(self,X,Y):"""Args:X: user idY: item idReturns:graph:dic['user_id1':{'item_id1':1},  ... ]"""item_user = dict()for i in range(len(X)):user = X[i]item = Y[i]if item not in item_user:item_user[item] = {}item_user[item][user]=1user_item = dict()for i in range(len(Y)):user = X[i]item = Y[i]if user not in user_item:user_item[user] = {}user_item[user][item]=1G = dict(item_user,**user_item)return Gdef graph_to_m(self):"""Returns:a coo_matrix sparse mat Ma list,total user item pointsa dict,map all the point to row index"""graph = self.Gvertex = list(graph.keys())address_dict = {}total_len = len(vertex)for index in range(len(vertex)):address_dict[vertex[index]] = indexrow = []col = []data = []for element_i in graph:weight = round(1/len(graph[element_i]),3)row_index=  address_dict[element_i]for element_j in graph[element_i]:col_index = address_dict[element_j]row.append(row_index)col.append(col_index)data.append(weight)row = np.array(row)col = np.array(col)data = np.array(data)m = coo_matrix((data,(row,col)),shape=(total_len,total_len))return m,vertex,address_dictdef mat_all_point(self,m_mat,vertex,alpha):"""get E-alpha*m_mat.TArgs:m_matvertex:total item and user pointsalpha:the prob for random walkingReturns:a sparse"""total_len = len(vertex)row = []col = []data = []for index in range(total_len):row.append(index)col.append(index)data.append(1)row = np.array(row)col = np.array(col)data = np.array(data)eye_t = coo_matrix((data,(row,col)),shape=(total_len,total_len))return eye_t.tocsr()-alpha*m_mat.tocsr().transpose()def recommend_use_matrix(self, alpha, userID, K=10,use_matrix=True):"""Args:alpha:the prob for random walkinguserID:the user to recomK:recom item numReturns:a dic,key:itemid ,value:pr score"""m, vertex, address_dict = self.graph_to_m()userID = 'user_' + str(userID)print('add',address_dict)if userID not in address_dict:return []score_dict = {}recom_dict = {}mat_all = self.mat_all_point(m,vertex,alpha)index = address_dict[userID]initial_list = [[0] for row in range(len(vertex))]initial_list[index] = [1]r_zero = np.array(initial_list)res = gmres(mat_all,r_zero,tol=1e-8)[0]for index in range(len(res)):point = vertex[index]if len(point.strip().split('_'))<2:continueif point in self.G[userID]:continuescore_dict[point] = round(res[index],3)for zuhe in sorted(score_dict.items(),key=operator.itemgetter(1),reverse=True)[:K]:point,score = zuhe[0],zuhe[1]recom_dict[point] = scorereturn recom_dictif __name__ == '__main__':moviesPath = '../data/ml-1m/movies.dat'ratingsPath = '../data/ml-1m/ratings.dat'usersPath = '../data/ml-1m/users.dat'# usersDF = pd.read_csv(usersPath,index_col=None,sep='::',header=None,names=['user_id', 'gender', 'age', 'occupation', 'zip'])# moviesDF = pd.read_csv(moviesPath,index_col=None,sep='::',header=None,names=['movie_id', 'title', 'genres'])ratingsDF = pd.read_csv(ratingsPath, index_col=None, sep='::', header=None,names=['user_id', 'movie_id', 'rating', 'timestamp'])X=ratingsDF['user_id'][:1000]Y=ratingsDF['movie_id'][:1000]rank = PersonalRank(X,Y).recommend_use_matrix(alpha=0.8,userID=1,K=30)print('PersonalRank result',rank)

Stanford有一个快速计算Personal PageRank的Talk
其开源代码是scala写的。

部分摘录如下:
Personalized PageRank简介:

Given: 源 s, 目的 t,以及"teleport probability"(佩奇命名的科幻瞬间转移概率) α\alphaα
- 从源s开始随机游走;
- 每一步,以α\alphaα的概率停止前进,否则continue。
那么给出从 s 到 t 的Personalized PageRank:
     πs(t)=P[Walkfromsstopsatt]\bf{\pi}_s(t)= \mathbb{P}[\mathtt{Walk\, from}\,\textit{s}\, \mathtt{stops\, at}\,\textit{t}]πs​(t)=P[Walkfromsstopsatt]

  • 等价于特征向量(eigenvector)/平稳分布的定义。若马尔可夫链在n+1时刻状态空间的分布与n时刻的分布相同,则称
    此分布为平稳分布。若马尔可夫链的初始状态服从平稳分布,则该马尔可夫链为平稳过程。
  • FAST-PPR (快速personal PageRank)允许任意的起点集合,例如:
    randoms∈V⇒random s \in V \Rightarrowrandoms∈V⇒ Global PageRank
    randoms∈S⇒random s \in S \Rightarrowrandoms∈S⇒ personalize to S

目标
给定 α\alphaα,出发节点 s,单个的目的节点 t ,threshold δ\deltaδ
目标是估计: πs(t)\pi_s(t)πs​(t)
当πs(t)>δ\pi_s(t)>\deltaπs​(t)>δ时:

  • 源自于个性化搜索;
  • 只要πs(t)\pi_s(t)πs​(t),而不是整个πs\pi_sπs​向量;
  • 由于平均的πs\pi_sπs​ 是 1n\dfrac{1}{n}n1​,我们希望 δ∼1n\delta \sim \dfrac{1}{n}δ∼n1​。考虑实时性,运行时间必须<<1δ<< \dfrac{1}{\delta}<<δ1​。

根据
以前的蒙特卡洛算法以O(1δ)O(\frac{1}{\delta})O(δ1​)的复杂度从s开始随机游走,运行时间是Θ(1δ)\Theta(\dfrac{1}{\delta})Θ(δ1​)

以前的本地更新算法从目的节点t 沿着边反向游走,并在本地更新Personal PageRank值,平均运行时间为O(dˉδ)\large O(\dfrac{\bar{d}}{\delta})O(δdˉ​),其中dˉ=∣E∣∣V∣\bar{d}=\frac{|E|}{|V|}dˉ=∣V∣∣E∣​(边的个数/顶点个数)

得到定理:


Main idea:

基于随机游走的PersonalRank相关推荐

  1. 【图像融合】基于随机游走算法实现多焦点图像融合含Matlab代码

    1 内容介绍 近几年来,随机游走模型(random walk)与引导滤波器(guided filter)在图像处理领域受到了研究者们的广泛关注.前者已经被应用于图像处理的多种领域--图像融合.图像平滑 ...

  2. 基于随机游走Random Walk的图节点Node表示

    前言 在图中,如果能把节点表示成合适的数值,能做很多任务,例如节点分类,关系预测,聚类等等.如何把节点表示成计算机能看懂的数值目前也有很多方法,本文主要为大家介绍基于Random Walk的节点表示方 ...

  3. 基于随机游走的图嵌入之快速指南

    1. 背景 1.1 何为图嵌 图嵌入是利用节点属性.节点间拓扑关系将复杂.高维图数据进行向量化的一项技术. 图数据结构突破传统数据库按记录组织数据的限制,具备更灵活的现实数据建模能力.如何将图数据结构 ...

  4. 随机游走模型 matlab,基于随机游走的图像分割matlab代码

    [实例简介] 利用随机游走模型对图像进行了分割 编程环境是matlab 含有示例图片 可以直接运行 [实例截图] [核心代码] Randomwalksforimagesegmentation_matl ...

  5. 【蚊子无人机】基于matlab随机游走模型无人机消除蚊子路径规划【含Matlab源码 2433期】

    ⛄一.随机游走模型 随机游走模型是通过随机选取某一文献作为起点,随机游走所有的文献,根据信息特征相似性对游走线路作加权处理,查阅所有文献后完成聚类. 随机游走算法通过对数据集进行统一的定义,把给定的数 ...

  6. 图模型-随机游走算法

    文章目录 推荐基本概念 PageRank PersonalRank TextRank SimRank 推荐基本概念 其中用户user=[A,B,C],物品item=[a,b,c,d],用户和物品有以下 ...

  7. KDD 2019 | 结合属性随机游走的图递归网络

    今天给大家介绍德克萨斯A&M大学的Xiao Huang等人在KDD 2019发表的一篇文章"Graph Recurrent Networks with Attributed Rand ...

  8. 图机器学习 | 图信号处理、矩阵分解、随机游走和深度学习算法

    点上方计算机视觉联盟获取更多干货 仅作学术分享,不代表本公众号立场,侵权联系删除 转载于:专知 AI博士笔记系列推荐 周志华<机器学习>手推笔记正式开源!可打印版本附pdf下载链接 图是连 ...

  9. 带属性随机游走的图循环网络

    1. 背景     随机游走模型被广泛应用于从网络嵌入到标签传播的各种网络分析任务中.但是在真实的系统中,节点通常不是纯顶点,而是具有不同的特征.然而,为具有属性的网络开发随机游走模型是困难的,节点属 ...

  10. 【论文逐句精读】DeepWalk,随机游走实现图向量嵌入,自然语言处理与图的首次融合

    DeepWalk论文精读 阅读前的建议 背景知识 DeepWalk诞生背景和想要解决的问题 为什么可以借鉴NLP里面的方法? Embedding编码应该具有什么样的特性? 什么是随机游走(Random ...

最新文章

  1. ie下提示SCRIPT1028:缺少标识符、字符串或数字
  2. [Android]用架构师角度看插件化(3)-Replugin 需要占坑跳转?
  3. Android 客户端与服务器交互方式
  4. linux文件在哪个分区,linux下肿么查看目录所在的分区
  5. 尚学堂java 答案解析 第四章
  6. C#大型医院管理系统源码
  7. Java整形位运算避免精度缺失,浮点数精度问题透析:小数计算不准确+浮点数精度丢失根源...
  8. laravel 软删除
  9. 输入n个整数,输出其中的最大值
  10. 一个SAP程序员的2020年度总结:未知生,焉知死
  11. 期刊论文发表什么是省级刊物
  12. 简述什么是图灵机_图灵机简介和原理分析
  13. 代码不sao,如何成大佬迎娶白富美!
  14. mysql 家谱树查询_族谱树算法
  15. 西电计科19级保研情况分享
  16. 加速ssh连接的方法(优化ssh服务)
  17. [51Nod1371]填数字(DP)
  18. android - ROS Wiki 首页翻译(ros第一篇)
  19. proe中的一些使用技巧!
  20. 我读 《国富论》 - 亚当 · 斯密 / 论增进劳动生产力的因素,以及劳动生产物在各个阶层中自然分配的顺序

热门文章

  1. Excel如何快速验证银行卡号和姓名是否一致?
  2. 培根芦笋卷+蚝油家常豆腐+春笋甜椒拌饭
  3. C语言search函数的作用,C语言中库函数自带的查找函数bsearch
  4. 纯电动汽车快慢充原理介绍
  5. python使用matplotlib绘制3D图
  6. Visual C#程序设计基础pdf
  7. ps小技巧(复制选区)
  8. 还贷款 月供贷款计算
  9. python学习笔记-修改pip下载源-创建虚拟环境
  10. Spring系列之一:Spring入门