1. 幂迭代算法(简称幂法)

(1) 占优特征值和占优特征向量

已知方阵\(\bm{A} \in \R^{n \times n}\), \(\bm{A}\)的占优特征值是量级比\(\bm{A}\)所有其他特征值都大的特征值\(\lambda\),若这样的特征值存在,则与\(\lambda\)相关的特征向量我们称为占优特征向量。

(2) 特征值的性质

如果一个向量反复与同一个矩阵相乘,那么该向量会被推向该矩阵的主特征向量的方向。如下面这个例子所示:

  1. import numpy as np
  2. def prime_eigen(A, x, k):
  3. x_t = x.copy()
  4. for j in range(k):
  5. x_t = A.dot(x_t)
  6. return x_t
  7. if __name__ == '__main__':
  8. A = np.array(
  9. [
  10. [1, 3],
  11. [2, 2]
  12. ]
  13. )
  14. x = np.array([-5, 5])
  15. k = 4
  16. r = prime_eigen(A, x, k)
  17. print(r)
  18. # 250, 260

为什么会出现这种情况呢?因为对\(\forall \bm{x} \in \R^{n}\)都可以表示为\(A\)所有特征向量的线性组合(假设所有特征向量张成\(\R^n\)空间)。我们设\(\bm{x}^{(0)} = (-5, 5)^T\),则幂迭代的过程可以如下表示:

\[\begin{aligned}
& \bm{x}^{(1)} = \bm{A}\bm{x}^{(0)} = 4(1,1)^T - 2(-3, 2)^T\\
& \bm{x}^{(2)} = \bm{A}^2\bm{x}^{(0)} = 4^2(1, 1)^T + 2(-3, 2)^T\\
& ...\\
& \bm{x}^{(4)} = \bm{A}^4\bm{x}^{(0)} = 4^4(1, 1)^T + 2(-3, 2)^T = 256(1, 1)^T + 2(-3, 2)^T\\
\end{aligned} \tag{1}
\]

可以看出是和占优特征值对应的特征向量在多次计算后会占优。在这里4是最大的特征值,而计算就越来越接近特征向量\((1, 1)^T\)的方向。

不过这样重复与矩阵连乘和容易导致数值上溢,我们必须要在每步中对向量进行归一化。就这样,归一化和与矩阵\(\bm{A}\)的连乘构成了如下所示的幂迭代算法:

  1. import numpy as np
  2. def powerit(A, x, k):
  3. for j in range(k):
  4. # 每次迭代前先对上一轮的x进行归一化
  5. u = x/np.linalg.norm(x)
  6. # 计算本轮迭代未归一化的x
  7. x = A.dot(u)
  8. # 计算出本轮对应的特征值
  9. lam = u.dot(x)
  10. # 最后一次迭代得到的特征向量x需要归一化为u
  11. u = x / np.linalg.norm(x)
  12. return u, lam
  13. if __name__ == '__main__':
  14. A = np.array(
  15. [
  16. [1, 3],
  17. [2, 2]
  18. ]
  19. )
  20. x = np.array([-5, 5])
  21. k = 10
  22. # 返回占优特征值和对应的特征向量
  23. u, lam = powerit(A, x, k)
  24. print("占优的特征向量:\n", u)
  25. print("占优的特征值:\n", lam)

算法运行结果如下:

  1. 占优的特征向量:
  2. [0.70710341 0.70711015]
  3. 占优的特征值:
  4. 3.9999809247674625

观察上面的代码,我们在第\(t\)轮迭代的第一行,得到的是归一化后的第\(t-1\)轮迭代的特征向量近似值\(\bm{u}^{(t-1)}\)(想一想,为什么),然后按照\(\bm{x}^{(t)} = \bm{A}\bm{u}^{(t-1)}\)计算出第\(t\)轮迭代未归一化的特征向量近似值\(\bm{x}^{(t)}\),需要计算出第\(t\)轮迭代对应的特征值。这里我们我们直接直接运用了结论\(\lambda^{(t)} = (\bm{u}^{(t-1)})^T \bm{x^{(t)}}\)。该结论的推导如下:



证明


对于第\(t\)轮迭代,其中特征值的近似未\(\bm{\lambda}^{(t)}\),我们想解特征方程

\[\bm{x^{(t-1)}} \cdot \lambda^{(t)} = \bm{A}\bm{x}^{(t-1)}
\tag{2}
\]

