networkx pagerank

本来觉得是不想写这篇博客的,因为网上关于pagerank的介绍很多很多了,而且入门pagerank本来也不难,不过在networkx中实现的pagerank和网上大多数资料介绍的pagerank是不一样的,这一点网上的说明却比较少,因此本博客着重于讲一下这一点。

1.pagerank介绍

首先给出我看的一些pagerank的连接。

  1. pagerank的简单介绍及实现,这个链接中介绍了最原始的pagerank的思路,写得也比较清楚,之后给出了实现的代码,代码写得也是非常清晰易懂,但是这个实现的pagerank是最原始的pagerank,会出现较多的问题,其迭代公式为:
    在这里插入图片描述
  2. pagerank更深入的介绍,这个链接中的介绍相对而言就更加深入了,给出的迭代公式也是经过改进之后的,这个也是网上绝大多数资料所说的pagerank。
    在这里插入图片描述
  3. networkx实现pagerank的源码解析官方文档,以上是一个源码解析和官方文档,networkx中的pagerank和1,2链接中的pagerank的迭代公式是不同的,源码解析中的说明也是正确的,但是我觉得写得还是不那么好理解,所以我希望进一步解释下,这也就是写本博客的缘由。并且我也通过两个例子证实第二个2链接中的迭代公式与源码解析中的公式是不一样的,但是有意思的是结果似乎是相同的,但是没有从理论上想出来是为什么,主要是数学功底不够,要是有大佬知道可以留言。

2.networkx实现的pagerank的说明

首先,还是要贴一下源码中的对各输入参数的说明。看这个就很容易理解各个参数的意思了。基本上参数都可以很容易理解了,之前给的链接中的博客都有说明了,唯一可能有些费解的只有dangling这个参数,这个参数是为dangling node准备的,什么是dangling node,也就是出度为0的结点,在上述的博文中叫做终止点,因为dangling node会导致迭代过程中dangling node的pr值越来越大,所以需要对迭代公式做改进的,在这里我们可以输入一个字典,指定dangling node的权重,如果不指定的话,就是默认dangling node和其他结点的权重一样,也就是权重全部平均。其他应该就没问题了。

def pagerank(G, alpha=0.85, personalization=None,
             max_iter=100, tol=1.0e-6, nstart=None, weight='weight',
             dangling=None):
    """Return the PageRank of the nodes in the graph.

    PageRank computes a ranking of the nodes in the graph G based on
    the structure of the incoming links. It was originally designed as
    an algorithm to rank web pages.

    Parameters
    ----------
    G : graph
      A NetworkX graph.  Undirected graphs will be converted to a directed
      graph with two directed edges for each undirected edge.

    alpha : float, optional
      Damping parameter for PageRank, default=0.85.

    personalization: dict, optional
      The "personalization vector" consisting of a dictionary with a
      key for every graph node and nonzero personalization value for each node.
      By default, a uniform distribution is used.

    max_iter : integer, optional
      Maximum number of iterations in power method eigenvalue solver.

    tol : float, optional
      Error tolerance used to check convergence in power method solver.

    nstart : dictionary, optional
      Starting value of PageRank iteration for each node.

    weight : key, optional
      Edge data key to use as weight.  If None weights are set to 1.

    dangling: dict, optional
      The outedges to be assigned to any "dangling" nodes, i.e., nodes without
      any outedges. The dict key is the node the outedge points to and the dict
      value is the weight of that outedge. By default, dangling nodes are given
      outedges according to the personalization vector (uniform if not
      specified). This must be selected to result in an irreducible transition
      matrix (see notes under google_matrix). It may be common to have the
      dangling dict to be the same as the personalization dict.

    Returns
    -------
    pagerank : dictionary
       Dictionary of nodes with PageRank as value

    Examples
    --------
    >>> G = nx.DiGraph(nx.path_graph(4))
    >>> pr = nx.pagerank(G, alpha=0.9)

    Notes
    -----
    The eigenvector calculation is done by the power iteration method
    and has no guarantee of convergence.  The iteration will stop
    after max_iter iterations or an error tolerance of
    number_of_nodes(G)*tol has been reached.

    The PageRank algorithm was designed for directed graphs but this
    algorithm does not check if the input graph is directed and will
    execute on undirected graphs by converting each edge in the
    directed graph to two edges.

    See Also
    --------
    pagerank_numpy, pagerank_scipy, google_matrix

    References
    ----------
    .. [1] A. Langville and C. Meyer,
       "A survey of eigenvector methods of web information retrieval."
       http://citeseer.ist.psu.edu/713792.html
    .. [2] Page, Lawrence; Brin, Sergey; Motwani, Rajeev and Winograd, Terry,
       The PageRank citation ranking: Bringing order to the Web. 1999
       http://dbpubs.stanford.edu:8090/pub/showDoc.Fulltext?lang=en&doc=1999-66&format=pdf
    """

