PAGE-RANK算法及SPARK实现分析



查看原文: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/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值