PageRank算法实例详解 郝伟 2021/02/26 [TOC] PageRank(下称PR)算法是Google于1998/01/29提出的用于计算网页价值的一种算法,具有算法原理简单,可以离线计算等优点,能够有效地减少在线查询时间,极大降低了查询的响应时间。虽然PR也存在一些问题,比如不考虑主题的相关性,所以查询结果与主题的相关性较低;及存在时间长的页面通过等级比新页面要高,这是因为存在时间久的网页更容易被别的网页引用,所以新网页的价值容易被低估(人们往往会更喜欢关注新的网页)。但是通过相关算法的补充进行解决,PR仍然是非常有效的网页评估算法,比如截止2016年11月,PR仍然是Google搜索的核心算法[3]。

1.  研究目的

为漏洞知识图谱的搜索算法提供思路。

2. PageRank算法基本原理

PageRank本质上是一个马可夫模型,通过一个状态转移矩阵对节点的重要性进行不断迭代,以实现对节点重要性的准确评估。下面通过一个实例来理解其算法。

假设有A, B, ..., E 五个网络,其链接指向关系如下图所示:

graph TD
A --> B
A --> C
B --> D
C --> D
C --> E
D --> E
E --> A

我们可以建立节点转移矩阵

进一步,我们可以得到矩阵的概率转移矩阵,即对每行正则化,每项表示为: 比如 L(A) = 1, 最终整个矩阵可得:

所以

在实际应用中,有两点注意事项:

  1. 从一个节点到另一个节点的点击实际上都是会递减,即越向后点击的几率越低;
  2. 不应该有完全独立的节点,它不指向任何网页任何网页也不指向它。

基于这两点,算法对状态转移矩阵进行了修改,加上了一个系数0.85和一个最小值(1-0.85)/N,N为项数。在加上了两个量以后得:

由于V的初始化状态经过迭代后总会达到最终稳定状态(即迭代后不再变化),所以初始化值可以是固定值或随机值。以下取的是随机值,得到$V_0$如下所示:

其中,函数L(X)表示节点X出边的数量。

进一步抽象,一般表达式的每个节点的PR值可以表示为以下:

根据状态转移矩阵和$V0$,即可通过 $V{i+1} = P^TV_i$ 进行迭代计算,过程如下所示:

...

通过以上计算,最终得到了计算好的向量 其中第一行代表一个对应网页的PR值。

3. 实现代码

#encoding=utf-8
"""
创建日期:Wed Feb 24 06:48:58 2021
作者信息:郝伟老师pc
功能简介:
==== [[Python (programming language)|Python]] ====
PageRank algorithm with explicit number of iterations.

Returns
-------
ranking of nodes (pages) in the adjacency matrix

①x: 表示矩阵(也可以是一维)
②ord:范数类型
    向量的范数:
       ||x||= sum(x_i ^ 2)^0.5    
    矩阵的范数:
    ord=1:列和的最大值
    ord=2:|λE-ATA|=0,求特征值,然后求最大特征值得算术平方根
    ord=∞:行和的最大值
    ord=None:默认情况下,是求整体的矩阵元素平方和,再开根号。(注意.None不是求2范数)
③axis:处理类型
    axis=1表示按行向量处理,求多个行向量的范数
    axis=0表示按列向量处理,求多个列向量的范数
    axis=None表示矩阵范数。
④keepdims:是否保持矩阵的二维特性,避免出现shape = (5, )这样的形状
    True表示保持矩阵的二维特性,False相反

Ref: 
[1] PageRank algorithm, https://en.wikipedia.org/wiki/PageRank
[2] numpy.linalg.norm(), https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.linalg.norm.html
[3] PageRank算法, https://blog.csdn.net/hguisu/article/details/7996185
[4] The Anatomy of a Large-Scale Hypertextual Web Search Engine, http://infolab.stanford.edu/~backrub/google.html, 1998, Stanford
"""

import numpy as np

def show(title, obj):
    print((' ' + title + ' ').center(60, '*'))
    print(obj)