以得到第\(t\)轮迭代时该特征向量对应的特征值\(\lambda^{(t)}\)。我们采用最小二乘法求方程\((2)\)的近似解。我们可以写出该最小二乘问题的正规方程为

\[(\bm{x}^{(t-1)})^T\bm{x}^{(t-1)} \cdot \lambda^{(t-1)} = (\bm{x}^{(t-1)})^T (\bm{A}\bm{x}^{(t-1)})
\tag{3}
\]

然而我们可以写出该最小二乘问题的解为

\[\lambda^{(t)} = \frac{(\bm{x}^{(t-1)})^T\bm{A}\bm{x}^{(t-1)}}{(\bm{x}^{(t-1)})^T\bm{x}^{(t-1)}}
\tag{4}
\]

式子\((4)\)就是瑞利(Rayleigh)商。给定特征向量的近似,瑞利商式特征值的最优近似。又由归一化的定义有

\[\bm{u}^{(t-1)} = \frac{\bm{x}^{(t-1)}}{||\bm{x}^{(t-1)}||} = \frac{\bm{x}^{(t-1)}}{{[(\bm{x}^{(t-1)})^T\bm{x}^{(t-1)}]}^{\frac{1}{2}}}
\tag{5}
\]

则我们可以将式\((4)\)写作:

\[\lambda^{(t)} = (\bm{u}^{(t-1)})^T\bm{A}\bm{u}^{(t-1)}
\tag{6}
\]

又因为前面已经计算出\(\bm{x}^{(t)} = \bm{A} \bm{u}^{(t-1)}\),为了避免重复计算,我们将\(\bm{x}^{(t)}\)代入式\((5)\)得到:

\[\lambda^{(t)} = (\bm{u}^{(t-1)})^T\bm{x}^{(t)}
\tag{7}
\]

证毕。


可以看出,幂迭代本质上每步进行归一化的不动点迭代。

2. 逆向幂迭代

上面我们的幂迭代算法用于求解(绝对值)最大的特征值。那么如何求最小的特征值呢?我们只需要将幂迭代用于矩阵的逆即可。

我们有结论,矩阵\(\bm{A}^{-1}\)的最大特征值就是矩阵\(\bm{A}\)的最小特征值的倒数。事实上,对矩阵\(\bm{A} \in \R^{n \times n}\) ,令其特征值表示为\(\lambda_1, \lambda_2, ..., \lambda_n\),如果其逆矩阵存在,则逆矩阵\(A\)的特征值为\(\lambda_1^{-1}, \lambda_2^{-1}, ..., \lambda_n^{-1}\), 特征向量和矩阵\(A\)相同。该定理证明如下:



证明


有特征值和特征向量定义有

\[\bm{A}\bm{v} = \lambda \bm{v}
\]

这蕴含着

\[\bm{v} = \lambda \bm{A}^{-1}\bm{v}
\]

因而

\[\bm{A}^{-1}\bm{v} = \lambda^{-1}\bm{v}
\]

得证。


对逆矩阵\(\bm{A}^{-1}\)使用幂迭代,并对所得到的的\(\bm{A}^{-1}\)的特征值求倒数,就能得到矩阵\(\bm{A}\)的最小特征值。幂迭代式子如下:

\[\bm{x}^{(t+1)} = \bm{A}^{-1}\bm{x}^{(t)}
\tag{8}
\]

但这要求我们对矩阵\(\bm{A}\)求逆,当矩阵\(\bm{A}\)过大时计算复杂度过高。于是我们需要稍作修改,对式\((8)\)的计算等价于

\[\bm{A}\bm{x}^{(t+1)} = \bm{x}^{(t)}
\tag{9}
\]

这样,我们就可以采用高斯消元对\(\bm{x}^{(t+1)}\)进行求解,

不过,我们现在这个算法用于找出矩阵最大和最小的特征值,如何找出其他特征值呢?

如果我们要找出矩阵\(\bm{A}\)在实数\(s\)附近的特征值,可以对矩阵做出接近特征值的移动。我们有定理:对于矩阵\(\bm{A} \in \R^{n \times n}\),设其特征值为\(\lambda_1, \lambda_2, ..., \lambda_n\),则其转移矩阵\(\bm{A}-sI\)的特征值为\(\lambda_1 -s, \lambda_2 -s, ..., \lambda_n -s\),而特征向量和矩阵\(A\)相同。该定理证明如下:



