Google早已成为全球最成功的互联网搜索引擎,在Google出现以前,曾出现过许多通用或专业领域搜索引擎。Google最终能击败全部竞争对手,很大程度上是由于它解决了困扰前辈们的最大难题:对搜索结果按重要性排序。而解决这个问题的算法就是PageRank。绝不夸张的说,是PageRank算法成就了Google今天的地位。html
从本质上说,搜索引擎是一个资料检索系统,搜索引擎拥有一个资料库(具体到这里就是互联网页面),用户提交一个检索条件(例如关键词),搜索引擎返回符合查询条件的资料列表。node
理论上检索条件能够很是复杂,为了简单起见,咱们不妨设检索条件是一至多个以空格分隔的词,而其表达的语义是同时含有这些词的资料(等价于布尔代数的逻辑与)。例如,提交“littlehann 博客”,意思就是“给我既含有‘littlehann’又含有‘博客’词语的页面”,如下是Google对这条关键词的搜索结果:python
固然,实际上如今的搜索引擎都是有分词机制的,例如若是以“littlehann的博客”为关键词,搜索引擎会自动将其分解为“littlehann 的 博客”三个词,而“的”做为中止词(Stop Word)会被过滤掉。git
创建一个搜索引擎的核心问题就是如下几个:github
1. 创建资料库; 2. 创建一种数据结构,根据关键词找到含有这个词的页面; 3. 将结果按照重要程度排序后呈现给用户;
这个问题通常是经过一种叫爬虫(Spider)的特殊程序实现的(专业领域搜索引擎例如某个学术会议的论文检索系统可能直接从数据库创建资料库)。web
简单来讲,爬虫就是从一个页面出发(例如新浪首页),经过HTTP协议通讯获取这个页面的全部内容,把这个页面url和内容记录下来(记录到资料库),而后分析页面中的连接,再去分别获取这些连接链向页面的内容,记录到资料库后再分析这个页面的连接。算法
上述过程不断重复,就能够将整个互联网的页面所有获取下来(固然这是理想状况,要求整个Web是一个强连通(Strongly Connected),而且全部页面的robots协议容许爬虫抓取页面,为了简单,咱们仍然假设Web是一个强连通图,且不考虑robots协议)。sql
抽象来看,能够将资料库看作一个巨大的key-value结构,key是页面url,value是页面内容。数据库
这个问题是经过一种叫倒排索引(inverted index)的数据结构实现的。安全
抽象来讲倒排索引也是一组key-value结构,key是关键词,value是一个页面编号集合(假设资料库中每一个页面有惟一编号),表示这些页面含有这个关键词。
搜索引擎获取“littlehann 博客”查询条件,将其分为“littlehann”和“博客”两个词。
而后分别从倒排索引中找到“littlehann”所对应的集合,假设是{1, 3, 6, 8, 11, 15};
“博客”对应的集合是{1, 6, 10, 11, 12, 17, 20, 22},
将两个集合作交运算(intersection),结果是{1, 6, 11}。即寻找同时出现了这2个词的页面。
最后,从资料库中找出一、六、11对应的页面返回给用户就能够了。
上面两个问题解决后,咱们很天然会想到,Web页面数量很是巨大,因此一个检索的结果条目数量也很是多,例如上面“littlehann 博客”的检索返回了上万条条结果。用户不可能从如此众多的结果中一一查找对本身有用的信息。
因此,一个好的搜索引擎必须想办法将“质量”较高的页面排在前面。
其实直观上也能够感受出,在使用搜索引擎时,咱们并不太关心页面是否够全(上百万的结果,全不全有什么区别?并且实际上搜索引擎都是取top,并不会真的返回所有结果。),而很关心前一两页是否都是质量较高的页面,是否能知足咱们的实际需求。
所以,对搜索结果按重要性合理的排序就成为搜索引擎的最大核心问题。
1. 不评价 早期的搜索引擎直接按照某天然顺序(例如时间顺序或编号顺序)返回结果。这在结果集比较少的状况下还说得过去,可是一旦结果集变大,用户叫苦连天,试想让你从几万条质量良莠不齐的页面中寻找须要的内容,简直就是一场灾难,这也注定这种方法不可能用于现代的通用搜索引擎。 2. 基于检索词的评价 后来,一些搜索引擎引入了基于检索关键词去评价搜索结构重要性的方法,实际上,这类方法如TF-IDF算法在现代搜索引擎中仍在使用。
早期一些搜索引擎基于相似的算法评价网页重要性的。这种评价算法看似依据充分、实现直观简单,但却很是容易受到一种叫“Term Spam”的攻击。
其实从搜索引擎出现的那天起,spammer和搜索引擎反做弊的斗法就没有中止过。Spammer是这样一群人——试图经过搜索引擎算法的漏洞来提升目标页面(一般是一些广告页面、博彩或垃圾页面)的重要性,使目标页面在搜索结果中排名靠前。
如今假设Google单纯使用关键词占比评价页面重要性,而我想让个人博客在搜索结果中排名更靠前(最好排第一)。
那么我能够这么作:在页面中加入一个隐藏的html元素(例如一个div),而后其内容是“littlehann”重复一万次。这样,搜索引擎在计算“littlehann 博客”的搜索结果时,个人博客关键词占比就会很是大(TF-IDF的公式决定了),从而作到排名靠前的效果。
更进一步,我甚至能够干扰别的关键词搜索结果,例如我知道如今欧洲杯很火热,我就在我博客的隐藏div里加一万个“欧洲杯”,当有用户搜索欧洲杯时,个人博客就能出如今搜索结果较靠前的位置。这种行为就叫作“Term Spam”。
早期搜索引擎深受这种做弊方法的困扰,加之基于关键词的评价算法自己也不甚合理,所以常常是搜出一堆质量低下的结果,用户体验大大打了折扣。而Google正是在这种背景下,提出了PageRank算法,并申请了专利保护。此举充分保护了当时相对弱小Google,也使得Google一举成为全球数一数二的搜索引擎。
Relevant Link:
http://blog.codinglabs.org/articles/intro-to-pagerank.html
1. 每个一个网页自己具备必定的重要性,它的重要性是经过其余网络的连接到该网页来评价的。其余网页连接到该网页能够形象地理解为给这个网页投票。 2. 一个网页的连接会把该网页的重要性传递到连接的网页中,而一个网页的重要性又必须经过连接它的网页来肯定。这是一个互相依赖的递归过程。 3. 公平起见,一个网页X若连接了m个网页,那么这m个网页的每一个网页接收到的来自网页X的重要性是PR(X)/m。
PageRank算法的目标就是计算每个网页的PageRank值,而后根据这个值的大小对网页的重要性进行排序。
它的思想是模拟一个清闲的上网者,上网者首先随机选择一个网页打开,而后在这个网页上呆了几分钟后,跳转到该网页所指向的连接,这样无所事事、漫无目的地在网页上跳来跳去,PageRank就是估计这个清闲的上网者分布在各个网页上的几率。
在这个小节咱们以一个清闲上网者的视角来讨论PageRank的算法过程,以便创建起一个感性的概念性认识,方便咱们记忆和拦截核心概念。
互联网中的WWW网页能够看出是一个有向图,其中网页是结点。若是网页A有连接到网页B,则存在一条有向边A->B。下面是一个简单的示例:
这个例子中只有四个网页。分别是A、B、C、D。这4个网页分别拥有各自不一样的“跳转选择选项”,清闲上网者在每一个网页中,能够往哪个网页去进行下一跳,是由这个选项规定的。
若是当前在A网页,那么清闲的上网者将会各以1/3的几率跳转到B、C、D,这里的3表示A有3条出链。若是一个网页有k条出链,那么跳转任意一个出链上的几率是1/k;
同理D到B、C的几率各为1/2;
而B到C的几率为0。
通常用转移矩阵表示上网者的跳转几率(注意,这个跳转几率是在创建网络图的时候就肯定好的,后面不会再改变)。
若是用n表示网页的数目,则转移矩阵M是一个n*n的方阵(每个网页均可能转移到任意的网页,包括它本身)。
若是网页j 有 k 个出链,那么对每个出链指向的网页i,有M[i][j]=1/k(权重是等分的),而其余网页的M[i][j]=0(没有出链就意味着不给那个网页投票);
上面示例图对应的转移矩阵的转置以下(注意,下面的矩阵是列向量的形式):
好了,如今咱们已经获得了全部网页的转移矩阵,也即肯定了全部网页各自的“跳转选择选项”。接下来要让咱们的清闲上网者开始在网页上不断游走,但愿这个上网者经过不断地游走,给出一个最终的评估,对A、B、C、D这4个网页的重要性权重给出一个数值结果。
根据最大熵原则,清闲上网者对这4个网页的权重没有任何先验知识,因此假设每个网页的几率都是相等的,即1/n。
因而初试的几率分布就是一个全部值都为1/n的n维列向量V0,用V0去右乘转移矩阵M,就获得了第一步以后上网者的几率分布向量MV0。n x n)* (n x 1)依然获得一个n x 1的矩阵。
M的第一行乘以 V0,表示累加全部网页到网页A的几率即获得9/24;
M的第二行乘以 V0,表示累加全部网页到网页B的几率即获得9/24;
M的第三行乘以 V0,表示累加全部网页到网页C的几率即获得9/24;
M的第四行乘以 V0,表示累加全部网页到网页D的几率即获得9/24;
这一轮结束后,上网者对各个网页的权重值获得了一次调整,从思想上很相似EM优化过程。
能够把矩阵M和向量r相乘当作M的列以向量r为权重进行线性组合,矩阵M同一列的不一样行表明该节点向其余节点的分发链接。
获得了V1后,再用V1去右乘M获得V2,一直下去,最终V会收敛,
即Vn=M * V(n-1)。
不断的迭代,最终V = [3/9,2/9,2/9,2/9]'
这个[3/9,2/9,2/9,2/9]'就表明了上网者对这4个网页权重的最终评价。显然,这个权重评价是根据 M矩阵 的拟合而来的。
直观上能够这么理解:这个清闲上网者看到转移矩阵M,他在想,这个M矩阵就表明了当前整个网络的拓朴结构,那么这个拓朴结构背后必定隐含了某种规律,这个规律就是每一个网页的权重。这个规则“支撑”着网络成为今天我看到的样本。那我要努力去游走,让个人评价无限接近网络背后的真实规律。恩,加油,我必定行的!
笔者思考:这种渐进收敛的思路,本质上体现了极大似然估计的思想,即从结果反推最有可能产生这个结果的模型参数。笔者建议读者朋友翻出极大似然估计的书籍参照着学习,笔者也有一篇blog讨论了极大似然估计的话题。
如今咱们从马尔科夫过程的角度来看PageRank的训练和收敛过程。关于markvo的讨论,能够参阅另外一篇blog。
假设咱们在上网的时候浏览页面并选择下一个页面,这个过程与过去浏览过哪些页面无关,而仅依赖于当前所在的页面。这个假设前提符合马尔科夫的有限状态依赖假设。
咱们能够把PageRank的这一选择过程能够认为是一个有限状态、离散时间的随机过程,其状态转移规律可用Markov链描述。
在PageRank算法中,网页拓朴间互相连接的邻接矩阵,就对应了几率转移矩阵。
能够想象,在一个庞大的网络中,邻接矩阵是一个十分庞大有至关稀疏的方阵(用黑色表明1, 用白色表明0)。例以下图:
矩阵中的的空行表明了没有被其余网页连接过,可能表明是新网页(例如新的新闻html页面),或者是异常的恶意url。
定义矩阵G的“列和”与“行和”,在PageRank场景下,几率转移矩阵的“行和”和“列和”是有明确含义的。
1. cj(列和) 是页面j 的导出连接数目。也就是该页面给其余页面的“投票”。固然,在PageRank中,列和是有明确约束的,即一个页面能给其余页面投票的总权重和是1,不能超过1。 2. ri(行和) 是页面 i 的导入连接数目。也就是该页面收到的权重投票。
在讨论马尔科夫收敛问题前,咱们要对PageRank的迭代公式进行一个明肯定义。可是,在讨论PageRank公式以前还要先讨论两个在实际中会遇到的问题:
,即Spider Traps问题(自循环节点),由于这个问题的存在,致使PageRank的迭代公式须要做出一些变形。
能够预见,若是把真实的Web组织成转移矩阵,那么这将是一个极为稀疏的矩阵。
从矩阵论知识能够推断,极度稀疏的转移矩阵迭代相乘可能会使得向量v变得很是不平滑,即一些节点拥有很大的rank,而大多数节点rank值接近0。
而一种叫作Spider Traps节点的存在加重了这种不平滑。例以下图:
D有外链因此不是Dead Ends,可是它只链向本身(注意链向本身也算外链,固然同时也是个内链)。这种节点叫作Spider Trap。
若是对这个图进行计算,会发现D的rank愈来愈大趋近于1(由于每轮迭代它都只给本身投票),而其它节点rank值几乎归零。
所谓Dead Ends,就是这样一类节点:它们不存在外链。看下面的图:
注意这里D页面不存在外链,是一个Dead End。
在这个图中,M第四列(D对应的那列)将全为0。在没有Dead Ends的状况下,每次迭代后向量v各项的和始终保持为1,而有了Dead Ends,迭代结果将最终归零。
为了克服这种因为矩阵稀疏性、Spider Traps、以及Dead Ends带来的问题,须要对PageRank计算方法进行一个平滑处理,具体作法是加入“随机转移几率”。
所谓随机转移,就是咱们认为在任何一个页面浏览的用户都有可能以一个极小的几率瞬间转移到另一个随机页面。
固然,这两个页面可能不存在超连接,随机转移只是为了算法须要而强加的一种纯数学意义的几率数字。
笔者思考:你们仔细体会这种作法的思想,它本质上就是一个结构化风险最小化思想。和在机器学习算法中加入正则项、惩罚项、剪枝;在深度学习中 Dropout 的核心思想都是一致的。咱们能够这么来理解,加入了随机转移几率后,每一个节点向其余节点转移的几率是否是更加倾向于“均等化”了,这就等于削弱了本来的网络结构的先验特性。
加入随机几率转移后,向量迭代公式变为:
其中 β 每每被设置为一个比较小的参数(0.2或更小),它的做用就是在本来模型基础上加入惩罚因子;
e为N维单位向量,加入e的缘由是这个公式的前半部分是向量,所以必须将β/N转为向量才能相加。
通过随机转移几率的修正后,整个计算就变得平滑,由于每次迭代的结果除了依赖转移矩阵外,还依赖一个小几率的随机几率转移。
以该图为例:
原始转移矩阵M为:
设β为0.2,则计算公式为:
若是按这个公式迭代算下去,会发现Spider Traps的效应被抑制了,从而每一个页面都拥有一个合理的pagerank。
同时,即便是出现了Dead Ends,由于随机几率矩阵的存在,实际的M 也所以不存在为0的行了。
问题获得了完美的解决。
首先给每一个页面赋予随机的PR值,而后经过不断地迭代PR值。当知足下面的不等式后迭代结束,得到全部页面的PR值:
# -*- coding: utf-8 -*- from pygraph.classes.digraph import digraph class PRIterator: __doc__ = '''计算一张图中的PR值''' def __init__(self, dg): self.damping_factor = 0.85 # 阻尼系数,即α self.max_iterations = 100 # 最大迭代次数 self.min_delta = 0.00001 # 肯定迭代是否结束的参数,即ϵ self.graph = dg def page_rank(self): # 先将图中没有出链的节点改成对全部节点都有出链 for node in self.graph.nodes(): if len(self.graph.neighbors(node)) == 0: for node2 in self.graph.nodes(): digraph.add_edge(self.graph, (node, node2)) nodes = self.graph.nodes() graph_size = len(nodes) if graph_size == 0: return {} # 给每一个节点赋予初始的PR值,第一轮的PR值是均等的,即 1/N page_rank = dict.fromkeys(nodes, 1.0 / graph_size) # 公式中的(1−α)/N部分 damping_value = (1.0 - self.damping_factor) / graph_size flag = False for i in range(self.max_iterations): change = 0 for node in nodes: rank = 0 # 遍历全部“入射”的页面 for incident_page in self.graph.incidents(node): # "入射"页面的权重根据其出链个数均分,而后传递给当前页面 rank += self.damping_factor * (page_rank[incident_page] / len(self.graph.neighbors(incident_page))) # 增长随机几率转移矩阵的部分 rank += damping_value change += abs(page_rank[node] - rank) # 绝对值 page_rank[node] = rank print("This is NO.%s iteration" % (i + 1)) print(page_rank) if change < self.min_delta: flag = True break if flag: print("finished in %s iterations!" % node) else: print("finished out of 100 iterations!") return page_rank if __name__ == '__main__': # 建立一个网络拓朴图 dg = digraph() dg.add_nodes(["A", "B", "C", "D", "E"]) dg.add_edge(("A", "B")) dg.add_edge(("A", "C")) dg.add_edge(("A", "D")) dg.add_edge(("B", "D")) dg.add_edge(("C", "E")) dg.add_edge(("D", "E")) dg.add_edge(("B", "E")) dg.add_edge(("E", "A")) # PRrank迭代计算 pr = PRIterator(dg) page_ranks = pr.page_rank() print("The final page rank is\n", page_ranks)
从结果上能够看出两个比较明显的规律:
1. E节点的权重是最高的,由于E的入链最多,这很显然; 2. A节点的权重次之,也很高,由于高权重E节点存在向A节点的入链;
咱们知道,当Markov链收敛时,必有:
相似地,当提到Markov链收敛时,必有:
Relevant Link:
http://www.cnblogs.com/fengfenggirl/p/pagerank-introduction.html https://www.letiantian.me/2014-06-10-pagerank/ https://wizardforcel.gitbooks.io/dm-algo-top10/content/pagerank.html https://blog.csdn.net/cannel_2020/article/details/7672042 https://blog.csdn.net/Young_Gy/article/details/70169649?utm_source=blogxgwz2 http://blog.codinglabs.org/articles/intro-to-pagerank.html https://blog.csdn.net/golden1314521/article/details/41597605 https://blog.csdn.net/rubinorth/article/details/52215036 https://blog.csdn.net/leadai/article/details/81230557
设 A = (aij) 是一个 n x n 的正矩阵:,该矩阵有如下几个性质:
1. A 存在一个正实数的特征值,叫作 Perron根 或者 Perron - Frobenius特征值,使得其余全部特征值(包括复数特征值)的规模都比它小;
2. 只对应一个特征向量 v;
3. 所对应的特征向量 v 的全部元素都为正实数;
4. 之外的其余特征值所对应的特征向量的元素至少有一个为负数或者复数;
5.
6.
每一个矩阵元都大于0的矩阵称之为正矩阵;
每一个矩阵元都大于等于0的矩阵是非负矩阵(Nonnegative matrix)
素阵是指自身的某个次幂为正矩阵(Positive matrix)的矩阵。设 A 为一个 n x n 的方阵,若是存在正整数 k 使得矩阵知足:
那么,称矩阵 A 为素矩阵。
随机矩阵又叫作几率矩阵(probability matrix)、转移矩阵(transition matrix)、马尔科夫矩阵(markov matrix)等。
随机矩阵一般表示左随机矩阵(left stochastic matrix)。
若是方阵为左随机矩阵,则其知足如下条件:
即“列和”为1
方阵A 是不可约的,当且仅当与矩阵A 对应的有向图是强连通的。
有向图 G = (V,E) 是强连通的当且仅当对每一节点对,存在 u 到 v 的路径(不必定是直接相连)。
说状态 i 是周期的,而且具备周期 k > 1,是指存在一个最小的正整数 k,使得从某状态 i 出发又回到状态 i 的全部路径的长度都是 k 的整数倍。
若是一个状态不是周期的或者 k = 1,那它就是非周期的。
若是一个马尔柯夫链的全部状态都是非周期的,那么就说这个马尔柯夫链是非周期的。
下图所示,从状态1 出发回到状态1 的路径只有一条,即 1-2-3-1,须要的转移次数是3,因此这是一个周期为3 的马尔柯夫链。
咱们从排序声望(rank prestige)的角度进一步阐述PageRank的思想:
1. 从一个网页指向另外一个网页的超连接是PageRank值的隐含式传递,网页的PageRank值是由指向它的全部的网页所传递过来的PageRank值总和决定的。这样,网页 i 的入链越多,它的PageRank值就越高,它获得的声望就越高。 2. 一个网页指向多个其余网页,那么它传递的声望值就会被它所指向的多个网页分享。也就是说,即便网页 i 被一个PageRank值很高的网页 j 所指向,可是若是网页 i 的出链很是多,网页 i 从网页 j 获得的声望值可能所以被稀释地也很小
咱们能够把web网络抽象成一个有向图 G = (V,E),其中 V 是图的节点集合(一个节点对应一个网页),E 是图的有向边集合(有向边对应超连接)。
设web上的网页总数为 n,即 n = | V |。上述四项能够形式化为:
,i = 1,2....,n
其中 P(i) 表示网页 i 的PageRank值,是网页 j 出链的数量,(j,i) 表示存在网页 j 指向网页 i 的超连接。
从数学的观点看就是存在一个包含 n 个未知量的线性方程组,每一个网页的权重都是一个未知量。
能够用一个矩阵来表示,首先做一个符号的约定,用列向量 P 表示 n 个网页的PageRank值,以下:
再用矩阵 A 表示有向图的邻接矩阵,并按以下规则未每条有向边赋值:
例如以下邻接矩阵 A:
咱们能够获得以下方程组:
咱们的任务是在已知矩阵 A 的条件下,求解向量 P。这个 P 是循环定义的,因此采用幂迭代方法求解 P。
咱们定义给定初值 ,定义
是通过第 n 次迭代获得的 P 值,能够形式化以下:
知足上述方程组的解就是
。
固然,也能够用马尔柯夫链(markov chain)进行建模,这时就能够当作是markov chain的一个状态(state),A 能够表示状态转移矩阵(state transition matrix),这样就能够转换成马尔柯夫链的遍历性和极限分布问题。
是否收敛,取决于下面几个条件是否成立:
1. 是否存在?
2. 若是极限存在,它是否与的选取有关?即收敛性是否初始值敏感?
3. 若是极限存在,而且与的选取无关,它做为网页排序的依据是否真的合理?
若是要知足前2个问题, 转移矩阵A 必须知足如下3个条件:
1. 转移矩阵A 必须是随机矩阵; 随机矩阵要求矩阵的每个行和都为1,即不能出现dead end节点(不存在任何出链的节点),若是web网络拓朴中存在dead end,则原始随机矩阵的条件不能成立。 可是不要忘了,由于随机几率转移矩阵(心灵矩阵)的存在,实际的M不存在为0的行,因此这第一个条件时知足的。 2. 转移矩阵A 是不可约的; 一样的道理,正常的web拓朴不必定能知足彻底强连通的条件(由于Dead Ends的存在),可是由于随机几率转移矩阵(心灵矩阵)的存在,这第二个条件也成立 3. 转移矩阵A 是非周期的; 一样由于随机几率转移矩阵(心灵矩阵)的存在,周期性的定义没法知足,因此最终的转移矩阵能够说知足非周期性
上述的3个条件使得收敛性的前两个条件获得了知足。接下来还剩最后一个问题,即'网页排序的依据是不是真的合理'。
这个问题笔者是这么认为的:
所谓的“重要”,其实要看咱们的目的是什么。这就跟你买车同样,有的人认为性能重要,就会更看重性能方面的指标;有的人认为颜值重要,就会更关注外观相关的指标。
而 PageRank 的发明场景是互联网网页搜索排序,佩奇认为网页之间的互相连接程度体现了网页的重要性,毕竟互联网的本质就是万物互联,一个孤立存在的网页会被认为是没有价值的,或者很不因特内的。
这又引伸出另外一个重要的问题,PageRank算法能够直接移植到网络安全攻防检测领域吗?先抛出一个观点:要慎重!就算能够,在大多数状况下,也须要改造原始的算法公式。
实际上,这也是笔者在项目中遇到的最多的一个问题之一。不少很秀的算法,从原理上看,明明是能够适用于网络安全领域,可是当你真的移植到你的业务场景中后,会发现,结果并非和你预期中那么完美。形成这种问题的根本缘由是什么呢?
笔者认为这是由于如今机器学习经典教材中的经典算法,虽说起来是通用算法,可是其实它们都是由于一些具体的场景被创造出来的,最适合的也是其当初被创造出来的场景。移植到其余的问题领域后,最核心的假设前提可能改变了,算法是否能发挥出原来同样惊艳的做用,也就须要打一个问号了。
因此在实际的项目中,咱们须要根据具体场景问题具体分析,对多个算法进行stacking组合,造成一个最合适的pipeline。甚至须要修改原始算法核心公式,为具体问题定制化一个专用的算法。这样才有可能真正发挥出做用。
Relevant Link:
http://www.doc88.com/p-8018027982328.html
https://pan.baidu.com/s/11I8G8Wnc0W1u8RVHXDjeQA
每一行由 “帐户 - 粉丝”组成。
# -*- coding: utf-8 -*- from pygraph.classes.digraph import digraph import sqlite3 class PRIterator: __doc__ = '''计算一张图中的PR值''' def __init__(self, dg): self.damping_factor = 0.85 # 阻尼系数,即α self.max_iterations = 1000 # 最大迭代次数 self.min_delta = 0.00001 # 肯定迭代是否结束的参数,即ϵ self.graph = dg def page_rank(self): # 先将图中没有出链的节点改成对全部节点都有出链 for node in self.graph.nodes(): if len(self.graph.neighbors(node)) == 0: for node2 in self.graph.nodes(): digraph.add_edge(self.graph, (node, node2)) nodes = self.graph.nodes() graph_size = len(nodes) if graph_size == 0: return {} # 给每一个节点赋予初始的PR值,第一轮的PR值是均等的,即 1/N page_rank = dict.fromkeys(nodes, 1.0 / graph_size) # 公式中的(1−α)/N部分 damping_value = (1.0 - self.damping_factor) / graph_size flag = False for i in range(self.max_iterations): change = 0 for node in nodes: rank = 0 # 遍历全部“入射”的页面 for incident_page in self.graph.incidents(node): # "入射"页面的权重根据其出链个数均分,而后传递给当前页面 rank += self.damping_factor * (page_rank[incident_page] / len(self.graph.neighbors(incident_page))) # 增长随机几率转移矩阵的部分 rank += damping_value change += abs(page_rank[node] - rank) # 绝对值 page_rank[node] = rank print("This is NO.%s iteration" % (i + 1)) print(page_rank) if change < self.min_delta: flag = True break if flag: print("finished in %s iterations!" % node) else: print("finished out of 100 iterations!") return page_rank if __name__ == '__main__': # 建立一个网络拓朴图 dg = digraph() conn = sqlite3.connect('zhihu.db') c = conn.cursor() nodes = [] cursor = c.execute("SELECT DISTINCT user_url, followee_url FROM Following;") for row in cursor: #print row if row[0] not in nodes: nodes.append(row[0]) if row[1] not in nodes: nodes.append(row[1]) # 添加实体节点 dg.add_nodes(nodes) cursor = c.execute("SELECT DISTINCT user_url, followee_url FROM Following;") for row in cursor: user_url = str(row[0]) followee_url = str(row[1]) # 添加实体间link(边) followee_url -> user_url #print "followee_url:{0} -> user_url:{1}".format(followee_url, user_url) dg.add_edge((followee_url, user_url)) conn.close() # PRrank迭代计算 pr = PRIterator(dg) page_ranks = pr.page_rank() with open("page_ranks.txt", 'w') as fp: fp.write(str(page_ranks)) print("The final page rank is\n", page_ranks)
使用大数据组件进行100轮训练后,获得的pagerank排序结果以下:
node weight chengbailao 0.01406879 wind 0.00608487 neaton 0.00568152 jixin 0.00488994 zeng-kai-87 0.004726 yskin 0.00370338 hou-ye-60 0.00326399 followstars 0.00305445 yu-chen-41-39 0.00276672 gmf8541 0.00273379 _zhao_xu_ 0.0026574 zhai-huo-18 0.0026528 xushiyuzhihu 0.0025693 yueyihe 0.00212099 peng 0.00210883 bing-hou-20 0.00208451 oogoo 0.00201606 liuya802 0.0018386 mengtoy 0.0018233 yvancao 0.00176921 tang-chen 0.00174643 guo-shu-86-30 0.00168231 bhuztez 0.00165525 wang-wen-ping 0.00164296 chenxix 0.00163695 melinywu 0.0016331 chen-chen-66-21 0.00161215 ihate 0.00156636 stephen-cheng 0.00152092 boxun 0.00148928 wang-wen-ping-27 0.00148886 lxjts 0.0014828 tan-ri-tian 0.00144296 wangxiaofeng 0.00143438 zhong-ye-zi-49 0.0014341 james-swineson 0.00141263 puloon 0.0013969 mym95 0.00139541 lie-feng-2 0.00138813 susus 0.00137226 gymitat 0.00136749 fang-wen-32 0.00135586 joyneop 0.0013496 xuzhihong 0.00134178 qi-yuan-yuan-52 0.00131502 wannian 0.00130875 qiao-yang-76-30 0.00130659 bettercallsaul 0.00129384 du-forever 0.00128617 yuningyichen 0.00128111 xiao-chu 0.00126434 chen-yin-dong 0.00124541 shen0101 0.00124078 liu-yi-han-46 0.00123879 403Forbidden 0.00123664 eodoso 0.00123578 shenpp 0.00123555 dian-qian-du-dian-jian 0.00122604 anyan 0.00121871 qianjin 0.00119378 guaguaguaguaguagua 0.00115795 xie-wei-you 0.00114612 lu-zheng-29-24 0.00113225 yang-hu-85 0.00113176 yuba100 0.00113136 jueshihaojian 0.00112986 whale 0.00111578 fashiontop 0.0011137 cklover 0.00110348 zhu-yixin-42 0.00110276 mcbuder 0.00110074 quiver 0.00110009 lewhwa 0.00110006 zuo-qing-96 0.00108733 niu-yue-lao-li-xiao-chang 0.00108432 patli 0.00108025 sapereaude 0.00107575 hipara 0.00107537 GilgameshK 0.00107137 zkaip 0.00107053 sddcreerf 0.00106716 ling-er-ding-dang 0.00106533 liqiang123 0.00104595 david-du 0.00103702 aguaithefreak 0.00101756 chong 0.00100256 miaomiaomiao 0.00099407 hu-qian-qiu 0.00098814 han-yan-hui 0.00097521 songtsee 0.00096843 sun-peng-70-45 0.00096207 johnsonwang 0.00095147 hu-bi-teng 0.0009501 deutsch-99 0.00092026 lubenyuan.com 0.00091791 shijun 0.00091377 chengyuan 0.00091036 gazhi-liu 0.0008935 luo-li-10 0.00088078
Relevant Link:
http://www.cnblogs.com/fengfenggirl/p/pagerank-cnblogs.html https://github.com/BigPeng/cnblogs-user-pagerank https://www.jianshu.com/p/60ffb949113f https://www.jianshu.com/p/3b2a1895a12d