PageRank演算法概述與Python實現

郝hai發表於2024-04-27

PageRank演算法是一種用於評估網頁重要性的演算法。它基於網頁之間的連結結構來確定網頁的權重和重要性。演算法的核心思想是透過迭代計算網頁之間的連結關係,以確定每個網頁的權重。它將網際網路視為一個有向圖,其中網頁是節點,連結是有向邊。演算法透過以下方式計算網頁的PageRank值:每個網頁的初始PageRank值相等,然後透過迭代更新計算,將每個網頁的PageRank值傳遞給連結到它的網頁,直到收斂為止。在計算過程中,還引入了阻尼因子,用於模擬使用者在瀏覽網頁時的隨機跳轉行為。這樣可以確保演算法收斂並得到穩定的結果。PageRank演算法是Coogle搜尋擎對檢索結果的一種排序演算法。它的基本思想主要是來自傳統文獻計量學中的文獻引文分析,即一篇文獻的質量和重要性可以透過其他文獻對其引用的數量和引文質量來衡量,也就是說,一篇文獻被其他文獻引用越多,並且引用的文獻的質量越高,則該文獻本身就越重要,它也被廣泛應用於其他領域,如網路分析、推薦系統和資料探勘。

一、PageRank演算法思想

PageRank演算法是Google創始人提出的一種用於標識網頁重要性的方法,它主要用於網頁的排序。PR值越高說明網站越重要,因此其排名也越靠前。PageRank演算法的核心思想是基於有向圖上的隨機遊走模型,這個模型描述了一個隨機遊走者如何沿著圖的邊隨機移動,從一個節點訪問到另一個節點。在滿足某些條件的前提下,這個隨機遊走過程最終會收斂到一個平穩分佈,在這個平穩分佈中,每個節點被訪問的機率即為其PageRank值,這個值可以被解釋為節點的重要性。
PagsRank演算法的具體實現可以利用網頁所對應圖的鄰接矩陣來表達超連結關係。首先寫出所對應圖的鄰接矩陣\(W\),為了能將網頁的頁面等級值平均分配給該網頁所連結指向的網頁,對\(W\)各個行向量進行歸一化處理,得到矩陣\(P\)。矩陣\(P\)稱為狀態轉移機率矩陣,它的各個行向量元素之和為1,\(P^T\)的最大特徵值(一定為1)所對應的歸一化特徵向量即為各頂點的PageRank值。PageRank值的計算步驟如下:

  • 構造有向圖\(D=(V,A,W)\),其中\(V=\{v_1,v_2,...,v_N\}\)為頂點集合,每一個網頁是的一個頂點,\(A\)為弧的集合,網頁間的每一個超連結是圖的一條弧,鄰接矩陣\(W=(w_{ij})_{N\times N}\)​,如果從網頁\(i\)到網頁\(j\)有超連結,則\(w_{ij}=1\),否則為0。
  • 記矩陣 \({r_i} = \sum\limits_{j = 1}^N {{w_{ij}}}\)​為第\(i\)行的和,它給出了頁面\(i\)的鏈出連結數目。定義矩陣\(P=(p_{ij})_{N \times N}\)​如下:

    \[p_{ij}=\frac{{{w_{ij}}}}{{{r_i}}} \]

  • \(P^T\)的最大特徵值(一定為1)所對應的歸一化特徵向量即為各頂點的PageRank值。

1.1 示例1

影響力評價問題,見下圖給出了6篇文章之間的引用關係,即文章1分別引用了文章2、4、5、6;文章2分別引用了文章4、5、6;等等,試給出6篇文章影響力大小的排序。

網路圖 PageRank
import numpy as np
from scipy.sparse.linalg import eigs
import pylab as plt
import sympy as sp

L = [(1,2),(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,1),(3,2),(3,4),(4,5),(4,6),(5,3),(5,6),(6,3)]
w = np.zeros((6, 6))  # 鄰接矩陣初始化
for i in range(len(L)):
    w[L[i][0]-1, L[i][1]-1] = 1
r = np.sum(w, axis=1, keepdims=True)
for i in range(len(r)):
    if r[i][0] != 0:
        r[i][0] = sp.Rational(r[i][0] ** -1)

r = np.array(r)
print(r) # 由於0不為分母,先將r成以-1方,避免相除出錯。

P = w * r            # 這裡利用矩陣廣播
print(P)
val, vec = eigs(P.T, 1)
V = vec.real
V = V.flatten()  # 展開成(n,)形式的陣列
V = V/V.sum()
print("V=", np.round(V, 4))
plt.bar(range(1, len(w)+1), V, width=0.6, color='b')
plt.show()

