標簽傳播算法(Label Propagation Algorithm, LPA)初探


0. 社區划分簡介

0x1:非重疊社區划分方法

在一個網絡里面,每一個樣本只能是屬於一個社區的,那么這樣的問題就稱為非重疊社區划分。

在非重疊社區划分算法里面,有很多的方法

1. 基於模塊度優化的社區划分

基本思想是將社區划分問題轉換成了模塊度函數的優化,而模塊度是對社區划分算法結果的一個很重要的衡量標准。

模塊度函數在實際求解中無法直接計算得到全局最優解析解(類似深度神經網絡對應的復雜高維非線性函數),所以通常是采用近似解法,根據求解方法不同可以分為以下幾種方法:

1. 凝聚方法(down to top): 通過不斷合並不同社區,實現對整個網絡的社區划分,典型的方法有Newman快速算法,CNM算法和MSG-MV算法;
2. 分裂方法(top to down): 通過不斷的刪除網絡的邊來實現對整個網絡的社區划分,典型的方法有GN算法;
3. 直接近似求解模塊度函數(近似等價解): 通過優化算法直接對模塊度函數進行求解,典型的方法有EO算法;

2. 基於譜分析的社區划分算法
3. 基於信息論的社區划分算法
4. 基於標簽傳播的社區划分算法

undone

Relevant Link:

https://www.cnblogs.com/LittleHann/p/9078909.html

 

1. Label Propagation簡介

LPA是一種基於標簽傳播的局部社區划分。對於網絡中的每一個節點,在初始階段,Label Propagation算法對於每一個節點都會初始化一個唯一的一個標簽。每一次迭代都會根據與自己相連的節點所屬的標簽改變自己的標簽,更改的原則是選擇與其相連的節點中所屬標簽最多的社區標簽為自己的社區標簽,這就是標簽傳播的含義了。隨着社區標簽不斷傳播。最終,連接緊密的節點將有共同的標簽。

0x1:LPA基本思想

LPA認為每個結點的標簽應該和其大多數鄰居的標簽相同,將一個節點的鄰居節點的標簽中數量最多的標簽作為該節點自身的標簽(bagging思想)。給每個節點添加標簽(label)以代表它所屬的社區,並通過標簽的“傳播”形成同一個“社區”內部擁有同一個“標簽”。
筆者思考:

在基本思想上,LPA 和 Kmean 本質非常類似,在 LPA 的每輪迭代中,節點被歸屬於哪個社區,取決於其鄰居中累加權重最大的label(取數量最多的節點列表對應的label是weight=1時的一種特例),而 Kmeans的則是計算和當前節點“最近”的社區,將該節點歸入哪個社區。
但是這兩個算法還是有細微的區別的:

1. 首先: Kmeans是基於歐式空間計算節點向量間的距離的,而LPA則是根據節點間的“共有關系”以及“共有關系的強弱程度”來度量度量節點間的距離;
2. 第二點: Kmeasn中節點處在歐式空間中,它假設所有節點之間都存在“一定的關系”,不同的距離體現了關系的強弱。但是 LPA 中節點間只有滿足“某種共有關系”時,才存在節點間的邊,沒有共有關系的節點是完全隔斷的,計算鄰居節點的時候也不會計算整個圖結構,而是僅僅計算和該節點有邊連接的節點,從這個角度看,LPA 的這個圖結構具有更強的社區型;

0x2:LPA算法優點

LPA算法的最大的優點就是算法的邏輯非常簡單,相對於優化模塊度算法的過程是非常快的,不用pylouvain那樣的多次迭代優化過程。
LPA算法利用自身的網絡的結構指導標簽傳播,這個過程是無需任何的任何的優化函數,而且算法初始化之前是不需要知道社區的個數的,隨着算法迭代最后可以自己知道最終有多少個社區。
 
筆者思考: 其實 LPA 之所以可以做到無需開發者指定聚類的社區個數,核心原因是因為 LPA 是一個徹底的 down to top 聚類算法,其實如果對 Kmeans 稍加改造,將其初始化過程改為將所有節點都初始化為單獨的cluster,然后也進行 down to top 的聚類,Kmeasn也可以做到無需顯式指定cluster數量

0x3:LPA算法缺點

划分結果不穩定,隨機性強是這個算法致命的缺點。具體體現在:

1. 更新順序:節點標簽更新順序隨機,但是很明顯,越重要的節點越早更新會加速收斂過程;
2. 隨機選擇:如果一個節點的出現次數最大的鄰居標簽不止一個時,隨機選擇一個標簽作為自己標簽。這種隨機性可能會帶來一個雪崩效應,即剛開始一個小小的聚類錯誤會不斷被放大。不過話也說話來,如果相似鄰居節點出現多個,可能是weight計算的邏輯有問題,需要回過頭去優化weight抽象和計算邏輯;

0x4:LPA的一個簡單例子

算法初始化:a、b、c、d各自為獨立的社區;

第一輪標簽傳播:

一開始c選擇了a,因為大家的社區標簽都是一樣的,所以隨機選擇了一個;

d也根據自己周圍的鄰居節點來確定標簽數,最多的是a,所以就是d為a了;

繼續標簽傳播:以此類推,最后就全部都是a了;

Relevant Link:

https://www.jianshu.com/p/cff65d7595f9
https://arxiv.org/pdf/0709.2938.pdf
https://blog.csdn.net/Katherine_hsr/article/details/82343647
http://sighingnow.github.io/%E7%A4%BE%E4%BC%9A%E7%BD%91%E7%BB%9C/community_detection_k_means_clustering.html

 

