简单易学的机器学习算法——Label Propagation

1. 概述

对于社区,没有一个明确的定义,有很多对社区的定义,如社区是指在一个网络中,有一组节点,它们彼此都相似,而组内的节点与网络中的其他节点则不相似。更为一般的可以表述为:社区是指网络中节点的集合,这些节点内部连接较为紧密而外部连接较为稀疏。

基于上述的形象的表示,出现了很多的社区划分算法,如Fast Unfolding算法[1],Fast Unfolding算法是基于模块度的算法,模块度相当于对上述社区的形象描述的一种抽象表示,成为优化的主要目标。但是模块度的计算相对较为复杂,这也成为限制基于模块度算法在大规模图数据上的应用。

Label Propagation算法[2]是一种基于标签传播的局部社区划分算法,相比较而言其简单的计算过程,能够在大规模的图数据上应用。

2. Label Propagation算法

2.1. Label Propagation算法概述

Label Propagation算法是一种基于标签传播的局部社区划分算法。对于网络中的每一个节点,在初始阶段,Label Propagation算法对每一个节点一个唯一的标签,在每一个迭代的过程中,每一个节点根据与其相连的节点所属的标签改变自己的标签,更改的原则是选择与其相连的节点中所属标签最多的社区标签为自己的社区标签,这便是标签传播的含义。随着社区标签的不断传播,最终紧密连接的节点将有共同的标签。

Label Propagation算法最大的优点是其算法过程比较简单,想比较于优化模块度的过程,算法速度非常快。Label Propagation算法利用网络的结构指导标签的传播过程,在这个过程中无需优化任何函数。在算法开始前我们不必要知道社区的个数,随着算法的迭代,在最终的过程中,算法将自己决定社区的个数。

2.2. Label Propagation算法原理

对于Label Propagation算法,假设对于节点 x x x,其邻居节点为 x 1 , x 2 , ⋯   , x k x_1,x_2,\cdots ,x_k x1,x2,,xk,对于每一个节点,都有其对应的标签,标签代表的是该节点所属的社区。在算法迭代的过程中,节点 x x x根据其邻居节点更新其所属的社区。更新的规则是选择节点 x x x的邻居节点中,所属社区最多的节点对应的社区为其新的社区。

上述便是Label Propagation算法的核心概念。在初始节点,令每一个节点都属于唯一的社区,当社区的标签在节点间传播的过程中,紧密相连的节点迅速地取得一致的标签。具体过程如下图所示:

在这里插入图片描述

这样的过程不断地持续下去,直到所有可能聚集到一起的节点都具有了相同的社区标签。在传播过程的最终,具有相同社区标签的节点被划到相同的社区中称为一个个独立的社区。

在标签传播的过程中,节点的标签的更新过程可以分为两种,即:

  • 同步更新
  • 异步更新

同步更新是指对于节点 x x x,在第 t t t代时,根据其所有邻居节点在第 t − 1 t-1 t1代时的社区标签对其标签进行更新。即:

C x ( t ) = f ( C x 1 ( t − 1 ) , C x 2 ( t − 1 ) , ⋯   , C x k ( t − 1 ) ) C_x\left ( t \right )=f\left ( C_{x_1}\left ( t-1 \right ),C_{x_2}\left ( t-1 \right ),\cdots ,C_{x_k}\left ( t-1 \right ) \right ) Cx(t)=f(Cx1(t1),Cx2(t1),,Cxk(t1))

其中, C x ( t ) C_x\left ( t \right ) Cx(t)表示的是节点 x x x在第 t t t代时的社区标签。函数 f f f表示的取的参数节点中所有社区个数最大的社区。同步更新的方法存在一个问题,即对于一个二分或者近似二分的网络来说,这样的结构会导致标签的震荡,如下图所示:

在这里插入图片描述

在上图中,两边的标签会在社区标签 a a a和社区标签 b b b不停地震荡。

对于异步更新方式,其更新公式为:

C x ( t ) = f ( C x i 1 ( t ) , ⋯   , C x i m ( t ) , C x i ( m + 1 ) ( t − 1 ) , ⋯   , C x i k ( t − 1 ) ) C_x\left ( t \right )=f\left ( C_{x_{i1}}\left ( t \right ),\cdots ,C_{x_{im}}\left ( t \right ),C_{x_{i(m+1)}}\left ( t-1 \right ),\cdots ,C_{x_{ik}}\left ( t-1 \right ) \right ) Cx(t)=f(Cxi1(t),,Cxim(t),Cxi(m+1)(t1),,Cxik(t1))

