PageRank算法解析及R语言实现

在PageRank算法出现之前,早期的搜索引擎是根据关键词出现次数对网页进行排序,但这样的算法有严重的缺陷,假设网站A像祥林嫂一样反复重复几个热门关键词,那么用户在搜索这些关键词的时候,网站A就会被排在前面,而这显然不是用户希望得到的结果,更严重的后果是网络上会充斥着广告和垃圾信息,而真正有用的信息却难以被用户搜索到。

于是, 1996 年初, 谷歌公司的创始人, 当时还是美国斯坦福大学 (Stanford University) 研究生的佩奇 (Larry Page) 和布林 (Sergey Brin) 开始了对网页排序问题的研究。他们从学术界判别论文重要性的通用方法,即考量引用次数得到了灵感,认为网页的重要性(也就是排序)是不能靠每个网页自己来标榜的,而应该通过全互联网所有网页间的相互链接来确定。具体地说,一个网页被其它网页链接得越多, 它的排序就应该越靠前。不仅如此,Larry和Sergey还进一步提出,一个网页越是被排序靠前的网页所链接,它的排序就也应该越靠前。这一条的意义也是不言而喻的,就好比一篇论文被诺贝尔奖得主所引用,显然要比被普通研究者所引用更说明其价值。

PageRank算法解析及R语言实现_第1张图片

思路虽然有了,实现却并非易事。网页 Wi 的排序 Pi 由所有其他的网页的排序 Pji 决定,而 Pi 又会对 Pji 产生影响,于是就面临了“先有鸡还是先有蛋”的难题。

Larry和Sergey的解决方法是这样的:假设用户A是互联网上所有用户的平均(即A不存在个人喜好),那么再假定经历足够多的网上漫游过后,A在n时刻处在页面 Wi 的概率为 Pi Wi 页面有 Ni 个外链,由于A没有个人喜好,因此点击每个外链的可能性均为 1/Ni

根据这个假设,我们可以得出: Pi(n+1)=jPj(n)pji/Nj

其中 pji 是一个描述互联网链接结构的指标函数 (indicator function),其定义是如果网页 Wj 有链接指向网页 Wi ,则 pji 取值为 1,反之则为 0

为符号简洁起见, 我们将虚拟用户第 n 次浏览时访问各网页的几率合并为一个列向量 Pn , 它的第 i 个分量为 Pi(n) ,并引进一个只与互联网结构有关的矩阵 H , 它的第 i 行 j 列的矩阵元为 Hij=pji/Nj , 则上述公式可以改写为:

Pn+1=HPn

Pn+1=P1(n+1)P2(n+1)Pj(n+1) H=0p12/N1p1j/N1p21/N20p2j/N2pj1/Njpj2/Nj0 Pn=P1(n)P2(n)Pj(n)

至此,我们将问题建模为一个平稳马尔科夫链1,H 就是转移矩阵。于是我们有了求解 Pn 的公式:

Pn=HnP0


可是,万里长征才走了一半,我们需要计算的是经过无数用户无数次的网页浏览后,用户停留在每个网页的概率,即 limnPn

那么,首先我们要解决三个问题:

  1. 极限 limnPn 是否存在?
  2. 如果极限存在,它是否与 P0 的选取无关?
  3. 如果极限存在,并且与 P0 的选取无关,它作为网页排序的依据是否真的合理?

非常遗憾的是,就目前来看,这三个问题都无法解决:

  1. 首先,假设互联网仅有两个页面且互相链接, P0=[1,0]T ,那么很不幸, Pn 将在 [1,0]T [0,1]T 之间无穷震荡,无法稳定。
  2. 其次,若互联网上存在多个互不连通的区域,那么 Pn 的分布就和 P0 的选取有关。
  3. 另外,互联网上还有很多的网页没有任何的外链,即所谓的”悬停网页()“,体现在 H 中即有某些列向量矩阵元之和为0,它们只进不出,所以就会像黑洞一样,使得所有网页的P值向0趋近。严重的是,哪怕只有一个悬停网页,也会使得整个排序算法失效。

