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
2.2 示例2
假设有4个网页,见下图。他们之间的链接信息如上图所示,A跳转到B、C、D的概率各位1/3,B跳转到A、D的概率为1/2,C跳转到A的概率为1,因此我们可以得到转移矩阵为M。
网络连接图 | 概率转移矩阵 |
---|---|
我们假设 \(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\)。
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值
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 迭代算法
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 幂法计算
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算法在未来会有更广泛的应用和更好的表现。