查看原文:http://www.wyblog.cn/2017/01/06/pagerank%e7%ae%97%e6%b3%95%e5%8f%8aspark%e5%ae%9e%e7%8e%b0%e5%88%86%e6%9e%90/算法
这里不总结算法,下面这篇博客总结的很清晰。http://www.cnblogs.com/fengfenggirl/p/pagerank-introduction.html要明白的是,PageRank算法计算过程,其实就是一个马尔科夫链的状态转移过程,通过不断右乘状态转移矩阵,最后收敛到一个固定的向量,问题是这个固定的向量是什么呢? 假设状态转移矩阵为A,状态矩阵为X,最后我们收敛时,达到的效果是AX=X,这是不是非常的眼熟?再写明白一点就是 AX=1*X,这不是线性代数里的特征值与特征向量吗?原来,PageRank算法最后收敛到的结果,就是转移矩阵特征值为1的特征向量!这就产生了两个问题,其一,为什么这个向量就是代表了网页rank,其二,什么情况下它收敛或者不收敛? 对于第一个问题,其实比较好解释。在线性代数里,矩阵变换实际上就是一个线性变换,而特征值与特征向量代表的是,经过矩阵A对向量X做的线性变换,恰好等于其特征值对对应的特征向量做的伸缩变换,这就是说,在特征向量上,转移矩阵A实际上只能使其伸长或者缩短。回到PageRank算法上,我们寻找的恰是特征值为1的特征向量,代表转移矩阵已经不能对特征向量伸缩变换了,也就是说各个网站对其他网站的贡献度都达到稳定了,很明显,越重要的网站获得的贡献值应该越多,而此时网站获得的贡献值已经达到最优,所以就可以被当做网站rank的因素了。 其二,通过迭代的方式寻找特征向量时何时收敛,这是一个线性代数问题,作为学渣,等再查到资料再来更新吧。Spark实现
根据PageRank算法可知,其过程就是一个先将网页贡献值给分发出去,然后再进行汇总统计的过程。而这一过程,刚好是个先map再reduce的过程,所以,用分布式计算框架来进行计算非常方便。 这里采用Spark进行实现,有一个好处是Spark提供一种缓存机制,能把rdd给缓存在内存里,在迭代调用时能够加快速度。在PageRank算法里,我们每一次迭代都需要知道当前网站链接了哪些其他网站,也就是要给哪些网站贡献值。如果每一次迭代都重新计算一次这个链接关系的话,势必是浪费大量资源。所以,Spark实现里用了cache机制将其持久化。 对于一个网站链接图:Spark的工作流程可以如下表示:
基本思想就是,map()出每个网站的所有邻接点及这个网站的贡献值,然后进行flatmap()将贡献值全部分给邻接网站,最后再reduceByKey()进行聚合,就能计算出新的rank了。其中,为了避免算法的陷阱,更新rank值时,采用了线性加权的方式。 spark代码如下:
import re import sys from operator import add from pyspark.sql import SparkSession #用于计算值 def computeContribs(urls, rank): num_urls = len(urls) for url in urls: yield (url, rank / num_urls) #创建链接节点组 def parseNeighbors(urls): parts = re.split(r'\s+', urls) return parts[0], parts[1] #iterations为迭代次数参数 if __name__ == "__main__": if len(sys.argv) != 3: print("Usage: pagerank <file> <iterations>", file=sys.stderr) exit(-1) spark = SparkSession\ .builder\ .appName("PythonPageRank")\ .getOrCreate() lines = spark.read.text(sys.argv[1]).rdd.map(lambda r: r[0]) #创建邻接表,形式为<A,(B,C,D)>,并将其持久化以便迭代时反复使用 links = lines.map(lambda urls: parseNeighbors(urls)).distinct().groupByKey().cache() #初始化排名值为1.0 ranks = links.map(lambda url_neighbors: (url_neighbors[0], 1.0)) #迭代计算rank值 for iteration in range(int(sys.argv[2])): contribs = links.join(ranks).flatMap( lambda url_urls_rank: computeContribs(url_urls_rank[1][0], url_urls_rank[1][1])) ranks = contribs.reduceByKey(add).mapValues(lambda rank: rank * 0.85 + 0.15) for (link, rank) in ranks.collect(): print("%s has rank: %s." % (link, rank)) spark.stop()
查看原文:http://www.wyblog.cn/2017/01/06/pagerank%e7%ae%97%e6%b3%95%e5%8f%8aspark%e5%ae%9e%e7%8e%b0%e5%88%86%e6%9e%90/
PAGE-RANK算法及SPARK实现分析
最新推荐文章于 2024-06-09 19:51:04 发布