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

0. 社區劃分簡介

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

在一個網絡裏面,每個樣本只能是屬於一個社區的,那麼這樣的問題就稱爲非重疊社區劃分。html

在非重疊社區劃分算法裏面,有不少的方法node

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

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

模塊度函數在實際求解中沒法直接計算獲得全局最優解析解(相似深度神經網絡對應的複雜高維非線性函數),因此一般是採用近似解法,根據求解方法不一樣能夠分爲如下幾種方法:github

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的則是計算和當前節點「最近」的社區,將該節點納入哪一個社區。
可是這兩個算法仍是有細微的區別的:app

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
相關文章
相關標籤/搜索