其中,邻居节点 x i 1 , ⋯   , x i m x_{i1},\cdots ,x_{im} xi1,,xim的社区标签在第 t t t代已经更新过,则使用其最新的社区标签。而邻居节点 x i ( m + 1 ) , ⋯   , x i k x_{i(m+1)},\cdots ,x_{ik} xi(m+1),,xik在第 t t t代时还没有更新,则对于这些邻居节点还是用其在第 t − 1 t-1 t1代时的社区标签。

对于节点的更新顺序可以顺序选择,为了最终算法能够收敛,需要对读入的节点打散。

对于整个迭代过程,理想的情况下是图中节点的社区标签不再改变,此时迭代过程便可以停止。但是这样的定义方式存在一个问题,即对于某个节点,其邻居节点中存在两个或者多个最大的社区标签。对于这样的节点,其所属的社区是随机选取的,这样,按照上述的定义,此过程会一直执行下去。对上述的迭代终止条件重新修改:

迭代的终止条件是:对于每一个节点,在其所有的邻居节点所属的社区中,其所属的社区标签是最大的。

上述的定义可以表示为:如果用 C 1 , ⋯   , C p C_1,\cdots ,C_p C1,,Cp表示社区标签, d i C j d_i^{C_j} diCj表示是节点 i i i的所有邻居节点中社区标签为 C j C_j Cj的个数,则算法终止的条件为:对于每一个节点 i i i,有:

i f   i   h a s   l a b e l   C m   t h e n   d i C m ≥ d i C j   ∀ j if\: i\: has\: label\: C_m\: then\: d_i^{C_m}\geq d_i^{C_j}\: \forall j ifihaslabelCmthendiCmdiCjj

这样的停止条件可以使得最终能够获得强壮的社区(Strong Community),但是社区并不是唯一的。对于Strong Community,其要求对于每一个节点,在其社区内部的邻居节点严格大于社区外部的邻居节点,然而Label Propagation算法能够保证对于每一个节点,在其所属的社区内有足够多的邻居节点。

3.3. Label Propagation算法过程

Label Propagation算法的过程如下:

  • 对网络中的每一节点初始化其所属社区标签,如对于节点 x x x,初始化其社区标签为 C x ( 0 ) = x C_x\left ( 0 \right )=x Cx(0)=x
  • 设置代数 t t t
  • 对于网络中的节点设置其遍历顺序和节点的集合 X X X
  • 对于每一个节点 x ∈ X x\in X xX,令 C x ( t ) = f ( C x i 1 ( t ) , ⋯   , C x i m ( t ) , C x i ( m + 1 ) ( t − 1 ) , ⋯   , C x i k ( t − 1 ) ) C_x\left ( t \right )=f\left ( C_{x_{i1}}\left ( t \right ),\cdots ,C_{x_{im}}\left ( t \right ),C_{x_{i(m+1)}}\left ( t-1 \right ),\cdots ,C_{x_{ik}}\left ( t-1 \right ) \right ) Cx(t)=f(Cxi1(t),,Cxim(t),Cxi(m+1)(t1),,Cxik(t1))
  • 判断是否可以迭代结束,如果否,则设置 t = t + 1 t=t+1 t=t+1,重新遍历。

3. 实验

3.1. 数据描述

实验过程中使用的数据为参考[1]中使用的数据,其结构如下所示:

在这里插入图片描述

其在Fast Unfolding算法的划分结果为:

  • 社区1:节点0,1,2,3,4,5,6,7
  • 社区2:节点8,9,10,11,12,13,14,15

3.2. 实验代码

#####################################
# Author:zhaozhiyong
# Date:20151205
# Fun:Label Propagation
# Fix:已经用Python3重新修改
#####################################
import random

def loadData(filePath):
    f = open(filePath)
    vector_dict = {}
    edge_dict = {}
    
    for line in f.readlines():
        lines = line.strip().split("\t")

        for i in range(2):
            if lines[i] not in vector_dict:
                #put the vector into the vector_dict
                vector_dict[lines[i]] = int(lines[i])
                #put the edges into the edge_dict
                edge_list = []
                if len(lines) == 3:
                    edge_list.append(lines[1-i]+":"+lines[2])
                else:
                    edge_list.append(lines[1-i]+":"+"1")
                edge_dict[lines[i]] = edge_list
            else:
                edge_list = edge_dict[lines[i]]
                if len(lines) == 3:
                    edge_list.append(lines[1-i]+":"+lines[2])
                else:
                    edge_list.append(lines[1-i]+":"+"1")
                edge_dict[lines[i]] = edge_list
    return vector_dict, edge_dict

