初步理解pagerank算法
第一次写不是课程要求的博客,可能有不严谨的地方,如果有写错的希望能在评论区指出。
算法思想
pagerank算法用于网页排序,根据给网页的重要程度给各个网页打分,根据分数高低进行排序。而怎么判断网页的重要程度?该算法认为,若是对于某个网页A,引用(链接到)网页A的网页越多,重要度越强(即引用网页A的网页也被好多网页引用),那么网页A本身的重要度越强。
算法描述
下面说说如何量化。我们需要知道所有被排名网页的引用关系。计网页wi的重要度为PR(wi),采用如下方式计算最终的重要度。
初始化每个网页的PR值。
设定PR值在0到1之间(为了和概率挂上联系)。首先,等值初始化所有网页的PR值,即若网页总数为n,则所有网页网页wi的初始PR(wi)=1/n。
可以证明,所有随机初始化(不能全是0)得到的PR值向量在后续的迭代计算中会收敛到一个定值。
迭代得到最终的PR值
利用迭代法得到最终的PR值。为了利用到上面“算法思想”的想法,跟自然的想到,可以将所有引用的wi的网页的PR值加和表示wi的新的PR。但这样就会出现个问题:
1.若是某网页wk引用的网页数量多于wj引用的网页,而二者都引用了wi,那么wj对wi的PR的贡献度很有可能比wk要高。
因此,pagerank算法使用所有引用的wi的网页的PR值的加权求和表示wi的新的PR。各个引用wi的网页(使用wj表示)的“转移权值”使用1/n(wj),其中nj是网页wj的引用网页总数。,即PR的迭代公式如下:
P
R
(
w
i
)
=
Σ
w
j
∈
M
(
w
i
)
P
R
(
w
i
)
n
(
w
j
)
PR(w_i)=\Sigma_{w_j \in M(w_i)} \frac{ PR(w_i)}{n(w_j)}
PR(wi)=Σwj∈M(wi)n(wj)PR(wi)
其中M(wi)表示引用了网页wi的所有网页的集合。
后面的举例会更清楚的说明这些步。
矩阵形式描述:
计所有的PR值构成的向量为P。
根据每个网页的引用与被引用关系,可以得到个有向图G(这里用邻接矩阵表示,不知道邻接矩阵的请自行百度),其中节点数即为网页的总数,节点i指向节点j表示网页wj引用了网页wi。
Gij若不为0,则表示网页wj引用了网页wi,其值即上面讲的wj对wi的“转移权值”,所以,G的每一列的所有非0元素都相等,且都为1/n(wj)。G每一列的加和结果是1(除非wj一个其他网页都不引用,则该列所有元素为0)。
迭代计算的公式为:
P
n
e
w
=
G
P
o
l
d
\textbf{P}_{new} = \textbf{G}\textbf{P}_{old}
Pnew=GPold
举个例子
下图表示,总共4个网页需要排序。其中A、B互相引用;B引用了C;D引用了B和C。
首先,初始化PR值,即P=(0.25, 0.25, 0.25, 0.25)。
迭代法:
重复计算下式直到收敛(即P中各个元素不再变化或者在一个很小的阈值之内变化)
P
R
A
=
1
2
P
R
B
PR_A=\frac{1}{2}PR_B
PRA=21PRB
P
R
B
=
P
R
A
+
1
2
P
R
D
PR_B=PR_A+\frac{1}{2}PR_D
PRB=PRA+21PRD
P
R
C
=
1
2
P
R
B
+
1
2
P
R
D
PR_C=\frac{1}{2}PR_B+\frac{1}{2}PR_D
PRC=21PRB+21PRD
P
R
D
=
0
PR_D=0
PRD=0
代码:
(引用了numpy库,graph是我自己写的一个类的对象,graph包含一个字典,长度是图的节点数量,键是节点,值是与该节点邻接的所有节点与边上权值构成的小字典。代码写的比较烂,还没有运行对不对):
@staticmethod
def pagerank_v1(graph, d=0.85,threshold=1e-5, iteror=100):
'''
迭代
'''
i = 0
error = 2**32
num = len(graph.nodes)
PR = []
pr = []
for k in range(num):
PR.append(1/num)
pr.append(1/num)
PR = np.asarray(PR)
pr = np.asarray(pr)
while (i <= iteror) & (error > threshold):
for k in range(num):
pr[k] = Graph.add(graph.nodes[k], PR[k])
error = max(np.sqrt(PR - pr))
for k in range(num):
PR[k] = pr[k]
i += 1
return PR
@staticmethod
def add(node_edge, A):
result = 0
for i in node_edge:
result += A * node_edge[i]
return result
矩阵法:
构造图的邻接矩阵G,如下:
迭代计算
P
n
e
w
=
G
P
o
l
d
\textbf{P}_{new} = \textbf{G}\textbf{P}_{old}
Pnew=GPold
直到P收敛。
注意,若是P能够收敛(设收敛时为Pfinal),则有Pfinal=G Pfinal,即Pfinal是G特征值为1时候的特征向量,可以利用已有的计算矩阵特征值的工具进行计算。
代码:
import numpy as np
@staticmethod
def pagerank_v2(graph, d=0.85):
'''
特征值
'''
m1 = Graph.get_graph_from_matrix(graph)
m1 = np.matrix(m1)
eigenvalue,featurevector=np.linalg.eig(m1)
return featurevector[0]
几点说明:
可以证明,若G的所有列之和为1,G必有特征值为1的特征向量。简要说一下证明,G的转置左乘元素全是1的列向量,结果还是元素全是1的列向量,故G的转置有1特征值,故G有值为1的特征值。
特征值1是G的最大特征值。简要说一下证明,设r是G的特征值,x是G对应的特征向量,||G||为G的1范数,则||rx||=||Gx||。而|r|·||x||=||rx||,||G||·||x||>=||Gx||,故|r|<=||G||。而由于G的每列加和为1,故||G||=1。即|r|<=1。
算法的改进:
从上面给出的例子不难看出,PR(D)最终为0,从而其他的PR值也逐渐变成0。事实上,上述算法会产生以下问题:排名不唯一问题(即G特征值为1的特征向量不止一个)。
为了改进这些问题,加上一个平滑参数d(通常取0.85),将迭代公式修改为:
P
R
(
w
i
)
=
1
−
d
N
+
d
⋅
Σ
w
j
∈
M
(
w
i
)
P
R
(
w
i
)
n
(
w
j
)
PR(w_i)=\frac{1-d}{N} +d· \Sigma_{w_j \in M(w_i)} \frac{ PR(w_i)}{n(w_j)}
PR(wi)=N1−d+d⋅Σwj∈M(wi)n(wj)PR(wi)
矩阵形式修改为:
P
n
e
w
=
d
⋅
G
P
o
l
d
+
1
−
d
N
\textbf{P}_{new} =d· \textbf{G}\textbf{P}_{old} + \frac{\textbf1-\textbf d}{N}
Pnew=d⋅GPold+N1−d
其中,N为总节点数量。
此外,上面例子还有一个悬挂节点的问题,即有些网页不会引用其他网页,造成G某一列和不为1,从而使1可能不是G的特征值。但我们仍然可以使用该算法为每个网页打分。
算法证明:
可以参照下面其他博主的链接,写的非常详细
证明
证明
证明
或者论文
THE $25,000,000,000∗ EIGENVECTOR
THE LINEAR ALGEBRA BEHIND GOOGLE