推荐系统4——图模型(PersonalRank)

在之前我也看了很多人写的推荐系统的博客,理论的、算法的都有,多是个人的理解和感悟,虽然很深刻,但是对于自己而言还是不成系统,于是我参考大牛项亮编著的《推荐系统实践》将该领域知识系统整理一遍,与大家一起学习。
本系列对应的代码请查看https://github.com/wangyuyunmu/Recommended-system-practice
上一篇总结了基于用户行为数据的推荐方法——隐语义,本篇继续总结基于用户行为数据的相关推荐方法——图模型。


1,图模型

user-item关系可以用图来表示,下面是user与item的二分图:
user-item二分图
寻找user与item之间关系的方法,就成了度量图中两个顶点的相关性。

度量图中两个顶点之间相关性的方法很多,但一般来说图中顶点的相关性主要取决于下面3个要素:
1)两个顶点之间的路径数;
2)两个顶点之间路径的长度;
3)两个顶点之间的路径经过的顶点。

相关性高的点一般具有如下性质:
1)两个顶点之间有很多路径相连;
2)连接两个顶点之间的路径长度都比较短;
3)连接两个顶点之间的路径不会经过出度比较大的顶点。
(出度:从一个点链接其他点的边的数量)

2,基于随机游走的Personal Rank算法

下式是PersonalRank的评价指标,PR可以看作是节点的权值或者重要性。out表示出度。
P R ( i ) = ( 1 − d ) r i + d ∑ j ∈ ( i ) P R ( j ) ∣ o u t ( j ) ∣ PR(i)=(1-d)r_i+d\sum_{j\in(i)} \frac{PR(j)}{\vert out(j) \vert} PR(i)=(1d)ri+dj(i)out(j)PR(j)

