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值代入這個公式在進行一次迭代,如果再一次迭代後得到的也是這個值的話說明確實是使用這個迭代公式的。
PR(A)=alpha(APR+dangling)+(1alpha)=0.850.50.649123+0.15/2=0.350877PR(A)=alpha * (A * PR+dangling分配)+(1-alpha) * 平均分配=0.85 * 0.5 * 0.649123 + 0.15/2 = 0.350877
PR(B)=alpha(APR+dangling)+(1alpha)=0.350877+0.850.35087=0.649123PR(B)=alpha * (A * PR+dangling分配)+(1-alpha) * 平均分配=0.350877+0.85 * 0.35087=0.649123
確實和上述得到的一致,得證。

使用鏈接2中的博文中的迭代公式進行計算
我們可以嘗試使用以前的計算方法去進行計算,即使用如下的迭代公式:
在這裏插入圖片描述
初始時赴初值pr均是0.5,最終就會是下面這樣,我們可以發現是不歸一化的。
PR(A)=0.15/2=0.075PR(A)= 0.15/2 = 0.075
PR(B)=0.15/2+0.850.075=0.13875PR(B)= 0.15/2+0.85*0.075 = 0.13875
進行歸一化後,令人驚奇地發現結果和networkx輸出的結果是一致的。
那麼這個是不是巧合呢?於是我又使用三個結點進行實驗,如下圖所示:
在這裏插入圖片描述
並且也使用和上述一樣的方法進行了實驗,發現使用源碼解析中的公式進行計算就是得到上述的PR值,然後又使用鏈接2中的博文中的公式進行計算,得到值後在進行歸一化,依然得到了一樣的結論,也就是計算結果和networkx輸出的結果一致。

4.猜測與結論

於是總結上述的兩個實驗,我猜測源碼解析中的PR迭代公式和下圖中PR計算迭代公式效果是一樣的。
在這裏插入圖片描述
兩者唯一的區別是源碼解析中的公式最後迭代得到的直接就是歸一化後的PR值,而使用上面圖片中的公式迭代計算得到的是沒有歸一化的數值,但是結果是一模一樣的。
可惜的是我無法從理論推導中給出證明,要是有大佬可以指點一下,將不勝感激,即下面兩個公式爲什麼效果是一樣的

在這裏插入圖片描述

在這裏插入圖片描述


補充:上述例子的代碼的獲取連接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章