1.2 示例2

PageRank演算法是圖的連結分析的代表性演算法,屬於圖資料上的無監督學習方法。其基本想法是在一個有向圖上定義一個隨機遊走模型,即一階馬爾科夫鏈,描述隨機遊走者沿著有向圖隨機訪問各個結點的行為。

網路圖 轉移機率矩陣
import numpy as np
from fractions import Fraction

np.set_printoptions(formatter={'all': lambda x: str(Fraction(x).limit_denominator())})  # 格式化 保留分數,不至於精度丟失
M = np.array([[0, 1 / 2, 1, 0], [1 / 3, 0, 0, 1 / 2], [1 / 3, 0, 0, 1 / 2], [1 / 3, 1 / 2, 0, 0]])  # 根據有向圖得出的轉移矩陣
R = np.array([1 / 4, 1 / 4, 1 / 4, 1 / 4]).reshape(4, 1)  # 初始R0

def diedai(M, R):  # 定義一個迭代函式,直至MR=R時,輸出R
    count = 0
    while (True):
        R_1 = np.dot(M, R)
        if ((R_1 == R).any()):  # 判斷兩個陣列是否相等
            print(R_1)
            print('迭代次數為:', count)
            break
        else:
            R = np.copy(R_1)
            count += 1

diedai(M, R)

二、演算法算例

2.1 示例1

  • PageRank公式:

\[P R(a)_{i+1}=\sum_{i=0}^n \frac{P R(T i)_i}{L(T i)} \]

\(\mathrm{PR}(\mathrm{Ti})\) :其他節點的 (指向 a 節點) PR值
\(\mathrm{L}(\mathrm{Ti})\) : 其他節點的 (指向 a 節點) 出鏈數
\(\mathrm{i}\) : 迴圈次數

初始化的PR值為 \(1/N=1/4\)

\[i=1, P R(C)_1=\frac{P R(A)_0}{L(A)}+\frac{P R(B)_0}{L(B)}=\frac{\frac{1}{4}}{2}+\frac{\frac{1}{4}}{1}=\frac{3}{8} \]

  • 矩陣化表達

\(A\) 將跳轉到 \(B\)\(C\) 的機率為 \(1 / 2\);從D將跳轉到 A 的機率為 1 。(矩陣的列表示出鏈)

  • 透過矩陣化表達,快速計算 PR 值

\[\begin{aligned} & P R(a)=M * V \\ & {\left[\begin{array}{cccc} 0 & 0 & 1 / 2 & 1 \\ 1 / 2 & 0 & 0 & 0 \\ 1 / 2 & 1 & 0 & 0 \\ 0 & 0 & 1 / 2 & 0 \end{array}\right] \times\left[\begin{array}{l} 1 / 4 \\ 1 / 4 \\ 1 / 4 \\ 1 / 4 \end{array}\right]=\left[\begin{array}{l} 3 / 8 \\ 1 / 8 \\ 3 / 8 \\ 1 / 8 \end{array}\right]} \end{aligned} \]

從A將跳轉到 B 或 C 的機率為 1/2。
從D將跳轉到 A 的機率為 1。(矩陣的列表示出鏈)

  • 透過矩陣化表達,快速計算PageRank值

\[(第一次迭代得到的 PR 值) \]

\begin{aligned}
& \begin{array}{llll}
\mathrm{A} & \mathrm{B} & \mathrm{C} & \mathrm{D} \
\mathrm{A} \
\mathrm{C} \
\mathrm{D}
\end{array}\left[\begin{array}{llll}
0 & 0 & 1 / 2 & 1 \
1 / 2 & 0 & 0 & 0 \
1 / 2 & 1 & 0 & 0 \
0 & 0 & 1 / 2 & 0
\end{array}\right] \mathbf{X}\left[\begin{array}{l}
3 / 8 \
1 / 8 \
3 / 8 \
1 / 8
\end{array}\right]=\left[\begin{array}{l}
5 / 16 \
3 / 16 \
5 / 16 \
3 / 16
\end{array}\right] \
&
\end{aligned}

