跳转至

Page Rank Algorithm⚓︎

约 1117 个字 27 行代码 1 张图片 预计阅读时间 4 分钟

PageRank 是一种用于评估有向图中节点重要性的方法。也就是说,根据一个网络中边的连接关系,给出每一个节点的重要性得分。

常用于网页排序社交分析 等。比如搜索引擎。

算法思想

PageRank的核心思想是假设==如果一个网页被许多其他重要的网页链接,那么该网页也被视为重要的==。换句话说,PageRank通过分析网页之间的相互链接关系,判断每个网页的权重。

算法流程:

初始化:
为网络中的每个网页分配一个初始的PageRank值,通常设为相等。
迭代计算:
对于每个网页,计算其新的PageRank值,该值由所有链接到该网页的其他网页的PageRank值贡献。具体而言,一个网页的PageRank值被均等地分配给它链接到的所有网页。
引入阻尼因子(damping factor):
为模拟用户在浏览网页时可能随机跳转到其他页面的行为,引入阻尼因子 \(d\)(通常设为\(0.85\))。这意味着用户有\(85\%\)的概率通过链接继续浏览,有\(15\%\)的概率随机跳转到其他页面。
重复迭代:
不断重复上述计算过程,直到PageRank值收敛到一个范围内:相邻两次迭代的值变化在可接受范围内。

我们用 \(R_i\) 表示一个向量,表示第 \(i\) 次迭代时节点的权重,每一个元素代表一个节点的权重;用 \(M\) 表示转移矩阵。\(d\) 表示阻尼因子。因此我们得到了迭代方程:

\[R_{i+1} = d \cdot M R_i + \frac{1-d}{n} \cdot \textbf{1}\]

第一个问题就是,\(M\) 矩阵是如何计算的。

我们考虑这样一张图,用如下矩阵 \(A\) 表示每个节点之间的相连关系,也就是 \(x_{ij} = 1\) 表示有一条边从 \(i\) 流向 \(j\).

\[A = \begin{bmatrix} 
0 & 1 & 1 & 0 & 0 & 1  \\
1 & 0 & 1 & 1 & 0 & 1  \\
0 & 0 & 0 & 0 & 0 & 1  \\
1 & 1 & 0 & 0 & 0 & 1  \\
0 & 0 & 1 & 1 & 0 & 1  \\
1 & 1 & 1 & 0 & 0 & 0  
 \end{bmatrix}\]

\(i\) 的和表示节点 \(i\) 的出度。列 \(j\) 的和表示节点 \(j\) 的入度(有多少边指向它)。我们的 \(M\) 矩阵与 \(A\) 矩阵的大小是相同的。对于原先就不存在的边,\(M\) 中对应位置也是0,但是我们要把 \(A\) 中的 1 替换成这个边的权重。我们按照如下规则计算:

对于每一个节点,如果流出它的边数为 \(k\),那么流入它的每一个边的权重就是 \(1 / k\)

以节点 \(0\) 为例,看第一行:流出它的共有3条: \((0,1), (0,2), (0,5)\),那么流入节点 \(0\) 的所有边的权重都为 1/3。所以,我们 \(M\) 矩阵的第一列就是:

\[\begin{bmatrix} 0 \\ 1/3 \\ 0 \\ 1/3 \\ 0 \\ 1/3 \end{bmatrix}\]

同理,对于节点 \(1\),看第二行:流出它的共有4条: \((1,0), (1,2), (1,3), (1,5)\),那么流入节点 \(1\) 的所有边的权重都为 1/4。所以,我们 \(M\) 矩阵的第二列:

\[\begin{bmatrix} 0.25 \\ 0 \\ 0 \\ 0.25 \\ 0 \\ 0.25 \end{bmatrix}\]

以及最终的 \(M\) 矩阵:

\[\begin{bmatrix} 
 0         & 1/4 &       1 &          0 &         0 &           1/3 \\
 1/3 & 0 &         1 &           1/3 & 0 &           1/3 \\
 0         & 0 &         0 &          0 &         0 &           1/3 \\
  1/3 & 1/4 &       0 &          0 &         0 &           1/3 \\
 0         & 0 &         1 &           1/3 & 0 &           1/3 \\
  1/3 & 1/4 &       1 &          0 &         0 &          0         \\
 \end{bmatrix}\]

!!! example "主要核心"

- **随机游走模型:** 利用随机游走的概念,模拟用户在网络中的浏览行为。
- **阻尼因子:** 引入阻尼因子,考虑用户随机跳转的可能性,使模型更贴近实际情况。

> 阻尼因子也是为了避免悬挂节点(没有出度的节点)、非强连通图(存在无法相互访问的节点群)等特殊情况

为什么这种方式是靠谱的

这需要我们引入:稳态概率分布:马尔可夫链的收敛性。也就是说,假如一个用户在图中无限次随机跳转(点击链接),最终停留在每个节点的概率会趋于稳定,称为平稳分布

(感谢Deepseek提供的这个比喻)我们将网页视为房间,链接视为房间之间的门。如果每个房间的人数比例(R)满足“每扇门进出的人数相等”,则总人数比例不再变化。这时 MR=R 就是这种平衡状态的数学表达。

# 调包版
import networkx as nx 
import numpy as np
barabasi_graph = nx.barabasi_albert_graph(60,41) 
matrix = nx.to_numpy_array(barabasi_graph, dtype = np.int16)
pr = nx.pagerank(barabasi_graph,0.85) 
print(np.array( [v for k, v in pr.items()])[:10])
# 调包看看真实的结果
# NumPy手搓版
import numpy as np
def page_rank(matrix, d=0.85, tol=1e-6, max_iter=10000):
    # Page Rank in NumPy!
    out_degree = matrix.sum(axis=1) # 计算每个节点的出度
    weight = matrix / out_degree
    N = matrix.shape[0] 
    pr = np.ones(N).reshape(N, ) * 1.0 / N # 按照出度为每个边赋予权重
    for iter in range(max_iter):
        old_pr = pr.copy()
        pr = d * weight.dot(pr) + (1 - d)/ N # 迭代公式
        err = np.absolute(pr - old_pr).sum() # 满足误差,或者到达最大迭代次数
        if err < tol:
            return pr.T
    return pr.T

if __name__ == "__main__":
    res = page_rank(matrix)
    print(res[:10])