证明


有特征值和特征向量定义有

\[\bm{A}\bm{v} = \lambda \bm{v}
\]

从两侧减去\(sI\bm{v}\),得到

\[(\bm{A} - sI)\bm{v} = (\lambda - s)\bm{v}
\]

因而矩阵\(\bm{A} - sI\)的特征值为\(\lambda - s\),特征向量仍然为\(\bm{v}\),得证。


这样,我们想求矩阵\(\bm{A}\)在实数\(s\)附近的特征值,可以先对矩阵\((\bm{A}-sI)^{-1}\)使用幂迭代求出\((\bm{A}-sI)^{-1}\)的最大特征值\(b\)(因为我们知道转移后的特征值为\((\lambda - s)^{-1}\),要使\(\lambda\)尽可能接近\(s\),就得取最大的特征值),其中每一步的\(x^{(t)}\)可以对\((\bm{A}-sI)\bm{x}^{(t)}=\bm{u}^{(t-1)}\)进行高斯消元得到。最后,我们计算出\(\lambda = b^{-1} + s\)即为矩阵\(A\)在\(s\)附近的特征值。该算法的实现如下:

  1. import numpy as np
  2. def powerit(A, x, s, k):
  3. As = A-s*np.eye(A.shape[0])
  4. for j in range(k):
  5. # 为了让数据不失去控制
  6. # 每次迭代前先对x进行归一化
  7. u = x/np.linalg.norm(x)
  8. # 求解(A-sI)xj = uj-1
  9. x = np.linalg.solve(As, u)
  10. lam = u.dot(x)
  11. lam = 1/lam + s
  12. # 最后一次迭代得到的特征向量x需要归一化为u
  13. u = x / np.linalg.norm(x)
  14. return u, lam
  15. if __name__ == '__main__':
  16. A = np.array(
  17. [
  18. [1, 3],
  19. [2, 2]
  20. ]
  21. )
  22. x = np.array([-5, 5])
  23. k = 10
  24. # 逆向幂迭代的平移值,可以通过平移值收敛到不同的特征值
  25. s = 2
  26. # 返回占优特征值和对应的特征值
  27. u, lam = powerit(A, x, s, k)
  28. # u为 [0.70710341 0.70711015],指向特征向量[1, 1]的方向
  29. print("占优的特征向量:\n", u)
  30. print("占优的特征值:\n", lam)

算法运行结果如下:

  1. 占优的特征向量:
  2. [0.64221793 0.7665221 ]
  3. 占优的特征值:
  4. 4.145795530352381

3. 幂迭代的应用:PageRank算法

幂迭代的一大应用就是PageRank算法。PageRank算法作用在有向图上的迭代算法,收敛后可以给每个节点赋一个表示重要性程度的值,该值越大表示节点在图中显得越重要。

比如,给定以下有向图:



其邻接矩阵为:

\[\left(
\begin{matrix}
0 & 1 & 1 \\
0 & 0 & 1 \\
1 & 0 & 0 \\
\end{matrix}
\right)
\tag{8}
\]

我们将邻接矩阵沿着行归一化,就得到了马尔可夫概率转移矩阵\(\bm{M}\):

\[\left(
\begin{matrix}
0 & \frac{1}{2} & \frac{1}{2} \\
0 & 0 & 1 \\
1 & 0 & 0 \\
\end{matrix}
\tag{9}
\right)
\]

我们定义上网者从一个页面转移到另一个随机页面的概率是\(q\),停留在本页面的概率是\(1-q\)。设图的节点数为\(n\),然后我们可以计算Google矩阵做为有向图的一般转移矩阵。对矩阵每个元素而言,我们有:

\[\bm{G}_{ij} = \frac{q}{n} + (1-q)\bm{M}_{ij}
\tag{10}
\]

注意,Google矩阵每一列求和为1,这是一个随机矩阵,它满足一个性质,即最大特征为1.

我们采用矩阵表示形式,即:

\[\bm{G}_{ij} = \frac{q}{n}\bm{E} + (1-q)\bm{M}_{ij}
\tag{11}
\]

其中\(\bm{E}\)为元素全为1的\(n \times n\)方阵。