2. LPA算法過程

0x1:算法過程描述

第一步:先給每個節點分配對應標簽,即節點1對應標簽1,節點i對應標簽i; 
第二步:遍歷N個節點(for i=1:N),找到對應節點鄰居,獲取此節點鄰居標簽,找到出現次數最大標簽,若出現次數最多標簽不止一個,則隨機選擇一個標簽替換成此節點標簽;
第三步:若本輪標簽重標記后,節點標簽不再變化(或者達到設定的最大迭代次數),則迭代停止,否則重復第二步   

0x2:邊權重計算

社區圖結構中邊的權重代表了這兩個節點之間的的“關系強弱”,這個關系的定義取決於具體的場景,例如:

1. 兩個DNS域名共享的client ip數量;
2. 兩個微博ID的共同好友數量;

0x3:標簽傳播方式

LPA標簽傳播分為兩種傳播方式,同步更新,異步更新。

1. 同步更新

同步的意思是實時,即時的意思,每個節點label更新后立即生效,其他節點在統計最近鄰社區的時候,永遠取的是當前圖結構中的最新值。

對於節點x,在第 t 輪迭代時,根據其所在節點在第t-1代的標簽進行更新。也就是

C_x(t) = f(C_{x_1}(t-1),C_{x_2}(t-1),C_{x_3}(t-1),...,C_{x_k}(t-1)),其中C_{x}(t)表示的就是節點x在第 t 次迭代時的社區標簽。

函數f表示的就是取參數節點中社區標簽最多的。

需要注意的是,這種同步更新的方法會存在一個問題,當遇到二分圖的時候,會出現標簽震盪,如下圖:

這種情況和深度學習中SGD在優化到全局最優點附近時會圍繞最優點附近進行布朗運動(震盪)的原理類似。解決的方法就是設置最大迭代次數,提前停止迭代。

2. 異步更新

異步更新方式可以理解為取了一個當前社區的快照信息,基於上一輪迭代的快照信息來進行本輪的標簽更新。

0x4: 算法代碼

1. 數據集

3列分別是:【node_out,node_in,edge_weitght】

2. 社區初始化

import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import string

def loadData(filePath):
    f = open(filePath)
    vector_dict = {}
    edge_dict = {}
    for line in f.readlines():
        lines = line.strip().split("   ")
        for i in range(2):
            if lines[i] not in vector_dict:
                vector_dict[lines[i]] = int(lines[i])
                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

if __name__ == '__main__':
    filePath = './label_data.txt'
    vector, edge = loadData(filePath)
    print(vector)
    print(edge)

初始化時,所有節點都是一個獨立的社區。

3. LPA社區聚類迭代

# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import string


def loadData(filePath):
    f = open(filePath)
    vector_dict = {}
    edge_dict = {}
    for line in f.readlines():
        lines = line.strip().split("   ")
        for i in range(2):
            if lines[i] not in vector_dict:
                vector_dict[lines[i]] = int(lines[i])
                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 = {}
    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])

        # 按照label為group維度,統計每個label的weight累加和
        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

    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
        label = get_max_community_label(vector_dict, adjacency_node_list)   # 從鄰居節點列表中選擇weight累加和最大的label
        if node_label >= label:
            continue
        else:
            return 0    #  找到weight權重累加和更大的label
    return 1


def label_propagation(vector_dict, edge_dict):
    t = 0
    print('First Label: ')
    while True:
        if (check(vector_dict, edge_dict) == 0):
            t = t + 1
            print('iteration: ', t)
            # 每輪迭代都更新一遍所有節點的社區label
            for node in vector_dict.keys():     
                adjacency_node_list = edge_dict[node]
                vector_dict[node] = get_max_community_label(vector_dict, adjacency_node_list)
        else:
            break
    return vector_dict



if __name__ == '__main__':
    filePath = './label_data.txt'
    vector, edge = loadData(filePath)
    print "load and initial the community...."
    #print(vector)
    #print(edge)
    print "start lpa clustering...."
    vector_dict = label_propagation(vector, edge)
    print "ending lpa clustering...."
    print "the finnal cluster result...."
    print(vector_dict)

    cluster_group = dict()
    for node in vector_dict.keys():
        cluster_id = vector_dict[node]
        print "cluster_id, node", cluster_id, node
        if cluster_id not in cluster_group.keys():
            cluster_group[cluster_id] = [node]
        else:
            cluster_group[cluster_id].append(node)
    

    print cluster_group

最后得到的聚類社區為:

{8: ['15', '9', '8'], 13: ['11', '10', '13', '12', '14'], 6: ['3', '7', '6'], 5: ['1', '0', '2', '5', '4']}

Relevant Link: 

https://github.com/GreenArrow2017/MachineLearning/tree/master/MachineLearning/Label%20Propagation
https://www.jianshu.com/p/cff65d7595f9

 

3. LPA算法改進思路

0x1:標簽隨機選擇改進

給節點或邊添加權重(勢函數、模塊密度優化、LeaderRank值、局部拓撲信息的相似度、標簽從屬系數等),信息熵等描述節點的傳播優先度。

這樣,在進行鄰居節點的最大標簽統計的時候,可以將鄰居節點的weight權值等作為參考因素。

0x2:標簽初始化改進

可以提取一些較為緊密的子結構來作為標簽傳播的初始標簽(例如非重疊最小極大團提取算法),或通過初始社區划分算法先確定社區的雛形再進行傳播。  

Relevant Link:

https://www.cnblogs.com/bethansy/p/6953625.html
https://blog.csdn.net/zzz24512653/article/details/26151669

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM