继上篇介绍的算法和单机实现。这篇介绍一下mapreduce实现。
其实python的实现已经用的mapreduce的思路了,改成在真实分布式环境并不难,我在hadoop平台上简单的实现了这个算法。
Map端完成边的propagation效应,即input(fromId, toId) -> output(toId,labelinfo)。
每轮计算需要上一轮节点的Label结果,一次性读到mapper中,供map时计算。
我用了一个二维数组int[3][#nodes]来存, 其中3行,第一行存的所有label隶属度。
第一行是隶属度最大的,第三行最小。每行的数组的index为NodeID。例如id=100的节点的3个里数组分别是 int[0][100], int[1][100], int[2][100]。
每个int有4个字节32位,第一个字节存可以表示256的范围,0~1的隶属度可以离散化255个区间;后三个字节可以表示2^24个数,即1600万+,也就是nodeid表示范围。
那么1000W的二维数组,实际需要160M左右的空间,完全可以放进内存。
Reduce 端完成label确定的工作,即 input (toId,labelinfo…) -> ouput(toId,newlabelinfo)
Reduce的工作比较直接,计算以后最后把label直接写出去。
这次我的Label文件放到HDFS上进行读取,因此Reduce就没有直接用context.write 。
我生成了个小规模的网络100W的节点,边约1000W,只有ID的网络文件大小只有150M-。map阶段花了1分钟左右,reducer因为只有1个,所以时间稍长;MR一次job大概2分到2分半。
由于网络规模问题,我没有对结果进行检验,只是关注了程序运行情况,因此也可以讨论一下可优化或是值得注意的地方。
1. 网络图的输入格式是一行一条边,对于简单propagate方式是没问题的,直接吧结果写出去就行,但是如果考虑复杂一些的方式就需要一行包含所有的邻居。
2. Mapper的输出可能会比较多,因此合并结果减少网络传输能提高任务的运行速度,比如在Mapper中合并,或者写Combiner(代码中有)。
那么这个算法的效果到底好不好呢?我也不清楚,按照算法思想,应该是异步计算的方式更靠谱,同步方式容易产生类标不能很好的收敛,例如如果3个点ABC互相连接的连通分量,那么每次label都会收敛成{a:0.33,b:33,c:0.33} …这个问题其实也值得考虑,毕竟同步方法可以直接利用hadoop的mapreduce优势。
如果对图挖掘的算法实现有兴趣,可以再参考2篇论文:
Pregel: A System for large-scale graph processing
PEGASUS: A Peta-Scale Graph Mining System - Implementation and Observations
最近发现的,没来得及看…
算法java代码在这,还是那句话:不规范,但是不难看懂。http://download.csdn.net/source/3424779
还有生成网络的py文件,不是平均度分布,也不知道是个啥形状的…