Label propagation是基於標傳播的一種社區劃分算法。Label Propagation Algorithm簡稱LPA算法,也能夠是說是一種劃分小團體的算法。這種社區劃分的方法有不少,LPA只是一種最簡單的一種。好比,以微博爲例,用戶在微博上能夠關注感興趣的人,一樣也會被其餘人關注,這樣用戶和用戶之間就存在了關係,使用LPA就能夠對用戶進行聚類操做,相同興趣點的用戶能夠聚類在一塊兒,劃分一塊兒以後就能夠統一進行推薦了,這樣就能夠用LPA。 ###社區劃分 社區結構指的就是在網絡中由一些節點構成的特定分組,在同一個分組內的節點經過節點,之間的鏈接邊緊密的鏈接在一塊兒,而在分組和分組之間,其鏈接比較鬆散,稱每個分組就是一個社區。由上就能夠知道社區是網絡中節點的集合,這些節點內部鏈接較爲緊密而外部鏈接較爲稀疏。 在一個社區網絡中每個用戶其實就是一個節點,用戶之間經過互相關注關係構成了用戶之間的社交關係,用戶之間經過轉發感興趣的東西,從而就構成了用戶之間的興趣關係。經過將不一樣用戶劃分到不一樣的社區,使得每個社區是都有不一樣的屬性,好比興趣,領域等等。而在兩個興趣之間的關係相對來講就比較弱一些的就被分紅了兩個社區,這兩個社區之間的相對鏈接會較爲稀疏。好比:node
黑圈裏面的相對鏈接比較稠密,因此能夠做爲一個社區。能夠看到整個網絡被分紅了3個圈,各個社區之間的聯繫就比較稠密。 ###社區劃分算法 假設在一個網絡裏面,每個樣本只能是屬於一個社區的,那麼這樣的問題就稱爲非重疊社區劃分。**在非重疊社區劃分算法裏面,有不少的方法:①基於模塊度優化的社區劃分。②基於譜分析的社區劃分算法。③基於信息論的社區劃分算法。④基於標籤傳播的社區劃分算法。**比較經典的應該算是基於模塊度優化的社區劃分算法了,其基本思想是將社區劃分問題轉換成了模塊度函數的優化,而模塊度是對社區劃分算法結果的一個很重要的衡量標準。在實際求解的過程當中是不能直接求解的,因此一般是採用近似解法, 根據求解方法不一樣能夠分爲如下幾種方法:①凝聚方法,經過不斷合併不一樣社區,實現對整個網絡的社區劃分,典型的方法有Newman快速算法,CNM算法和MSG-MV算法。②分裂方法,經過不斷的刪除網絡的邊來實現對整個網絡的社區劃分,典型的方法有GN算法。③直接近似求解模塊度函數,經過優化算法直接對模塊度函數進行求解,典型的方法有EO算法。 ###社區劃分的評價標準 按照不一樣的劃分算法,社區劃分的結果可能會存在不少種,因此每一種社區劃分的質量都是不同的,爲了能夠度量社區劃分的質量,因而便使用了模塊度的衡量標準。模塊度是在 區間上的一個實數,經過比較每個社區內部的鏈接密度和社區與社區直接的鏈接密度,去度量社區劃分的質量。若將鏈接比較稠密的點劃分在一個社區中,這樣模塊度的值就會變大。最後模塊度最大的一種劃分方法就是最優的劃分方法。 ###Label Propagation Algorithm LPA是一種基於標籤傳播的局部社區劃分。對於網絡中的每個節點,在初始階段,Label Propagation算法對於每個節點都會初始化一個惟一的一個標籤。每一次迭代都會根據與本身相連的節點所屬的標籤改變本身的標籤,更改的原則是選擇與其相連的節點中所屬標籤最多的社區標籤爲本身的社區標籤,這就是標籤傳播的含義了。隨着社區標籤不斷傳播。最終,鏈接緊密的節點將有共同的標籤。 LPA算法的最大的優勢就是算法的邏輯很是簡單,相對於優化模塊度算法的過程是很是快的。LPA算法利用自身的網絡的結構指導標籤傳播,這個過程是無需任何的任何的優化函數,並且算法初始化以前是不須要知道社區的個數的,隨着算法迭代最後能夠本身知道最終由多少個社區。 假設對於一個節點 ,其相鄰節點爲 ,對於每個節點,都有其對應的標籤,標籤表明的是該節點所屬的社區。在算法迭代的過程當中,節點 根據其鄰居節點更新所屬的社區。好比: 一開始c選擇了a,由於你們的社區標籤都是同樣的,因此隨機選擇了一個,d也根據本身周圍的鄰居節點來肯定標籤數,最多的是a,因此就是d爲a了,以此類推,最後就所有都是a了。 ###標籤傳播 和上訴的更新過程是相似的,標籤傳播也分兩種傳播方式,同步更新,異步更新。 同步更新:對於節點,在第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))$$其中表示的就是節點在第t代時的社區標籤。函數表示的就是取參數節點中社區標籤最多的。可是問題來了,這種同步更新的方法會存在一個問題,會出現標籤震盪。 雖然結果影響不是很是大,可是對於這種不斷的震盪終歸是很差的。並且很難中止,由於中止的條件就是當社區標籤再也不變化的時候終止。 異步更新:對於異步更新方式$$C_x(t) = f(C_{x_1}(t),C_{x_2}(t),C_{x_3}(t),...,C_{x_k}(t))$$這個算法就不會出現剛剛的標籤震盪的狀況了。 對於他們之間的權重關係,其實就是他們之間的重要程度,也就是用戶之間通訊的頻繁程度,好比密友和陌生人之間權重確定不同,還有他們之間的互動程度也不同。因此若是沒有給定權重,那就只能強算了。$$w_{ij} = exp(-\frac{|x_i-x_j|^2}{\alpha^2})$$使用高斯距離來獲得權重。以後就是權重相加,看看哪個權重大了。 ###算法迭代終止條件 當網絡中的每個節點所屬的社區再也不改變的時候迭代就能夠中止了,對於每個節點,在其全部的鄰居節點所屬當前社區中,其所屬的社區標籤是最大的,即:若是用 來表示社區標籤, 表示節點i全部鄰居節點中社區標籤爲 的個數,則算法終止條件爲:對於每個節點 ,若是節點i的社區標籤爲 ,則:$$d_i^{C_m}>= d_j^{C_j}$$這樣就能夠得到最終的社區。可是社區不是惟一的。 ###代碼實現 前面兩個是鏈接的點,最後一個是權重。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])
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]
複製代碼
獲得鄰居節點最多的社區標籤數。git
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])
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]
複製代碼
檢查是否到結束條件了。github
def check(vector_dict, edge_dict):
for node in vector_dict.keys():
adjacency_node_list = edge_dict[node]
node_label = vector_dict[node]
label = get_max_community_label(vector_dict, adjacency_node_list)
if node_label >= label:
continue
else:
return 0
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)
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
複製代碼
最後效果: bash
###附上GitHub代碼:github.com/GreenArrow2…網絡