r i = { 1 i = u 0 i ≠ u r_i= \begin{cases} 1&{i=u}\\ 0&{i\neq u} \end{cases} ri={10i=ui=u
理论上初始化图模型节点后,按照以上公式进行随机游走,最后所有节点的权值将会收敛,通过排序就可以得到topN个相关节点。

上面描述的过于抽象,还是举个例子吧。
在这里插入图片描述
原谅我没有找到更清晰的图,自己又懒得重新做~~~

上面的二部图表示:user A对item a和c感兴趣,B对a b c d都感兴趣,C对c和d感兴趣。
本文假设每条边代表的感兴趣程度是一样的。现在我们要为user A推荐item,实际上就是计算A对所有item的感兴趣程度。在personal rank算法中不区分user节点和item节点,这样一来问题就转化成:对节点A来说,节点A B C a b c d的重要度各是多少。重要度用PR来表示。

初始赋予 PR(A)=1, PR(B)=PR( C)=PR(a)=PR(b)=PR( c)=PR(d)=0即对于A来说,他自身的重要度为满分,其他节点的重要度均为0。然后开始在图上游走。即,每次都是从PR不为0的节点开始游走,往前走一步。继续游走的概率是α,停留在当前节点的概率是1−α。

第一次游走,从A节点开始,以α概率向下游走,以50%的概率走向a和c,a和c得到了A的部分重要度,PR(a)=PR( c)= α*(1/2)*PR(A),PR(A) = 1-α

第二次游走,分别从节点A a c开始,往前走一步,这样
节点a分得A 1/2∗α的重要度,
节点c分得A 1/2∗α的重要度,
节点A分得a 1/2∗α的重要度,
节点A分得c 1/3∗α的重要度,
节点B分得a 1/2∗α的重要度,
节点B分得c 1/3∗α的重要度,
节点C分得c 1/3∗α的重要度。
最后PR(A)要加上1-α。

def PersonalRank(G, alpha, root, max_depth):
    rank = dict()
    rank = {x: 0 for x in G.keys()}
    rank[root] = 1
    for k in range(max_depth):
        tmp = {x: 0 for x in G.keys()}
        # 取出节点i和他的出边尾节点集合ri
        for i, ri in G.items():
            # 取节点i的出边的尾节点j以及边E(i,j)的权重wij,边的权重都为1,归一化后就是1/len(ri)
            for j, wij in ri.items():
                # 这里可以看出前一个step(k)生成的图每个节点以alpha概率向其他相关节点传递PR值,
                # 生成新的图,但是每个节点都有1-alpha概率保留PR,所以新图整体少了1-alpha
                tmp[j] += alpha * rank[i] / (1.0 * len(ri))
        tmp[root] += (1 - alpha)
        rank = tmp
    lst = sorted(rank.items(), key=lambda x: x[1], reverse=True)
    for ele in lst:
        print("%s:%.3f, \t" % (ele[0], ele[1]))
    return rank

为什么要在原节点加上1-alpha?
(或许我对与图模型还不是特别了解,这里仅自己做一些推测。如果后续有进展将会进一步更新)

以上公式后半部分很好理解,根据从某个节点开始游走的概率alpha,出度out,及节点PR值,得到游走到相应节点的PR值。
在这里插入图片描述
生成新的PR值对应的图时,实际上每个节点PR值的生成,都是原图每个节点以游走概率alpha权值以后的结果,所以从整体上来说,新图对应的PR值整体减少了1-alpha,这回造成什么后果呢?如果迭代次数不断加深,PR值将会趋近于0,将1-alpha给了参考用户u,这样整体上各节点PR加和为1,计算出的就是所有顶点相对于u的相关度。

下图是page rank的公式,是另一种迭代方式,1-a平均分配到每个节点。
P R ( i ) = 1 − d N + d ∑ j ∈ ( i ) P R ( j ) ∣ o u t ( j ) ∣ PR(i)=\frac{1-d}{N}+d\sum_{j\in(i)} \frac{PR(j)}{\vert out(j) \vert} PR(i)=N1d+dj(i)out(j)PR(j)

3,改进

因为给每个用户推荐的时候都需要在用户二分图上进行随机游走计算和迭代,直到每个PR值收敛,时间复杂度高。所以要对personal算法进行优化,一种优化算法是迭代到某一阈值就停止,一般对结果影响不大,另一种优化通过矩阵求解。
M i j = { 1 ∣ o u t ( i ) ∣ j ∈ o u t ( i ) 0 e l s e M_{ij}= \begin{cases} \frac{1}{\vert out(i) \vert}& { j \in out(i)} \\ 0& {else} \end{cases} Mij={out(i)10jout(i)else

r = ( 1 − α ) r 0 + α M T r r = (1-\alpha)r_0+\alpha M^Tr r=(1α)r0+αMTr
M是一个稀疏矩阵,如下所示:

data, row, col = [], [], []
    for u in train:
        for v in train[u]:
            data.append(1 / len(train[u]))# 保存所有节点出度
            row.append(users[u])          # 出度的节点
            col.append(items[v])          # 连接的节点
    for u in item_user:                   # user遍历完之后,再次遍历item
        for v in item_user[u]:
            data.append(1 / len(item_user[u]))
            row.append(items[u])
            col.append(users[v])
    # 行程稀疏矩阵,按照列排列row和col分别代表data位置的索引,shape不太赞同,我觉得应该是len(users)+len(items)而不是len(data)
    M = csc_matrix((data, (row, col)), shape=(len(users)+len(items),len(users)+len(items)))

通过训练数据获得稀疏矩阵M,测试的时候,初始化user,稀疏矩阵求逆即可:

    def GetRecommendation(user):
        seen_items = set(train[user])
        # 解矩阵方程 r = (1-a)r0 + a(M.T)r
        r0 = [[0] for i in range(len(users)+len(items))]
        r0[users[user]][0] = 1 #测试那个user就将该user设置为1,表示从此开始随机游走
        r0 = np.array(r0)

        r = linalg.gmres(eye(len(users) + len(items)) - alpha * M.T, (1 - alpha) * r0)  # gmres(A,b),解决稀疏Ax=b的求解问题,
        r = r[0][len(users):]  # user 之后的节点才是item

        idx = np.argsort(-r)[:N]         # 取反是为了从大到小排列
        recs = [(id2item[ii], r[ii]) for ii in idx]  #返回topN的item与PR值的tuple
        return recs

以上是二分图模型的求解思路,在基于物品标签的推荐中,会涉及(user、item、tag)的三分图,相应总结后续补充。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,让我来介绍一下外卖信息管理系统的UML静态模型预习内容。 在UML静态模型,我们通常会使用来表示系统的各种类以及它们之间的关系。在外卖信息管理系统,我们可以考虑以下类: 1. 用户(User)类:表示系统的用户,包括顾客和商家两种角色。 2. 订单(Order)类:表示顾客下的订单。 3. 商家(Merchant)类:表示系统的商家。 4. 店铺(Shop)类:表示商家所经营的店铺。 5. 菜品(Dish)类:表示店铺的菜品。 6. 购物车(ShoppingCart)类:表示顾客在下单前所添加的菜品列表。 7. 骑手(Deliveryman)类:表示系统的骑手。 8. 评价(Comment)类:表示顾客对订单、菜品、商家或骑手的评价。 以上这些类之间的关系可以考虑如下: 1. 用户和订单之间是一对多的关系,即一个用户可以下多个订单。 2. 商家和店铺之间是一对多的关系,即一个商家可以拥有多个店铺。 3. 店铺和菜品之间是一对多的关系,即一个店铺可以提供多种菜品。 4. 用户和购物车之间是一对一的关系,即一个用户只能有一个购物车。 5. 订单和购物车之间是一对多的关系,即一个订单可以包含多个购物车。 6. 订单和商家之间是多对一的关系,即多个订单可以对应同一个商家。 7. 订单和骑手之间是多对一的关系,即多个订单可以由同一个骑手配送。 8. 评价和订单、菜品、商家或骑手之间是一对多的关系,即一个评价可以针对多个对象。 以上就是外卖信息管理系统UML静态模型的预习内容,希望对你有所帮助。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值