def pagerank(M, num_iterations: int = 100, d: float = 0.85):
    """ The trillion dollar algorithm.
    Parameters
    ----------
    M : numpy 矩阵
        使用邻接矩阵表示,其中 M_i,j 表示从页面 'j' to 'i', 
        并且对于所有的 'j' sum(i, M_i,j) = 1
    num_iterations : int, optional
        迭代次数, 默认值为 100
    d : float, optional
        损失因子,默认值为 0.85

    Returns
    -------
    numpy array
       一个表示PR的矢量,其中 v_i 是第 i个等级,范围 [0, 1],
       对 v 求和应等于1
    """
    N = M.shape[1] # N=5
    v = np.random.rand(N, 1) # such as [[0.59428688 0.91597221 0.82584001 0.69436602 0.79111371]]
    show('v', v)
    show('(np.linalg.norm(v))', (np.linalg.norm(v)))
    show('(np.linalg.norm(v, ord=1))', (np.linalg.norm(v, ord=1))) # max(sum(abs(x), axis=0))
    show('v.sum()', v.sum())
    v = v / np.linalg.norm(v, 1) 
    show('np.linalg.norm(v, 1)', np.linalg.norm(v, ord=1))
    matrix_trans = d * M + (1 - d) / N
    show('matrix_trans', matrix_trans)
    # 本示例中13次即可达到比较理想的值
    for i in range(num_iterations):
        v1 = matrix_trans @ v # @ equals to np.dot(matrix_trans, v)
        show('v i='+str(i), v)
        v=v1
    return v

# 概率转移矩阵
M = np.array([[  0, 0,   0, 0, 1],
              [0.5, 0,   0, 0, 0],
              [0.5, 0,   0, 0, 0],
              [  0, 1, 0.5, 0, 0],
              [  0, 0, 0.5, 1, 0]])
result = pagerank(M, 10, 0.85)
show('result', result.T)

# [[0.25419178 0.13803151 0.13803151 0.20599017 0.26375504]]
# [[0.43300166 0.23512905 0.23512905 0.35089288 0.44929214]]
def test():
    x = np.array([[0, 3, 4], [1, 6, 4]])
    #默认参数ord=None,axis=None,keepdims=False
    print("默认参数(矩阵整体元素平方和开根号,不保留矩阵二维特性):",np.linalg.norm(x))
    print("矩阵整体元素平方和开根号,保留矩阵二维特性:",np.linalg.norm(x,keepdims=True))
    print("矩阵每个行向量求向量的2范数:",np.linalg.norm(x,axis=1,keepdims=True))
    print("矩阵每个列向量求向量的2范数:",np.linalg.norm(x,axis=0,keepdims=True))
    print("矩阵1范数:",np.linalg.norm(x,ord=1,keepdims=True))
    print("矩阵2范数:",np.linalg.norm(x,ord=2,keepdims=True))
    print("矩阵∞范数:",np.linalg.norm(x,ord=np.inf,keepdims=True))
    print("矩阵每个行向量求向量的1范数:",np.linalg.norm(x,ord=1,axis=1,keepdims=True))

4. 参考资料

[1] The Anatomy of a Large-Scale Hypertextual Web Search Engine, Stanford University, http://infolab.stanford.edu/~backrub/google.html [2] PageRank算法简介, https://blog.csdn.net/hguisu/article/details/7996185 [3] The Google PageRank Algorithm, https://web.stanford.edu/class/cs54n/handouts/24-GooglePageRankAlgorithm.pdf [4] 谷歌背后的数学, https://www.changhai.org/articles/technology/misc/google_math.php [5] PageRank (III): Examples, http://pagerank.suchmaschinen-doktor.de/index/examples.html [6] Chapter 11: Markov Chains & PageRank, https://disco.ethz.ch/courses/fs16/ti2/lecture/chapter11.pdf [7] Google’s PageRank Explained, https://webworkshop.net/pagerank/

results matching ""

    No results matching ""