Graphx图算法【3】标签传播LabelProgagation

标签传播是将自己的标签信息传播给所有的邻居节点,邻居节点根据收到的标签信息选择出现最多的那个标签来更新自己的标签,并不断传播下去,直到图中节点的标签不再变动。

3.1 简介

标签传播是为了在网络中发现社区,通过将自身标签传递给邻居节点以期形成一个具有同样标签的社区团体,标签传播合适于非重叠社区的发现。

3.2 应用场景

(一)社区发现

标签传播进行社区发现即聚类,可以发现社交网络中的团体、诈骗犯罪团伙;

(二)节点预测

根据邻居节点的标签预测当前节点所属类别,可以进行分类预测、标签预测等。

(三)信息分类

3.3 算法思路

核心思想:将一个节点的邻居节点的标签中数量最多的那个标签作为自身的标签,如果数量最多的标签多于一个,则随机选择一个最多的标签作为自身的标签。

计算步骤:

  1. 初始化,给每个节点一个唯一的标签,Graphx将节点的自身id作为自己的标签;
  2. 每个节点将自身标签以及对应的数值1组成map发送给邻居节点;
  3. 每个节点将会收到来自邻居的标签信息,对收到的具有同样的标签进行汇总统计,
     对收到Map结构中的第二个元素进行相加求和,即得到节点收到的标签以及收到同样标签的数量;
  4. 节点将收到的消息进行处理,根据标签数量选择数量最大的标签及数量作为自己新的属性。
   
  不断迭代上述第3,4步。

3.4 源码解析

def run[VD, ED: ClassTag](graph: Graph[VD, ED], maxSteps: Int): Graph[VertexId, ED] = {
    require(maxSteps > 0, s"Maximum of steps must be greater than 0, but got ${maxSteps}")

    //初始化消息,每个节点将自身id作为自己的初始标签
    val lpaGraph = graph.mapVertices { case (vid, _) => vid }
    //每条边上分别将两端节点的标签信息及标签数量1发送给对方节点
    def sendMessage(e: EdgeTriplet[VertexId, ED]): Iterator[(VertexId, Map[VertexId, Long])] = {
      Iterator((e.srcId, Map(e.dstAttr -> 1L)), (e.dstId, Map(e.srcAttr -> 1L)))
    }
    //节点收到的多个消息,将相同标签的数量相加
    def mergeMessage(count1: Map[VertexId, Long], count2: Map[VertexId, Long])
      : Map[VertexId, Long] = {
      (count1.keySet ++ count2.keySet).map { i => //i表示标签
        val count1Val = count1.getOrElse(i, 0L)  //如果count1消息中有标签i,则拿到1,否则0
        val count2Val = count2.getOrElse(i, 0L)  //如果count2消息中有标签i,则拿到1,否则0
        i -> (count1Val + count2Val)             //将消息中标签i出现的次数相加
      }(collection.breakOut)// more efficient alternative to [[collection.Traversable.toMap]]
    }
    //collection.breakOut是作为map的第二个隐式转换的参数传入的,可以直接得到目标集合Map

    //如果没有收到消息则维持原来的标签,否则将收到的最多的标签作为自己新的标签
    def vertexProgram(vid: VertexId, attr: Long, message: Map[VertexId, Long]): VertexId = {
      if (message.isEmpty) attr else message.maxBy(_._2)._1
    }
    //初始化消息,空的标签Map信息
    val initialMessage = Map[VertexId, Long]()

    //进Pregel
    Pregel(lpaGraph, initialMessage, maxIterations = maxSteps)(
      vprog = vertexProgram,
      sendMsg = sendMessage,
      mergeMsg = mergeMessage)
  }
}

对于代码的说明都在注释里,这里简要说一下collection.breakOut这个参数,它是一个隐式转换函数,该函数返回必须是个CanBuildFrom这个trait的对象,如果map的写法是下面这样,则map的返回类型是和map函数的调用者keySet的是类型一致的,这就需要对结果再显示转换为Map类型。(先得到其他类型,再转换为Map类型)

keySet.map()

如果map的写法是下面这样的,则map的返回类型会自动做结果的类型转换(效率会比显示类型转换快)。至于为什么会转换为Map,看了几遍源码也没看懂在哪里指定转换为Map类型。

keySet.map()(collection.breakOut)

scala代码博大精深啊!!!

你可能感兴趣的:(Graphx图算法【3】标签传播LabelProgagation)