上一篇讲了一下spark graphx入门的一些小知识,这周开始做了一下标签传播算法的实现。标签传播算法,即label propagation,是社交网路中用于发现社区结构的一个非常简单高效的算法。时间复杂度低,实现简单。该方法同样能用于标签挖掘场景,通过少量的已标注数据进行指导并预测未标记数据的标签。操作简单,运算量小,适合大规模数据信息的挖掘和处理。
标签传播算法主要包含两个步骤:
第一步: 为所有节点指定一个唯一的标签,可以直接以节点id作为标签;
第二步: 逐轮刷新所有节点的标签, 对于某一个节点,考察其所有邻居节点的标签,并进行统计,将出现个数最多的那个标签赋给当前节点。当个数最多的标签不唯一时,随机选一个。
重复第二步直到达到收敛条件为止,通常会设置一个迭代次数的限制。一般来说,收敛的快慢和图的大小无关,迭代5次基本上95%以上的节点就会收敛。
在大规模图数据集上,并行的图计算是非常必要的。graphx提供了Pregel计算接口。pregel主要包含三个函数:vprog函数、sendMsg函数、mergeMsg函数。其源码如下:
def pregel[A: ClassTag](
initialMsg: A,
maxIterations: Int = Int.MaxValue,
activeDirection: EdgeDirection = EdgeDirection.Either)(
vprog: (VertexId, VD, A) => VD,
sendMsg: EdgeTriplet[VD, ED] => Iterator[(VertexId, A)],
mergeMsg: (A, A) => A)
: Graph[VD, ED] = {
Pregel(graph, initialMsg, maxIterations, activeDirection)(vprog, sendMsg, mergeMsg)
}
代码中A表示消息的类型。从中可以看到,vprog函数是运行在每个节点上的,通过接收信息,更新自身的节点属性值。sendMsg是发送消息的。mergeMsg则是合并消息的。
object LabelPropagation {
def run[VD, ED: ClassTag](graph: Graph[VD, ED], maxSteps: Int): Graph[VertexId, ED] = {
//初始化每个定点的社区表示为当前结点的id值
val lpaGraph = graph.mapVertices { case (vid, _) => vid }
//定义消息的发送函数,将顶点的社区标识发送到相邻顶点
def sendMessage(e: EdgeTriplet[VertexId, ED]): Iterator[(VertexId, Map[VertexId, Long])] = {
Iterator((e.srcId, Map(e.dstAttr -> 1L)), (e.dstId, Map(e.srcAttr -> 1L)))
}
//顶点的消息聚合函数 将每个节点消息聚合,做累加;例如一个定点出现了两次,id->2
def mergeMessage(count1: Map[VertexId, Long], count2: Map[VertexId, Long])
: Map[VertexId, Long] = {
(count1.keySet ++ count2.keySet).map { i =>
val count1Val = count1.getOrElse(i, 0L)
val count2Val = count2.getOrElse(i, 0L)
i -> (count1Val + count2Val)
}.toMap
}
//顶点属性更新,如果有消息,取出现次数最大的作为自身的新标记
def vertexProgram(vid: VertexId, attr: Long, message: Map[VertexId, Long]): VertexId = {
if (message.isEmpty) attr else message.maxBy(_._2)._1
}
val initialMessage = Map[VertexId, Long]
Pregel(lpaGraph, initialMessage, maxIterations = maxSteps)(
vprog = vertexProgram,
sendMsg = sendMessage,
mergeMsg = mergeMessage)
}
}
根据LPA算法的原理,可以看到代码中传递的消息类型就是Map[VertexId, Long],即节点所在标签和其出现的次数。
整个的原理和实现差不多就讲完了。在具体的应用过程中,其实还需要考虑很多东西,比如在人群标签扩散中,节点之间的关系紧密程度、关系的类型都会影响标签的传播准确性。