接下来摘抄源码解析博客中的对源码中迭代公式的说明:

PR=alpha * (A * PR+dangling分配)+(1-alpha) * 平均分配

也就是三部分,A*PR其实是我们用图矩阵分配的,dangling分配则是对dangling node的PR值进行分配,(1-alpha)分配则是天下为公大家一人一份分配的

dangling node 也就是悬空结点,它的出度为0,也就是无法从它到任何其他结点,解决办法是增加一定的随机性,dangling分配其实就是加上一个随机向量,也就是无法从这个结点去往任何其他结点,但是可能会随机重新去一个结点,也可以这么理解,到了一个网站,这个网站不连接到任何网站,但是浏览者可能重新随机打开一个页面。

其实通俗的来说,我们可以将PageRank看成抢夺大赛,有三种抢夺机制。

1,A*PR这种是自由分配,大家都愿意参与竞争交流的分配

2,dangling是强制分配,有点类似打倒土豪分田地的感觉,你不参与自由市场,那好,我们就特地帮你强制分。

3,平均分配,其实就是有个机会大家实现共产主义了,不让spider trap这种产生rank sink的节点捞太多油水,其实客观上也是在帮dangling分配。

从图和矩阵的角度来说,可以这样理解,我们这个矩阵可以看出是个有向图

矩阵要收敛–>矩阵有唯一解–>n阶方阵对应有向图是强连通的–>两个节点相互可达,1能到2,2能到1

如果是个强连通图,就是我们上面说的第1种情况,自由竞争,那么我们可以确定是收敛的

不然就会有spider trap造成rank sink问题

我们可以发现上述的迭代公式和之前所有的迭代公式都是不一样的。
在这里插入图片描述
在这里插入图片描述
networkx中的迭代公式里多了一个dangling分配,其他都是一模一样的。这个一开始我有一些难以理解,因为感觉dangling分配似乎并不是必要的,如果为了解决dangling node的问题,最后加一个随机分配就可以了,随机分配就意味着一定几率会随机跳转到一个全新的网页。接下来,我们来看一下源码中的这一部分的关键代码,来更进一步看看pr值究竟是咋样更新迭代的。

for _ in range(max_iter):
    xlast = x
    x = dict.fromkeys(xlast.keys(), 0)  #x初值
    danglesum = alpha * sum(xlast[n] for n in dangling_nodes) #第2部分:计算dangling_nodes的PR总值
    for n in x:        
        for nbr in W[n]:
            x[nbr] += alpha * xlast[n] * W[n][nbr][weight]    #第1部分:将节点n的PR资源分配给各个节点,循环之
    for n in x:         
        x[n] += danglesum * dangling_weights[n] + (1.0 - alpha) * p[n]   #第3部分:节点n加上dangling nodes和均分的值
 
    # 迭代检查
    err = sum([abs(x[n] - xlast[n]) for n in x])
    if err < N*tol:
        return x