然后我们定义向量\(\bm{p}\),其元素\(\bm{p}_i\)是待在页面\(i\)上的概率。我们由前面的幂迭代算法知道,矩阵与向量重复相乘后向量会被推到特征值为1的方向。而这里,与特征值1对应的特征向量是一组页面的稳态概率,根据定义这就是\(i\)个页面的等级,即PageRank算法名字中的Rank的由来。(同时,这也是\(G^T\)定义的马尔科夫过程的稳态解)。故我们定义迭代过程:

\[\bm{p}_{t+1} = \bm{G}^T\bm{p}_{t}
\]

注意,每轮迭代后我们要对\(\bm{p}\)向量归一化(为了减少时间复杂度我们除以\(p\)向量所有维度元素中的最大值即可,以近似二范数归一化);而且,我们在所有轮次的迭代结束后也要对\(p\)向量进行归一化(除以所有维度元素之和以保证所有维度之和为1)。

我们对该图的PageRank算法代码实现如下(其中移动到一个随机页面的概率\(q\)按惯例取0.15):

  1. import numpy as np
  2. # 归一化同时迭代,k是迭代步数
  3. # 欲推往A特征值的方向,A肯定是方阵
  4. def PageRank(A, p, k, q):
  5. assert(A.shape[0]==A.shape[1])
  6. n = A.shape[0]
  7. M = A.copy().astype(np.float32) #注意要转为浮点型
  8. for i in range(n):
  9. M[i, :] = M[i, :]/np.sum(M[i, :])
  10. G = (q/n)*np.ones((n,n)) + (1-q)*M
  11. G_T = G.T
  12. p_t = p.copy()
  13. for i in range(k):
  14. y = G_T.dot(p_t)
  15. p_t = y/np.max(y)
  16. return p_t/np.sum(p_t)
  17. if __name__ == '__main__':
  18. A = np.array(
  19. [
  20. [0, 1, 1],
  21. [0, 0, 1],
  22. [1, 0, 0]
  23. ]
  24. )
  25. k = 20
  26. p = np.array([1, 1, 1])
  27. q = 0.15 #移动到一个随机页面概率通常为0.15
  28. # 概率为1-q移动到与本页面链接的页面
  29. R= PageRank(A, p, k, q)
  30. print(R)
  31. # [0.38779177 0.21480614 0.39740209]

可以看到20步迭代结束后网页的Rank向量\(\bm{R}=(0.38779177, 0.21480614, 0.39740209)^T\),这也可以看做网页的重要性程度。

知名程序库和源码阅读建议

PageRank算法有很多优秀的开源实现,这里推荐几个项目:

(1) Spark-GraphX

GraphX是一个优秀的分布式图计算库,从属于Spark分布式计算框架,采用Scala语言实现了很多分布式的图计算算法,也包括我们这里所讲的PageRank算法。

文档地址https://spark.apache.org/graphx

源码地址https://github.com/apache/spark

(2) neo4j

neo4j是一个采用Java实现的知名的图数据库,该数据库也提供了PageRank算法的实现。

文档地址https://neo4j.com/

源码地址https://github.com/neo4j/neo4j.git

(3) elastic-search

如果你有兴趣深入研究搜索引擎的实现,那么向你推荐elastic-search项目,它是基于Java实现的一个搜索引擎。

文档地址https://www.elastic.co/cn/

源码地址https://github.com/elastic/elasticsearch.git

参考文献

  • [1] Timothy sauer. 数值分析(第2版)[M].机械工业出版社, 2018.
  • [2] 李航. 统计学习方法(第2版)[M]. 清华大学出版社, 2019.