def get_max_community_label(vector_dict, adjacency_node_list):
    label_dict = {}
    # generate the label_dict
    for node in adjacency_node_list:
        node_id_weight = node.strip().split(":")
        node_id = node_id_weight[0]
        node_weight = int(node_id_weight[1])
        if vector_dict[node_id] not in label_dict:
            label_dict[vector_dict[node_id]] = node_weight
        else:
            label_dict[vector_dict[node_id]] += node_weight

    # find the max label
    sort_list = sorted(label_dict.items(), key = lambda d: d[1], reverse=True)

    return sort_list[0][0]

def check(vector_dict, edge_dict):
    for node in vector_dict.keys():
        adjacency_node_list = edge_dict[node]
        node_label = vector_dict[node]
        label_check = {}
        for ad_node in adjacency_node_list:
            node_id_weight = ad_node.strip().split(":")
            node_id = node_id_weight[0]
            if vector_dict[node_id] not in label_check:
                label_check[vector_dict[node_id]] = 1
            else:
                label_check[vector_dict[node_id]] += 1
        
        sort_list = sorted(label_check.items(), key = lambda d: d[1], reverse=True)
        
        if node_label == sort_list[0][0]:
            continue
        else:
            return 0
    
    return 1    
            
def label_propagation(vector_dict, edge_dict):
    #initial, let every vector belongs to a community
    t = 0
    #for every node in a random order
    while True:
        if (check(vector_dict, edge_dict) == 0):
            t = t+1
            print("----------------------------------------")
            print("iteration: ", t)
            for node in vector_dict.keys():
                adjacency_node_list = edge_dict[node]
                vector_dict[node] = get_max_community_label(vector_dict, adjacency_node_list)
            print(vector_dict)
        else:
            break
    return vector_dict


if __name__ == "__main__":
    vector_dict, edge_dict=loadData("./cd_data.txt")
    dict_key_list = list(vector_dict.keys())
    random.shuffle(dict_key_list)
    new_vector_dict = {}
    for key in dict_key_list:
        new_vector_dict[key] = vector_dict.get(key)
    print("original community: ", new_vector_dict)
    vec_new = label_propagation(new_vector_dict, edge_dict)
    print("---------------------------------------------------------")
    print("the final result: ")
    community_dict = {}
    for key in vec_new.keys():
        if vec_new[key] not in community_dict:
            community_dict[vec_new[key]] = []
        
        community_dict[vec_new[key]].append(str(key))
    
    for key in community_dict.keys():
        print("community-" + str(key) + " ---> " + str(community_dict[key]))

3.3. 代码解析

上述的实验代码中主要包括 4 4 4个部分,其一为loadData()函数,其目的是从文件中读入数据,取得点的信息,边的信息;第二个是label_propagation()函数,其目的是Label Propagation算法的整个迭代过程,期中会调用两个函数,即get_max_community_label()函数和check()函数,get_max_community_label()函数的目的是在对每个节点遍历其邻居节点的过程中,选择出最大的社区标签,check()函数的目的是判断算法是否迭代结束。

4. 实验结果

在这里插入图片描述

参考文献

[1] Blondel V D, Guillaume J L, Lambiotte R, et al. Fast unfolding of communities in large networks[J]. Journal of statistical mechanics: theory and experiment, 2008, 2008(10): P10008.