从上面的源码可以看到首先计算了一下danglesum,也就是把dangling node的pr值全部加起来求和,x[nbr] += alpha * xlast[n] * W[n][nbr][weight]这个也就是pr值的循环迭代了,x[n] += danglesum * dangling_weights[n] + (1.0 - alpha) * p[n]这个包含两部分,(1.0 - alpha) * p[n]很容易理解肯定就是平均分配了,前面这个就是dangling分配了,也就是用danglesum乘上dangling node的权重,如果没有给定的话,就是所有的node权重都一样,即1/N。

3.验证说法的正确性及2个例子

这一部分就来验证下说法的正确性以及networkx这样实现的好处,之后我会给出一个例子。
让我们假设这样一个图结构:
在这里插入图片描述
一共就两个node,A指向B,也就是说B是一个dangling node,在这个情况下,我们使用networkx中的pagerank进行计算得到的pr值就如上图所示,如何验证这两个pr值计算的迭代公式确实是PR=alpha * (A * PR+dangling分配)+(1-alpha) * 平均分配呢?只需要将计算得到的pr值代入这个公式在进行一次迭代,如果再一次迭代后得到的也是这个值的话说明确实是使用这个迭代公式的。
P R ( A ) = a l p h a ∗ ( A ∗ P R + d a n g l i n g 分 配 ) + ( 1 − a l p h a ) ∗ 平 均 分 配 = 0.85 ∗ 0.5 ∗ 0.649123 + 0.15 / 2 = 0.350877 PR(A)=alpha * (A * PR+dangling分配)+(1-alpha) * 平均分配=0.85 * 0.5 * 0.649123 + 0.15/2 = 0.350877 PR(A)=alpha(APR+dangling)+(1alpha)=0.850.50.649123+0.15/2=0.350877
P R ( B ) = a l p h a ∗ ( A ∗ P R + d a n g l i n g 分 配 ) + ( 1 − a l p h a ) ∗ 平 均 分 配 = 0.350877 + 0.85 ∗ 0.35087 = 0.649123 PR(B)=alpha * (A * PR+dangling分配)+(1-alpha) * 平均分配=0.350877+0.85 * 0.35087=0.649123 PR(B)=alpha(APR+dangling)+(1alpha)=0.350877+0.850.35087=0.649123
确实和上述得到的一致,得证。

使用链接2中的博文中的迭代公式进行计算
我们可以尝试使用以前的计算方法去进行计算,即使用如下的迭代公式:
在这里插入图片描述
初始时赴初值pr均是0.5,最终就会是下面这样,我们可以发现是不归一化的。
P R ( A ) = 0.15 / 2 = 0.075 PR(A)= 0.15/2 = 0.075 PR(A)=0.15/2=0.075
P R ( B ) = 0.15 / 2 + 0.85 ∗ 0.075 = 0.13875 PR(B)= 0.15/2+0.85*0.075 = 0.13875 PR(B)=0.15/2+0.850.075=0.13875
进行归一化后,令人惊奇地发现结果和networkx输出的结果是一致的。
那么这个是不是巧合呢?于是我又使用三个结点进行实验,如下图所示:
在这里插入图片描述
并且也使用和上述一样的方法进行了实验,发现使用源码解析中的公式进行计算就是得到上述的PR值,然后又使用链接2中的博文中的公式进行计算,得到值后在进行归一化,依然得到了一样的结论,也就是计算结果和networkx输出的结果一致。

4.猜测与结论

于是总结上述的两个实验,我猜测源码解析中的PR迭代公式和下图中PR计算迭代公式效果是一样的。
在这里插入图片描述
两者唯一的区别是源码解析中的公式最后迭代得到的直接就是归一化后的PR值,而使用上面图片中的公式迭代计算得到的是没有归一化的数值,但是结果是一模一样的。
可惜的是我无法从理论推导中给出证明,要是有大佬可以指点一下,将不胜感激,即下面两个公式为什么效果是一样的

在这里插入图片描述

在这里插入图片描述


补充:上述例子的代码的获取连接

猜你喜欢

转载自blog.csdn.net/qq_39805362/article/details/106956210