概述
PageRank的Page可是认为是网页,表示网页排名,也可以认为是Larry Page(google 产品经理),因为他是这个算法的发明者之一,还是google CEO。PageRank算法计算每一个网页的PageRank值,然后根据这个值的大小对网页的重要性进行排序。
算法思想
- 如果一个网页被很多其他网页链接到的话说明这个网页比较重要,也就是PageRank值会相对较高
- 如果一个PageRank值很高的网页链接到一个其他的网页,那么被链接到的网页的PageRank值会相应地因此而提高
- 模拟一个悠闲的上网者,上网者首先随机选择一个网页打开,然后在这个网页上呆了几分钟后,跳转到该网页所指向的链接,这样无所事事、漫无目的地在网页上跳来跳去,PageRank就是估计这个悠闲的上网者分布在各个网页上的概率。
Pagerank模型
互联网中的网页可以看出是一个有向图,其中网页是结点,如果网页A有链接到网页B,则存在一条有向边A->B,下面是一个简单的示例
根据上图,我们可以得到一个转移概率矩阵。例如用户当前在A网页,那么用户将各以三分之一的概率跳转到B,C,D网页。转移矩阵
M
M
如下,每一列的和为1
page | A | B | C | D |
---|---|---|---|---|
A | 0 | 1/2 | 1 | 0 |
B | 1/3 | 0 | 0 | 1/2 |
C | 1/3 | 0 | 0 | 1/2 |
D | 1/3 | 1/2 | 0 | 0 |
初始时,假设上网者在每个网页的概率是相等的,即, 于是初始概率分布就是所有值都为 1/n 1 / n 的nwei维向量 V0 V 0 ,可以通过如下方式进行迭代
V1=MV0=⎡⎣⎢⎢⎢⎢01/31/31/31/2001/2100001/21/20⎤⎦⎥⎥⎥⎥⎡⎣⎢⎢⎢⎢1/41/41/41/4⎤⎦⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢3/92/92/92/9⎤⎦⎥⎥⎥⎥ V 1 = M V 0 = [ 0 1 / 2 1 0 1 / 3 0 0 1 / 2 1 / 3 0 0 1 / 2 1 / 3 1 / 2 0 0 ] [ 1 / 4 1 / 4 1 / 4 1 / 4 ] = [ 3 / 9 2 / 9 2 / 9 2 / 9 ]
Vi=MVi−1
V
i
=
M
V
i
−
1
不断的迭代计算,直到
|Vi+1−Vi|<ϵ
|
V
i
+
1
−
V
i
|
<
ϵ
,
Vi
V
i
不再变化为止。最终会收敛到一个恒定值,得到各个网页访问的概率
上述上网者的行为是一个马尔科夫过程的实例,要满足收敛性,需要具备一个条件:图是强连通的,即从任意网页可以到达其他任意网页
常见问题
- 终止点问题:互联网上的网页不满足强连通的特性,因为有一些网页不指向任何网页,如果按照上面的计算,上网者到达这样的网页后便走投无路、四顾茫然,导致前面累计得到的转移概率被清零,这样下去,最终的得到的概率分布向量所有元素几乎都为0
- 陷阱问题:另外一个问题就是陷阱问题,即有些网页不存在指向其他网页的链接,但存在指向自己的链接
Pagerank模型的完善
假定上网者在每一步,都可能不想看当前网页了,而是在地址栏输入另外一个地址,而地址栏跳转到各个网页的概率是
1/n
1
/
n
。假设上网者每一步查看当前网页的概率是
α
α
,从地址栏跳转的概率是
1−α
1
−
α
。alpha一般取0.85,迭代公式如下:
Vi+1=αMVi+(1−α)e
V
i
+
1
=
α
M
V
i
+
(
1
−
α
)
e
重新计算第一步的迭代如下:
V1=αMV0+(1−α)e=0.85∗⎡⎣⎢⎢⎢⎢01/31/31/31/2001/2100001/21/20⎤⎦⎥⎥⎥⎥⎡⎣⎢⎢⎢⎢1/41/41/41/4⎤⎦⎥⎥⎥⎥+0.15∗⎡⎣⎢⎢⎢⎢1/41/41/41/4⎤⎦⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢9/6013/6025/6013/60⎤⎦⎥⎥⎥⎥
V
1
=
α
M
V
0
+
(
1
−
α
)
e
=
0.85
∗
[
0
1
/
2
1
0
1
/
3
0
0
1
/
2
1
/
3
0
0
1
/
2
1
/
3
1
/
2
0
0
]
[
1
/
4
1
/
4
1
/
4
1
/
4
]
+
0.15
∗
[
1
/
4
1
/
4
1
/
4
1
/
4
]
=
[
9
/
60
13
/
60
25
/
60
13
/
60
]
迭代计算直至 |Vi+1−Vi|<ϵ | V i + 1 − V i | < ϵ ,可以得到最终结果
代码实现
大规模稀疏矩阵运算时,在python中使用scipy.sparse_coomatrix比直接使用dense matrix快的多
# coding:utf-8
import pandas as pd
import numpy as np
def PageRank(M, alpha, root):
"""
Personal Rank in matrix formation
:param M: transfer probability matrix
:param index2node: index2node dictionary
:param node2index: node2index dictionary
:return:type of list of tuple, ex.
[(node1, prob1),(node2, prob2),...]
"""
result = []
n = len(M)
v = np.zeros(n)
v[node2index[root]] = 1
while np.sum(abs(v - (alpha*np.matmul(M,v) + (1-alpha)*v))) > 0.0001:
v = alpha * np.matmul(M, v) + (1-alpha)*v
for ind, prob in enumerate(v):
result.append((index2node[ind], prob))
result = sorted(result, key=lambda x:x[1], reverse=True)[:num_candidates]
return result
def Generate_Transfer_Matrix(G):
"""generate transfer matrix given graph"""
index2node = dict()
node2index = dict()
for index,node in enumerate(G.keys()):
node2index[node] = index
index2node[index] = node
# num of nodes
n = len(node2index)
# generate Transfer probability matrix M, shape of (n,n)
M = np.zeros([n,n])
for node1 in G.keys():
for node2 in G[node1]:
# FIXME: some nodes not in the Graphs.keys, may incur some errors
try:
M[node2index[node2],node2index[node1]] = 1/len(G[node1])
except:
continue
return M, node2index, index2node
if __name__ == '__main__':
alpha = 0.85
root = 'A'
num_iter = 100
num_candidates = 10
G = {'A' : {'a' : 1, 'c' : 1},
'B' : {'a' : 1, 'b' : 1, 'c':1, 'd':1},
'C' : {'c' : 1, 'd' : 1},
'a' : {'A' : 1, 'B' : 1},
'b' : {'B' : 1},
'c' : {'A' : 1, 'B' : 1, 'C':1},
'd' : {'B' : 1, 'C' : 1}}
M, node2index, index2node = Generate_Transfer_Matrix(G)
# print transfer matrix
print(pd.DataFrame(M, index=G.keys(), columns=G.keys()))
result = PageRank(M, alpha, root)
# print results
print(result)
reference
http://blog.csdn.net/gamer_gyt/article/details/47443877
https://baike.baidu.com/item/google%20pagerank/2465380?fr=aladdin&fromid=111004&fromtitle=pagerank
https://en.wikipedia.org/wiki/PageRank