[2] Raghavan U N, Albert R, Kumara S. Near linear time algorithm to detect community structures in large-scale networks[J]. Physical review E, 2007, 76(3): 036106.

  • 8
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
⼈⼯智能之机器学习常见算法 摘要 摘要 之前⼀直对机器学习很感兴趣,⼀直没时间去研究,今天刚好是周末,有时间去各⼤技术论坛看看,刚好看到⼀篇关于机器学习不错的⽂章,在这⾥ 就分享给⼤家了. 机器学习⽆疑是当前数据分析领域的⼀个热点内容。很多⼈在平时的⼯作中都或多或少会⽤到机器学习的算法。这⾥IT经理⽹为您总结⼀下 常见的机器学习算法,以供您在⼯作和学习中参考。 机器学习的算法很多。很多时候困惑⼈们都是,很多算法是⼀类算法,⽽有些算法⼜是从其他算法中延伸出来的。这⾥,我们从两个⽅⾯来 给⼤家介绍,第⼀个⽅⾯是学习的⽅式,第⼆个⽅⾯是算法的类似性。 学习⽅式 学习⽅式 根据数据类型的不同,对⼀个问题的建模有不同的⽅式。在机器学习或者⼈⼯智能领域,⼈们⾸先会考虑算法的学习⽅式。在机器学习领 域,有⼏种主要的学习⽅式。将算法按照学习⽅式分类是⼀个不错的想法,这样可以让⼈们在建模和算法选择的时候考虑能根据输⼊数据来 选择最合适的算法来获得最好的结果。 监督式学习: 监督式学习: 在监督式学习下,输⼊数据被称为"训练数据",每组训练数据有⼀个明确的标识或结果,如对防垃圾邮件系统中"垃圾邮件""⾮垃圾邮 件",对⼿写数字识别中的"1","2","3","4"等。在建⽴预测模型的时候,监督式学习建⽴⼀个学习过程,将预测结果与"训 练数据"的实际结果进⾏⽐较,不断的调整预测模型,直到模型的预测结果达到⼀个预期的准确率。监督式学习的常见应⽤场景如分类问题 和回归问题。常见算法有逻辑回归(Logistic Regression)和反向传递神经⽹络(Back Propagation Neural Network) ⾮监督式学习: ⾮监督式学习: 在⾮监督式学习中,数据并不被特别标识,学习模型是为了推断出数据的⼀些内在结构。常见的应⽤场景包括关联规则的学习以及聚类等。 常见算法包括Apriori算法以及k-Means算法。 半监督式学习: 半监督式学习: 在此学习⽅式下,输⼊数据部分被标识,部分没有被标识,这种学习模型可以⽤来进⾏预测,但是模型⾸先需要学习数据的内在结构以便合 理的组织数据来进⾏预测。应⽤场景包括分类和回归,算法包括⼀些对常⽤监督式学习算法的延伸,这些算法⾸先试图对未标识数据进⾏建 模,在此基础上再对标识的数据进⾏预测。如图论推理算法(Graph Inference)或者拉普拉斯⽀持向量机(Laplacian SVM.)等。 强化学习: 强化学习: 在这种学习模式下,输⼊数据作为对模型的反馈,不像监督模型那样,输⼊数据仅仅是作为⼀个检查模型对错的⽅式,在强化学习下,输⼊ 数据直接反馈到模型,模型必须对此⽴刻作出调整。常见的应⽤场景包括动态系统以及机器⼈控制等。常见算法包括Q-Learning以及时间 差学习(Temporal difference learning) 在企业数据应⽤的场景下, ⼈们最常⽤的可能就是监督式学习和⾮监督式学习的模型。 在图像识别等领域,由于存在⼤量的⾮标识的数据 和少量的可标识数据, ⽬前半监督式学习是⼀个很热的话题。 ⽽强化学习更多的应⽤在机器⼈控制及其他需要进⾏系统控制的领域。 算法类似性 算法类似性 根据算法的功能和形式的类似性,我们可以把算法分类,⽐如说基于树的算法,基于神经⽹络的算法等等。当然,机器学习的范围⾮常庞 ⼤,有些算法很难明确归类到某⼀类。⽽对于有些分类来说,同⼀分类的算法可以针对不同类型的问题。这⾥,我们尽量把常⽤的算法按照 最容易理解的⽅式进⾏分类。 回归算法: 回归算法: 回归算法是试图采⽤对误差的衡量来探索变量之间的关系的⼀类算法。回归算法是统计机器学习的利器。在机器学习领域,⼈们说起回归, 有时候是指⼀类问题,有时候是指⼀类算法,这⼀点常常会使初学者有所困惑。常见的回归算法包括:最⼩⼆乘法(Ordinary Least Square),逻辑回归(Logistic Regression),逐步式回归(Stepwise Regression),多元⾃适应回归样条(Multivariate Adaptive Regression Splines)以及本地散点平滑估计(Locally Estimated Scatterplot Smoothing) 基于实例的算法 基于实例的算法 基于实例的算法常常⽤来对决策问题建⽴模型,这样的模型常常先选取⼀批样本数据,然后根据某些近似性把新数据与样本数据进⾏⽐较。 通过这种⽅式来寻找最佳的匹配。因此,基于实例的算法常常也被称为"赢家通吃"学习或者"基于记忆的学习"。常见的算法包括 k- Nearest Neighbor(KNN), 学习⽮量量化(Learning Vector Quantization, LVQ),以及⾃组织映射算法(Self-Org

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值