推荐系统-基于PersonalRank推荐模型

来源:互联网 发布:证券交易软件 编辑:程序博客网 时间:2024/05/21 14:03

时间:2017年5月
出处:http://blog.csdn.net/csearch/article/details/71315610
声明:版权所有,转载请联系作者并注明出


在推荐系统中,用户对物品的行为数据可以转化成图的形式,具体来说,可以转化为二分图进行表示,基于图来考虑推荐问题也是一种常用的思路之一。

1.PageRank模型

PageRank是Google网页排序的经典算法,这里只做简要概述,详细的算法推导过程及实现,可以网上找找相关资料,挺多的。
PageRank是Larry Page 和 Sergey Brin设计的用来衡量特定网页相对于搜索引擎中其他网页的重要性的算法,其计算结果作为google搜索结果中网页排名的重要指标。
* 网页之间通过超链接相互连接,互联网上不计其数的网页就构成了一张超大的图。
* PageRank假设用户从所有网页中随机选择一个网页进行浏览,然后通过超链接在网页直接不断跳转。
* 算法令用户继续浏览的概率为d,用户以相等的概率在当前页面的所有超链接中随机选择一个继续浏览。
* 这是一个随机游走的过程。当经过很多次这样的游走之后,每个网页被访问用户访问到的概率就会收敛到一个稳定值。这个概率就是网页的重要性指标,被用于网页排名。

算法迭代关系式如下所示:

PR(i)=1alphaN+alphaiin(i)PR(j)|outer(j)|

上式中
* PR(i)是网页i的访问概率(也就是重要度)
* alpha是用户继续访问网页的概率
* N是网页总数
* in(i)表示指向网页i的网页集合
* out(j)表示网页j指向的网页集合

接下来我们分析一下这个公式,网页i被访问到的概率由两部分组成:
* 第一部分是网页i作为起点,第一个被和用户点击后停留在当前页面的概率,即:1alphaN
* 第二部分是用户点击其他网页后(无论网页i是不是起点),再次跳转回到网页i的概率:alphaiin(i)PR(j)|outer(j)|
这两部分的和便是网页i被点击到的概率。

2.PersonalRank模型

很多情况下,我们会从已有的模型中去推(套)演(用)到新的场景中,进一步观察这个模型在新的场景下是否有可取之处?
那么,从上述PageRank算法做简单的推演,我们用user节点和item节点替换上面的网页节点就可以计算出每个user,每个item在全局的重要性,给出全局的排名,显然这并不是我们想要的,我们需要计算的是物品节点相对于某一个用户节点u的相关性。
那具体该怎么做呢?Standford的Haveliwala于2002年在他《Topic-sensitive pagerank》一文中提出了PersonalRank算法,该算法能够为用户个性化的对所有物品进行排序。它的迭代公式如下:

PR(i)=(1d)ri+djin(i)PR(j)|outer(i)|

* ri=1,i=u
* ri=0,iu

我们发现PersonalRank跟PageRank的区别只是用ri替换了1/N,也就是说从不同点开始的概率不同。u表示我们推荐的目标用户,这样使用上式计算的就是所有顶点相对于顶点u的相关度。

对于相关性高的顶点有如下的定义:
(1)两个顶点之间有很多路径相连。
(2)连接两个顶点之间的路径长度都比较短。
(3)连接两个顶点之间的路径不会经过度比较大的顶点。
上面有一个概念需要理解,度,顶点的度是指和该顶点相关联的边数。

与PageRank随机选择一个点开始游走(也就是说从每个点开始的概率都是相同的)不同,如果我们要计算所有节点相对于用户u的相关度,则PersonalRank从用户u对应的节点开始游走,每到一个节点都以1-d的概率停止游走并从u重新开始,或者以d的概率继续游走,从当前节点指向的节点中按照均匀分布随机选择一个节点往下游走。这样经过很多轮游走之后,每个顶点被访问到的概率也会收敛趋于稳定,这个时候我们就可以用概率来进行排名了。

在执行算法之前,我们需要初始化每个节点的初始概率值。如果我们对用户u进行推荐,则令u对应的节点的初始访问概率为1,其他节点的初始访问概率为0,然后再使用迭代公式计算。而对于pageRank来说,由于每个节点的初始访问概率相同,所以所有节点的初始访问概率都是1/N (N是节点总数)。

3.PersonalRank实现Demo

3.1.读取数据

# dat_path = '../data/ml-1m/ratings.dat',sep="::"dat_path = "../data/ml-100k/u1.base";sep = "\t"names=['UserID','MovieID','Score','Time']df = pd.read_csv(dat_path,sep=sep,names=names)print("user_cnt: ",df['UserID'].unique().size,", movie_cnt: ",df['MovieID'].unique().size)df.head()

输出
(‘user_cnt: ‘, 943, ‘, movie_cnt: ‘, 1650)
Out[17]:
UserID MovieID Score Time
0 1 1 5 874965758
1 1 2 3 876893171
2 1 3 4 878542960
3 1 4 3 876893119
4 1 5 3 889751712