\[第一次迭代:得到的$PR$ 值 第二次迭代:得到的$PR$ 值 ####2.2 示例2 假設有4個網頁,見下圖。他們之間的連結資訊如上圖所示,A跳轉到B、C、D的機率各位1/3,B跳轉到A、D的機率為1/2,C跳轉到A的機率為1,因此我們可以得到轉移矩陣為M。 | 網路連線圖 | 機率轉移矩陣 | | ---- | ---- | | ![](https://img2024.cnblogs.com/blog/2835440/202404/2835440-20240427145459146-1010603428.png)| ![](https://img2024.cnblogs.com/blog/2835440/202404/2835440-20240427145534317-1442273901.jpg) | \]

M=\left[\begin{array}{cccc}
0 & 1 / 2 & 1 & 0 \
1 / 3 & 0 & 0 & 1 / 2 \
1 / 3 & 0 & 0 & 1 / 2 \
1 / 3 & 1 / 2 & 0 & 0
\end{array}\right]

\[ 我們假設 $A 、 B 、 C 、 D$ 四個頁面的初始影響力都是相同的,即: \]

v_0=\left[\begin{array}{l}
1 / 4 \
1 / 4 \
1 / 4 \
1 / 4
\end{array}\right]

\[ 經過第一次轉移後,各頁面的影響因子 $v\{1\}$ 變為: \]

v_1=M v_0=\left[\begin{array}{cccc}
0 & 1 / 2 & 1 & 0 \
1 / 3 & 0 & 0 & 1 / 2 \
1 / 3 & 0 & 0 & 1 / 2 \
1 / 3 & 1 / 2 & 0 & 0
\end{array}\right]\left[\begin{array}{l}
1 / 4 \
1 / 4 \
1 / 4 \
1 / 4
\end{array}\right]=\left[\begin{array}{l}
9 / 24 \
5 / 24 \
5 / 24 \
5 / 24
\end{array}\right]

\[ 然後我們再用轉移矩陣乘以 $v\{1\}$ 得到 $v\{2\}$ 結果,直到迭代 $n$ 次 $v\{n\}$ 影響因子不在發生變化,可以收玫為 $(0.3333,0.2222,0.2222,0.2222)$ ,也就是對應A、B、C、D四個頁面下的PageRank值。考慮到頁面可能會自身迴圈和陷阱問題,可再加一個網頁跳轉機率 $p$ ,公式變為 $v=p^* M^* v+(1-p)^{\star} v$。 ```Python from numpy import * a = array([[0,1,1,0],[1,0,0,1],[1,0,0,1],[1,1,0,0]],dtype=float) #構造轉移矩陣 def transPre(data): b = transpose(data) #把矩陣轉置 c = zeros((a.shape),dtype=float) #把所有的元素重新分配 for i in range(a.shape[0]): for j in range(a.shape[1]): c[i][j] = data[i][j] / (b[j].sum()) return c # print(transPre(a)) def initiPre(c): # pr值的初始化 pr = zeros((c.shape[0],1),dtype=float) for i in range(c.shape[0]): pr[i] = float(1)/c.shape[0] return pr # print(initiPre(a)) def PageRank(p,m,v): #pageRank演算法 #p是網頁跳轉機率,m是轉移矩陣,v是pr值 while ((v == p*dot(m,v) + (1-p)*v).all() == False): v = p*dot(m,v) + (1-p)*v # print(v) print((v == p*dot(m,v) + (1-p)*v).all()) return v if __name__ == '__main__': M = transPre(a) pr = initiPre(M) p = 0.85 print(PageRank(p,M,pr)) ``` ###三、pagerank的Python實現 ####3.1 PageRank基本定義求PageRank值 ```Python import numpy as np def pagerank_basic(M, tol=1e-8, max_iter=1000): """使用PageRank的基本定義求解PageRank值 要求有向圖是強聯通且非週期性的 :param M: 轉移機率矩陣 :param tol: 容差 :param max_iter: 最大迭代次數 :return: PageRank值(平穩分佈) """ n_components = len(M) # 初始狀態分佈:均勻分佈 pr0 = np.array([1 / n_components] * n_components) # 迭代尋找平穩狀態 for _ in range(max_iter): pr1 = np.dot(M, pr0) # 判斷迭代更新量是否小於容差 if np.sum(np.abs(pr0 - pr1)) < tol: break pr0 = pr1 return pr0 if __name__ == "__main__": np.set_printoptions(precision=2, suppress=True) P = np.array([[0, 1 / 2, 1, 0], [1 / 3, 0, 0, 1 / 2], [1 / 3, 0, 0, 1 / 2], [1 / 3, 1 / 2, 0, 0]]) print(pagerank_basic(P)) # [0.33 0.22 0.22 0.22] ``` ####3.2 迭代演算法 ```Pyhon import numpy as np def pagerank_1(M, d=0.8, tol=1e-8, max_iter=1000): """PageRank的迭代演算法 :param M: 轉移機率矩陣 :param d: 阻尼因子 :param tol: 容差 :param max_iter: 最大迭代次數 :return: PageRank值(平穩分佈) """ n_components = len(M) # 初始狀態分佈:均勻分佈 pr0 = np.array([1 / n_components] * n_components) # 迭代尋找平穩狀態 for _ in range(max_iter): pr1 = d * np.dot(M, pr0) + (1 - d) / n_components # 判斷迭代更新量是否小於容差 if np.sum(np.abs(pr0 - pr1)) < tol: break pr0 = pr1 return pr0 if __name__ == "__main__": np.set_printoptions(precision=2, suppress=True) P = np.array([[0, 1 / 2, 0, 0], [1 / 3, 0, 0, 1 / 2], [1 / 3, 0, 1, 1 / 2], [1 / 3, 1 / 2, 0, 0]]) print(pagerank_1(P)) # [0.1 0.13 0.64 0.13] ``` ####3.3 冪法計算 ```Pyhton import numpy as np def pagerank_2(M, d=0.8, tol=1e-8, max_iter=1000): """計算一般PageRank的冪法 :param M: 轉移機率矩陣 :param d: 阻尼因子 :param tol: 容差 :param max_iter: 最大迭代次數 :return: PageRank值(平穩分佈) """ n_components = len(M) # 選擇初始向量x0:均勻分佈 x0 = np.array([1 / n_components] * n_components) # 計算有向圖的一般轉移矩陣A A = d * M + (1 - d) / n_components # 迭代並規範化結果向量 for _ in range(max_iter): x1 = np.dot(A, x0) x1 /= np.max(x1) # 判斷迭代更新量是否小於容差 if np.sum(np.abs(x0 - x1)) < tol: break x0 = x1 # 對結果進行規範化處理,使其表示機率分佈 x0 /= np.sum(x0) return x0 if __name__ == "__main__": np.set_printoptions(precision=2, suppress=True) P = np.array([[0, 1 / 2, 0, 0], [1 / 3, 0, 0, 1 / 2], [1 / 3, 0, 1, 1 / 2], [1 / 3, 1 / 2, 0, 0]]) print(pagerank_2(P)) # [0.1 0.13 0.64 0.13] ``` ###總結 PageRank演算法的應用場合廣泛,其中最為人所熟知的是在搜尋引擎中的應用。谷歌等頂級搜尋引擎的排名演算法中都運用了PageRank演算法。透過計算每個網頁的> PageRank值,搜尋引擎能夠確定網頁的等級,並將其作為搜尋結果排序的重要依據。pageRank演算法應用如下: > 搜尋引擎排名:它幫助搜尋引擎確定網頁在搜尋結果中的排名,使得更重要和相關的網頁更容易被使用者找到。這樣,使用者在進行搜尋時,更有可能得到高質量、相關性強的網頁結果。 > 網路分析:PageRank演算法也被用於分析其他型別的網路,如社交網路、引用網路等。透過計算節點的PageRank值,可以識別網路中的重要節點、中心節點和關鍵節點。 > 推薦系統:在推薦系統中,PageRank演算法可以用來識別使用者可能感興趣的內容或產品。透過分析使用者之間的關係和他們對內容的反應,可以為使用者推薦最相關的內容。 > 媒體網站最佳化:媒體和新聞網站可以使用PageRank演算法來最佳化其內容的佈局和連結結構,以提高重要文章和頁面的曝光度,並增加使用者的點選量和訪問量。 > 資料探勘:PageRank演算法在資料探勘領域也有廣泛應用,例如在社交網路分析、文字分析和推薦系統中。透過分析連結關係,可以發現隱藏在資料中的模式和趨勢。 總的來說,PageRank演算法不僅是一種用於搜尋引擎排名的技術,還是一種通用的網路分析工具,可以在許多領域中發揮作用,幫助人們理解和利用複雜的網路結構。值得注意的是,PageRank演算法並非完美無缺。在實際應用中,可能會遇到一些問題和挑戰,如垃圾連結、連結欺詐等。這些問題可能會干擾PageRank演算法的計算結果,從而影響網頁排名的準確性。因此,在使用PageRank演算法時,需要採取相應的措施來應對這些問題,確保演算法的有效性和準確性。相信透過不斷的研究和改進,相信PageRank演算法在未來會有更廣泛的應用和更好的表現。 ###參考資料 1. [PageRank 演算法 python應用](https://blog.csdn.net/wuyu1876/article/details/129533652) 2. [Python實現PageRank計算](https://blog.csdn.net/duan_mo_ran/article/details/114702874) 3. [《統計學習方法》第21章 PageRank 演算法(教材全解 + 公式推導 + Python實現)](https://blog.csdn.net/Changxing_J/article/details/118970748) 4. [手把手圖文並茂教你掌握 PageRank 演算法](https://blog.csdn.net/weixin_40431584/article/details/105561398)\]

相關文章