为了解决以上的问题,需要对用户A的行为进行修正。现实生活中的真实情况是,当A访问到悬停页面时,并不会就此停止,鉴于A是平均用户,合理的行为是随机跳转到互联网上任一网页。于是我们需要把 H 中为0的列向量(悬停页面对应的列向量)换成 e/N (e是所有分量均为 1 的列向量,N为互联网中所有页面的总数);为此,我们引入一个描述“悬挂网页”的指标向量 (indicator vector) a , 它的第 i 个分量的取值视 Wi 是否为”悬停网页”而定——如果是“悬挂网页“,取值为 1,否则为 0

用S表示修正后的转移矩阵: S=H+eaT/N ;这一步称为随机性修正(stochasticity adjustment)

现在我们解决了悬停网页的麻烦,可是其他问题怎么办呢?于是引入了另一个修正,用户并不会受限于当前网页提供的链接,他们也有一定概率跳出来,随机选择一个网页;假定跳转到当前页面提供的链接的概率为阻尼系数 α ,则跳出的概率为 1α

再次修正: G=αS+(1α)eeT/N

经过这一步修正,G成为了一个素矩阵2,因此也被称为素修正(primitivity adjustment)

结合马尔可夫链基本定理 (fundamental theorem of Markov chains),在一个马尔可夫过程中,如果转移矩阵是素矩阵, 那么极限收敛且与初始状态无关。

至此,之前的三个问题都得到了解决!

经过两次修正,最后我们得到了: Pn=GnP0

细心的你也许发现了,我们还留了一个小问题,即 α 的取值:按理 α 应该由众多的实际数据统计得出,但还有一个我们不得不考虑的问题制约着 α 的取值,即 Pn 收敛的速度, α 值越小, Pn 收敛越快,即计算速度越快,但矩阵S,即算法中最核心的网页间相互链接的信息,对 Pn 的影响也就越小,这显然是我们不希望看到的。因此,折中考虑之后,Larry和Sergey最终选择的是 α=0.85


具体的编码实现有多种方式,最简单稳定的是如下的迭代法:

R = GX; //G为转移矩阵,R,X为列向量
ITER_LIMIT = 1000;
THRESHOLD = xxx; //THRESHOLD为列向量,每个分量为阙值e
count = 0;

while (true) {
  if (abs(X-R) < THRESHOLD) { //如果最后两果近似或者相同,即达到稳定状态,返回R
    return R;
  } else if (count > ITER_LIMIT) { //如果达到迭代上限,即使未完全稳定,也返回R
    return R;
  } else {
    X =R;
    R = GX;
  }
}

R语言中的igraph包也对pagerank作了实现,具体使用如下:

page.rank (graph, algo = c("prpack", "arpack", "power"), 
vids = V(graph), directed = TRUE, damping = 0.85, 
personalized = NULL, weights = NULL, options = NULL)
Option Description
graph graph对象
algo 具体算法实现,默认prpack
vids 顶点数
directed 是否有向图,true/false
damping 阻尼系数 α
personalized 用户自定义页面间跳转概率,而不是平均分布,需保证列向量之和为1
weights 为页面间的链接分配权重
options 覆盖power/arpack的算法参数,对prpack算法无效
Return Value Description
vector PageRank值的向量
value 特征向量的特征值,永远为1
options arpack算法的参数,若采用其他算法,则为NULL
g <- random.graph.game(20, 5/20, directed=TRUE)

PageRank算法解析及R语言实现_第2张图片

page.rank(g)$vector

[1] 0.06174378 0.04634210 0.06182139 0.02905230 
[5] 0.03941021 0.05210103 0.07246689 0.06876744 
[9] 0.01526981 0.04776690 0.01775830 0.05296421 
[13] 0.06191911 0.06084280 0.04115572 0.05433190 
[17] 0.04165332 0.04229852 0.06537069 0.06696358

参考文献:谷歌背后的数学 http://www.changhai.org/articles/technology/misc/google_math.php


  1. 马尔可夫链 (Markov chain),是一类离散随机过程,它的最大特点是每一步的转移概率分布都只与前一步有关。而平稳马尔可夫过程则是指转移概率分布与步数无关的马尔可夫过程 (体现在我们的例子中,即 H 与 n 无关)。 ↩
  2. 确切地说,这种所有矩阵元都为正的矩阵不仅是素矩阵,而且还是所谓的正矩阵 (positive matrix)。这两者的区别是:正矩阵要求所有矩阵元都为正,而素矩阵只要求自己的某个正整数次幂为正矩阵。 ↩

你可能感兴趣的:(大数据算法)