3.2.整理用户及物品二分图

  • 不考虑权重,只要用户对电影评过分便认为喜欢,e=1
### (1)用户顶点二元组def getUserGraph(df, userID=1):      '''''     获取目标用户二分图, 不计权重     :param frame: ratings数据     :param userID: 目标ID     :return: 二分图字典     '''      # print(userID)      itemList = list(set(df[df['UserID']==userID]['MovieID']))      graphDict = {'i'+str(item): 1 for item in itemList}      return graphDict  ### (2)物品顶点二元组def getItemGraph(df, itemID=1):      '''''     获取目标物品二分图, 不计权重     :param frame: ratings数据     :param userID: 目标ID     :return: 二分图字典     '''      # print(itemID)      userList = list(set(df[df['MovieID']==itemID]['UserID']))      graphDict = {'u'+str(user): 1 for user in userList}      return graphDict  ### (3)整理成规范二分图Gdef initGraph(df):      '''''     初始化二分图     :param frame: ratings数据集     :return: 二分图     '''      userList = list(set(df['UserID']))    itemList = list(set(df['MovieID']))    G = {}    # 将用户二分图信息组装到图G中    for user in userList:        G['u'+str(user)] = getUserGraph(df, user)    # 将物品二分图信息组装到图G中    for item in itemList:        G['i'+str(item)] = getItemGraph(df, item)      return G%time G = initGraph(frame)print len(G)

输出
CPU times: user 15.3 s, sys: 118 ms, total: 15.4 s
Wall time: 15.4 s
9746

3.3.利用PersonalRank算法进行计算

PersonalRank算法,这个算法是基于pageRank算法进行了一些变化,在pageRank算法中,计算出来的是每一个顶点相对其他顶点的相关性,代入到我们的用户物品二分图中,这显然不是我们想要的,我们需要的是所有物品相对于特定某个用户的相关性,有公式如下:
PR(u)=(1alpha)ru+alphaiin(u)PR(i)|outer(i)|
ru=1,u=root
ru=0,uroot
对比pageRank,不同点只在于r的值不同,root代表根节点,即我们的目标用户节点,意思便是我们每次都是从目标用户节点出发,进行随机游走,而不同于pageRank的起点是随机从所有网页中进行选择,personalRank算法得出的结果便是所有顶点相对于目标用户结点的相关性。

def personalRank(G, alpha, userID, iterCount=20):      '''''     随机游走迭代     :param G: 二分图     :param alpha: 随机游走的概率     :param userID: 目标用户     :param iterCount: 迭代次数     :return: series     '''      rank = {g: 0 for g in G.keys()}      rank['u'+str(userID)] = 1                                       # 根节点为起点选择概率为1,其他顶点为0      for k in range(iterCount):                                      # 按设定的迭代次数进行迭代运算        tmp = {g: 0 for g in G.keys()}          for i, ri in G.items():                                     #遍历每一个顶点              for j, wij in ri.items():                               #遍历每个顶点连接的顶点                  tmp[j] += alpha * rank[i] / len(ri)          tmp['u' + str(userID)] += 1 - alpha                         #根顶点r=1,加上1-alpha          rank = tmp      series = pd.Series(list(rank.values()), index=list(rank.keys()))      series = series.sort_values(ascending=False)      return series                                                   #返回排序后的series  # 计算userID为1的personalRank图模型%time s = personalRank(G, alpha=0.6, userID=1, iterCount=20)print s.size

输出
9746

3.4.推荐和目标用户没有直接相连而且是物品的顶点

def recommend(df, series, userID, TopN=10):      '''''     推荐TopN个用户没有评分的物品     :param frame: ratings数据     :param series: series     :param userID: 目标用户     :param TopN: TopN     :return: 推荐物品     '''    # 获取用户看过的电影列表    itemList = ['i'+str(i) for i in list(set(df[df['UserID']==userID]['MovieID']))]    # 获取推荐列表信息    recommendList = [{u: series[u]} for u in list(series.index) if u not in itemList and 'u' not in u]      return recommendList[:TopN]  %time r = recommend(frame, s, 1, TopN=10)print(r)

输出
CPU times: user 44.7 ms, sys: 1.18 ms, total: 45.9 ms
Wall time: 45.3 ms
[{‘i2858’: 0.00050075131015363185}, {‘i1196’: 0.00043310276435962858}, {‘i1210’: 0.00041226057350608546}, {‘i593’: 0.00038068361173254449}, {‘i480’: 0.00036954608314440505}, {‘i1198’: 0.00036616397789523248}, {‘i2396’: 0.00036178614890107583}, {‘i2571’: 0.00035883917557775883}, {‘i589’: 0.0003587179505317039}, {‘i110’: 0.00034614943986419495}]

参考文档
* 用PersonalRank实现基于图的推荐算法

0 0
原创粉丝点击