数值分析:幂迭代和PageRank算法的更多相关文章

  1. 数值分析:幂迭代和PageRank算法(Numpy实现)

    1. 幂迭代算法(简称幂法) (1) 占优特征值和占优特征向量 已知方阵\(\bm{A} \in \R^{n \times n}\), \(\bm{A}\)的占优特征值是比\(\bm{A}\)的其他特 ...

  2. 分布式机器学习:PageRank算法的并行化实现(PySpark)

    1. PageRank的两种串行迭代求解算法 我们在博客<数值分析:幂迭代和PageRank算法(Numpy实现)>算法中提到过用幂法求解PageRank. 给定有向图 我们可以写出其马尔 ...

  3. PageRank算法初探

    1. PageRank的由来和发展历史 0x1:源自搜索引擎的需求 Google早已成为全球最成功的互联网搜索引擎,在Google出现之前,曾出现过许多通用或专业领域搜索引擎.Google最终能击败所 ...

  4. PageRank算法--从原理到实现

    本文将介绍PageRank算法的相关内容,具体如下: 1.算法来源 2.算法原理 3.算法证明 4.PR值计算方法 4.1 幂迭代法 4.2 特征值法 4.3 代数法 5.算法实现 5.1 基于迭代法 ...

  5. PageRank算法原理与Python实现

    一.什么是pagerank PageRank的Page可是认为是网页,表示网页排名,也可以认为是Larry Page(google 产品经理),因为他是这个算法的发明者之一,还是google CEO( ...

  6. 张洋:浅析PageRank算法

    本文引自http://blog.jobbole.com/23286/ 很早就对Google的PageRank算法很感兴趣,但一直没有深究,只有个轮廓性的概念.前几天趁团队outing的机会,在动车上看 ...

  7. PageRank算法简介及Map-Reduce实现

    PageRank对网页排名的算法,曾是Google发家致富的法宝.以前虽然有实验过,但理解还是不透彻,这几天又看了一下,这里总结一下PageRank算法的基本原理. 一.什么是pagerank Pag ...

  8. [转]PageRank算法

    原文引自: 原文引自: http://blog.csdn.net/hguisu/article/details/7996185 感谢 1. PageRank算法概述 PageRank,即网页排名,又称 ...

  9. 关于pagerank算法的一点点总结

    1. PageRank算法每个顶点收敛的值与每个点的初值是没有关系的,每个点随便赋初值. 2.像q=0.8这样的阻尼系数已经解决了PageRank中处在的孤立点问题.黑洞效应问题. 3.当有那个点进行 ...

随机推荐

  1. 设置rem基准值

    <script type="text/javascript"> (function(){ var docEl = document.documentElement; f ...

  2. MFC中L, _T(),TEXT,_TEXT区别以及含义

    字符串前面加L表示该字符串是Unicode字符串. _T是一个宏,如果项目使用了Unicode字符集(定义了UNICODE宏),则自动在字符串前面加上L,否则字符串不变.因此,Visual C++里边 ...

  3. 如何实现 Android 短视频跨页面的流畅续播?

    在一切皆可视频化的今天,短视频内容作为移动端产品新的促活点,受到了越来越多的重视与投入,同时短视频也是增加用户粘性.增加用户停留时长的一把利器.那么如何快速实现移动端短视频功能呢?前两篇我们介绍了盒马 ...

  4. 常见GDB命令

  5. Git--生成公钥和私钥并添加gitlab访问权限

    Git配置 打开git bash 执行以下命令 git config --global user.name 用户名 git config --global user.email 邮箱 ssh-keyg ...

  6. Fastjson 1.2.22-24 反序列化漏洞分析(1)

    Fastjson 1.2.22-24 反序列化漏洞分析(1) 前言 FastJson是alibaba的一款开源JSON解析库,可用于将Java对象转换为其JSON表示形式,也可以用于将JSON字符串转 ...

  7. 深度探索-Redis复制

    1.前言 本文介绍了Redis复制的主要流程和设计思想.通过本文的阅读,您大致能理解复制在软件架构方面的通用思想.在阅读本文之前,希望读者首先对Redis有一定的认识,对Redis的事件类型.和事件处 ...

  8. supermvc 操作备要

    模板暂时仅支持smarty,需要指定模板文件   系统配置文件在supermvc文件夹下 sconfig.php 修改项目目录下的conf下的config.php 会覆盖系统默认配置   contro ...

  9. linux7可以通过远程和localhost访问mysql,但是127.0.0.1不能访问

    网上搜索的其他方法都试过,不行 比如设置权限,开放端口,配置数据库... 最好偶然一个搜索查看可能原因是防火墙端口问题: vim /etc/sysconfig/iptables 在文件中添加下面语句 ...

  10. PHP执行数据库定时备份 和手动还原

    一 备份数据库 我的这个是在TP5上,其实不在TP5也可以 逻辑: 1 首先在自己电脑的cmd命令上测试备份数据库,成功才能往下进行所以得到 C:/luanxiede/mysql-5.7/bin/my ...