本文結(jié)構(gòu)安排
圖聚類簡(jiǎn)介
正則化割
Louvain
非負(fù)矩陣分解(NMF)
其他常見方法
圖(graph):是一種由點(diǎn)和邊集構(gòu)成的結(jié)構(gòu)
圖聚類(graph clustering) : 將點(diǎn)劃分為不同的簇,使得簇內(nèi)的邊盡量多,簇之間的邊盡量少。也稱為圖劃分(partitioning),社區(qū)檢查(community detection)
應(yīng)用Louvain算法產(chǎn)生的社區(qū)檢測(cè)圖示

Normalized cut
正則化割的基本原理是使各簇之間的割最小,但不是算其最小割,因?yàn)檫@會(huì)使相對(duì)孤立的邊緣點(diǎn)“自成一團(tuán)”,造成社區(qū)大小的不均衡。算法的基本過程的前半部分類似于譜聚類,先由度矩陣和鄰接矩陣,計(jì)算出拉普拉斯矩陣,得到第二小到K+1小的特征向量,對(duì)其進(jìn)行K-means聚類,預(yù)處理得到k’個(gè)簇。之后,計(jì)算兩兩簇合并之后,計(jì)算其正則化割,選擇正則化割最小的兩個(gè)簇合并。每次合并減小一個(gè)簇,直到減小到K個(gè)簇。
k-路劃分:
(1)計(jì)算相似度矩陣W和度矩陣D
(2)計(jì)算標(biāo)準(zhǔn)化拉普拉斯矩陣
(3)從第二小的特征值開始找個(gè)最小的特征值對(duì)應(yīng)的特征向量構(gòu)造
維度的特征矩陣F
(4)對(duì)特征矩陣F按行進(jìn)行標(biāo)準(zhǔn)化后,進(jìn)行Kmeans聚類得到簇
(5)在這個(gè)簇中,每次選取兩個(gè)簇進(jìn)行合并,直到最后剩下k個(gè)簇,選取的策略是最小化Ncut時(shí)的合并組合

Louvain
Louvain算法的基本原理也是采用合并的策略,但是它合并的標(biāo)準(zhǔn)是模塊度增益。首先將每個(gè)節(jié)點(diǎn)初始化為不同社區(qū),計(jì)算將節(jié)點(diǎn)加入其鄰居社區(qū)的模塊度增益△Q,選擇使模塊度增益最大的鄰居進(jìn)行合并,合并后的社區(qū)看做一個(gè)新的節(jié)點(diǎn),直到兩兩社區(qū)合并的模塊度增益都不大于0,則停止合并。

Louvain算法步驟如下:
(1)初始化每個(gè)數(shù)據(jù)點(diǎn)為一個(gè)社區(qū);
(2)對(duì)每個(gè)數(shù)據(jù)點(diǎn),嘗試加入其鄰居所在的社區(qū),計(jì)算比較加入前后的模塊度增益ΔQ,選出增益最大的那個(gè)鄰居社區(qū),若其對(duì)應(yīng)的增益ΔQ>0,則該數(shù)據(jù)點(diǎn)加入這個(gè)社區(qū),否則不改變其原來社區(qū)劃分;
(3)將得到的社區(qū)視為一個(gè)節(jié)點(diǎn),社區(qū)內(nèi)節(jié)點(diǎn)之間邊權(quán)重轉(zhuǎn)化為新節(jié)點(diǎn)環(huán)的權(quán)重,社區(qū)間的邊權(quán)重轉(zhuǎn)化為新節(jié)點(diǎn)間的邊權(quán)重;
(4)重復(fù)(2)(3)步驟,直至滿足收斂條件。
收斂條件可以是迭代了一定的次數(shù),亦或是模塊度不再增加。
NMF

NMF的基本原理是將原始矩陣分解得到社區(qū)指示矩陣和基矩陣,隨機(jī)初始化這兩個(gè)矩陣的元素,迭代更新這兩個(gè)矩陣,更新規(guī)則如下:
最小化目標(biāo)函數(shù):

停止條件是目標(biāo)函數(shù)收斂,即上一個(gè)計(jì)算的目標(biāo)函數(shù)值,與本次目標(biāo)函數(shù)值的差小于一個(gè)設(shè)定的閾值。
而對(duì)于目標(biāo)函數(shù)收斂后得到的U,我們可以根據(jù)其每行(對(duì)應(yīng)每個(gè)數(shù)據(jù)點(diǎn))的最大值對(duì)應(yīng)的列數(shù),得到該數(shù)據(jù)點(diǎn)對(duì)應(yīng)的類標(biāo)(即將它分配給這個(gè)社區(qū)),故而可以得到如下NMF算法的算法步驟:
(1)初始化U、V矩陣(非負(fù))
(2)根據(jù)U、V的更新公式異步迭代更新
(3)滿足終止條件后終止更新U、V
(4)找到U中每個(gè)數(shù)據(jù)點(diǎn)最大值對(duì)應(yīng)的列數(shù),分配其作為類標(biāo)
LPA
標(biāo)簽傳播算法(LPA)的做法比較簡(jiǎn)單:
第一步: 為所有節(jié)點(diǎn)指定一個(gè)唯一的標(biāo)簽;
第二步: 逐輪刷新所有節(jié)點(diǎn)的標(biāo)簽,直到達(dá)到收斂要求為止。對(duì)于每一輪刷新,節(jié)點(diǎn)標(biāo)簽刷新的規(guī)則如下:
對(duì)于某一個(gè)節(jié)點(diǎn),考察其所有鄰居節(jié)點(diǎn)的標(biāo)簽,并進(jìn)行統(tǒng)計(jì),將出現(xiàn)個(gè)數(shù)最多的那個(gè)標(biāo)簽賦給當(dāng)前節(jié)點(diǎn)。當(dāng)個(gè)數(shù)最多的標(biāo)簽不唯一時(shí),隨機(jī)選一個(gè)。
KeyCode
Normalized cut
構(gòu)建相似度矩陣W和計(jì)算度矩陣D;由于有每個(gè)節(jié)點(diǎn)的特征向量feature vector,故而節(jié)點(diǎn)之間的相似性可以通過對(duì)應(yīng)特征向量的余弦距離度量出來,由于數(shù)據(jù)集是.mat文件,所以我使用matlab生成相似度矩陣和高斯距離矩陣后再作為Java Code的輸入。
= zeros(length(A),length(A));
for i = 1:length(A)
for j = 1:i-1
W(i,j) = 1-pdist2(F(i,:),F(j,:),'cosine');
W(j,i) = W(i,j);
end
end
public void calculateW(){
this.W=DenseMatrix.Factory.zeros(nsamples, nsamples);
for (int p=0;p<nsamples;p++){
for (int q=0;q<nsamples;q++){
this.W.getAsDouble(CosineSimilarity(p,q));
}
}
}
計(jì)算標(biāo)準(zhǔn)化拉普拉斯矩陣,由度矩陣D和相似度矩陣W可得拉普拉斯矩陣L再進(jìn)行標(biāo)準(zhǔn)化
Matrix I = DenseMatrix.Factory.eye(this.nsamples,this.nsamples);
Matrix D = DenseMatrix.Factory.eye(this.nsamples,this.nsamples);
for (int i=0;i<this.nsamples;i++){
Double wij = 0.0;
for (int j=0;j<this.nsamples;j++)
{
wij+=this.W.getAsDouble(i,j);
}
D.setAsDouble(wij,i,i);
}
this.L = D.minus(this.W);
構(gòu)建特征矩陣;計(jì)算標(biāo)準(zhǔn)化拉普拉斯矩陣的特征值和特征向量,從第二小的特征值開始選取k′個(gè)最小的特征值對(duì)應(yīng)的特征向量組成特征矩陣:
Matrix[] eigenValueDecompostion = this.L.eig();
Matrix eigenVector = eigenValueDecompostion[0];
Matrix eigenValue = eigenValueDecompostion[1];
List<Matrix> res = eigenVector.getColumnList();
List<Matrix> NCutResult=new ArrayList<>();
for (int i=1;i<this.k+1;i++){
NCutResult.add(res.get(i));
}
對(duì)特征矩陣進(jìn)行聚類,不斷選取兩個(gè)簇合并直至剩下k個(gè)簇,選取哪兩個(gè)簇進(jìn)行合并取決于 此時(shí)哪兩個(gè)簇合并得到的Ncut值最小.
KMeans NCutkMeans = new KMeans(this.k,NCut);
NCutkMeans.clustering();
Louvain
初始化每個(gè)數(shù)據(jù)點(diǎn)為一個(gè)社區(qū):
initSingletonClusters();
public void initSingletonClusters()
{
int i;
clustercount = nodecount;
cluster = new int[nodecount];
for (i= 0; i < nodecount; i++)
cluster[i] = i;
}
對(duì)每個(gè)數(shù)據(jù)點(diǎn),嘗試加入其鄰居所在的社區(qū),計(jì)算比較加入前后的模塊度增益ΔQ,記錄最大的模塊度增益和它對(duì)應(yīng)的社區(qū)
,若最大增益max>0,則將該數(shù)據(jù)點(diǎn)加入對(duì)應(yīng)的社區(qū)
:特別地,這里的數(shù)據(jù)點(diǎn)既指單個(gè)節(jié)點(diǎn),也指一個(gè)社區(qū),故統(tǒng)一按社區(qū)視為數(shù)據(jù)點(diǎn)來處理,只是單個(gè)數(shù)據(jù)點(diǎn)算只有一個(gè)節(jié)點(diǎn)的社區(qū)。
public void communityDetection(){
boolean update=true;
Set<Integer> cur_set = new TreeSet<>();
for (int i=0;i<diGraph.getV();i++){
cur_set.add(diGraph.adjList.get(i).getCluserlabel());
}
String current = "";
String last_set = "";
for (int i=0;i<diGraph.getV();i++){
current+=String.valueOf(diGraph.adjList.get(i).getCluserlabel());
}
while (update){
for (Integer i:cur_set){
double bestdeltaQ=Double.NEGATIVE_INFINITY;
int bestcluster = i;//initial
for (Integer j:cur_set){
if (i!=j){
Set<Integer> c1 = diGraph.getClusterList(i);//Cluster C1 vertex_id_set
Set<Integer> c2 = diGraph.getClusterList(j);//Cluster C2 vertex_id_set
if (c1.size()!=0 && c2.size()!=0) {
double deltaQ = calculateDeltaQ(c1, c2, i, j);//calculate DeltaQ
if (deltaQ > bestdeltaQ) {
bestdeltaQ = deltaQ;
bestcluster = j;
}
}
}
}
if (bestdeltaQ > 0){
diGraph.adjList.get(i).setCluserlabel(bestcluster);//change clusterlabel
}
else {
diGraph.adjList.get(i).setCluserlabel(i);//keep clusterlabel
}
diGraph.clearVisited();//clear all
}
last_set = current;//judge algorithm converge or not
cur_set.clear();
current="";
for (int i=0;i<this.diGraph.getV();i++){
cur_set.add(this.diGraph.adjList.get(i).getCluserlabel());
}
for (int i=0;i<diGraph.getV();i++){
current+=String.valueOf(diGraph.adjList.get(i).getCluserlabel());
}
if (current.equals(last_set)){
update = false;
}
}
String filePath = "D:\\DataMining\\lab2\\"+this.name+"_Louvain.csv";
FileReaderFunc.writeListToFile(filePath,CommunityLabel);
}
計(jì)算模塊度增益的函數(shù)

public double calculateDeltaQ(Set<Integer> c1,Set<Integer> c2,int startlabel,int endlabel) {
double m=((double) diGraph.getE());
double k_i_in = diGraph.getClusterWeightIn(c1,c2,startlabel,endlabel);
double sigma_in = diGraph.getIn(c2);
double total = diGraph.getClusterWeightTotal(endlabel);
double k_i = diGraph.getClusterWeightTotal(startlabel);
double tmp1 = (sigma_in+k_i_in)/(2.0*m);
double tmp2 = (total+k_i)/(2.0*m);
double tmp3 = sigma_in/(2.0*m);
double tmp4 = total/(2.0*m);
double tmp5 = k_i/(2.0*m);
double deltaQ=tmp1-tmp2*tmp2-tmp3+tmp4*tmp4+tmp5*tmp5;
return deltaQ;
}
NMF
根據(jù)迭代更新公式寫代碼:

public void communityDetection() {
Matrix fenzi = DenseMatrix.Factory.zeros(this.N,this.K);
Matrix fenmu = DenseMatrix.Factory.zeros(this.N,this.K);
Matrix DU = DenseMatrix.Factory.zeros(this.N,this.K);
Matrix DV = DenseMatrix.Factory.zeros(this.N,this.K);
Matrix Tmp = this.U.mtimes(this.V.transpose());
Matrix E = this.A.minus(Tmp);
double cur_error = E.norm2();
double last_error = 0.0;
for (int it = 0; it < this.maxiterator ; it++){
fenzi = this.A.mtimes(this.V);
fenmu = this.U.mtimes(this.V.transpose());
fenmu = fenmu.mtimes(this.V);
DU = fenzi.divide(fenmu);
this.U = this.U.times(DU);
fenzi = this.A.transpose().mtimes(this.U);
fenmu = this.V.mtimes(this.U.transpose());
fenmu = fenmu.mtimes(this.U);
fenmu = fenmu.plus(this.lambda);
DV = fenzi.divide(fenmu);
this.V = this.V.times(DV);
last_error=cur_error;
Matrix Tmp1 = this.U.mtimes(this.V.transpose());
Matrix E1 = this.A.minus(Tmp1);
cur_error = E1.norm2();
if (Math.abs(cur_error-last_error)<0.00001){
break;
}
}
}
Experiments on Datasets
說明:相似度矩陣使用屬性矩陣構(gòu)造的余弦相似度
高斯距離矩陣使用全連接度量模式下的高斯核函數(shù)




Algorithm Comparison and Hyperparameter
Normalized cut
所謂Clustering,就是說聚類,把一堆東西(合理地)分成兩份或者K份。從數(shù)學(xué)上來說,
聚類的問題就相當(dāng)于Graph Partition的問題,即給定一個(gè)圖G = (V, E),如何把它的頂點(diǎn)集劃分為不相交的子集,
使得這種劃分最好。譜聚類算法的主要優(yōu)點(diǎn)有:譜聚類只需要數(shù)據(jù)之間的相似度矩陣,因此對(duì)于處理稀疏數(shù)據(jù)的聚類很有效。這點(diǎn)傳統(tǒng)聚類算法比如K-Means很難做到。由于使用了降維,因此在處理高維數(shù)據(jù)聚類時(shí)的復(fù)雜度比傳統(tǒng)聚類算法好。譜聚類算法的主要缺點(diǎn)有:如果最終聚類的維度非常高,則由于降維的幅度不夠,譜聚類的運(yùn)行速度和最后的聚類效果均不好。聚類效果依賴于相似矩陣,不同的相似矩陣得到的最終聚類效果可能很不同。
Louvain
Louvain算法是一種基于多層次優(yōu)化Modularity的算法,它的優(yōu)點(diǎn)是快速、準(zhǔn)確,是性能最好的社區(qū)發(fā)現(xiàn)算法之一。Modularity函數(shù)最初被用于衡量社區(qū)發(fā)現(xiàn)算法結(jié)果的質(zhì)量,它能夠刻畫發(fā)現(xiàn)的社區(qū)的緊密程度。那么既然能刻畫社區(qū)的緊密程度,也就能夠被用來當(dāng)作一個(gè)優(yōu)化函數(shù),即將結(jié)點(diǎn)加入它的某個(gè)鄰居所在的社區(qū)中,如果能夠提升當(dāng)前社區(qū)結(jié)構(gòu)的modularity。Louvain不斷地遍歷網(wǎng)絡(luò)中的結(jié)點(diǎn),嘗試將單個(gè)結(jié)點(diǎn)加入能夠使modularity提升最大的社區(qū)中,直到所有結(jié)點(diǎn)都不再變化,并且將一個(gè)個(gè)小的社區(qū)歸并為一個(gè)超結(jié)點(diǎn)來重新構(gòu)造網(wǎng)絡(luò),這時(shí)邊的權(quán)重為兩個(gè)結(jié)點(diǎn)內(nèi)所有原始結(jié)點(diǎn)的邊權(quán)重之和。一直迭代直至算法穩(wěn)定。Louvain可以利用modularity的特性,決定是否進(jìn)行合并,相比于譜聚類——無論是否合理都進(jìn)行分割,Louvain的使用更加靈活,算法進(jìn)行時(shí)不需要設(shè)置具體的社區(qū)個(gè)數(shù),社區(qū)的個(gè)數(shù)由社區(qū)自己的性質(zhì)決定。但是也可以在合并的過程中加入終止條件或者是別的約束,以滿足固定的社區(qū)個(gè)數(shù)。
NMF
NMF(非負(fù)矩陣分解)對(duì)原始矩陣的重構(gòu)誤差最小化,且原始數(shù)據(jù)的統(tǒng)計(jì)信息也可以得到保持。NMF在處理聚類問題上有以下幾個(gè)方面的優(yōu)勢(shì):
- 非負(fù)性,由于NMF中分解得到的簇劃分子矩陣具有非負(fù)性,只需要找到數(shù)據(jù)在哪個(gè)簇中的值最大就可以確定該數(shù)據(jù)的簇標(biāo)簽。非負(fù)矩陣分解的非負(fù)約束性使其能夠清晰的指出數(shù)據(jù)點(diǎn)的簇標(biāo)簽。
- 易構(gòu)性,根據(jù)不同的數(shù)據(jù)和目標(biāo),可以構(gòu)造與之匹配的非負(fù)矩陣分解方法。只需要根據(jù)原始數(shù)據(jù)構(gòu)造出原始矩陣,接著只需要加入具體的約束即可開始訓(xùn)練。
- 易于優(yōu)化,因?yàn)榉秦?fù)矩陣分解的方法目標(biāo)公式類似,所有優(yōu)化的方式比較容易套用。
劣勢(shì):由于非負(fù)矩陣分解處理輸入和輸出全是矩陣形式的,當(dāng)數(shù)據(jù)量過大時(shí),很容易出現(xiàn)內(nèi)存消耗過大甚至超出運(yùn)行內(nèi)存報(bào)錯(cuò)的情況,可以往這幾個(gè)方面解決。采取縮減方案——多層圖劃分,采取劃分方案——主成分分析或k均值預(yù)聚類,遞增方案——隨機(jī)優(yōu)化非負(fù)矩陣分解方法,采取分布式并行實(shí)施方案。
LPA
標(biāo)簽傳播算法是不重疊社區(qū)發(fā)現(xiàn)的經(jīng)典算法,其基本思想是:將一個(gè)節(jié)點(diǎn)的鄰居節(jié)點(diǎn)的標(biāo)簽中數(shù)量最多的標(biāo)簽作為該節(jié)點(diǎn)自身的標(biāo)簽。給每個(gè)節(jié)點(diǎn)添加標(biāo)簽(label)以代表它所屬的社區(qū),并通過標(biāo)簽的“傳播”形成同一標(biāo)簽的“社區(qū)”結(jié)構(gòu)。給每個(gè)節(jié)點(diǎn)添加標(biāo)簽(label)以代表它所屬的社區(qū),并通過標(biāo)簽的“傳播”形成同一標(biāo)簽的“社區(qū)”結(jié)構(gòu)。一個(gè)節(jié)點(diǎn)的標(biāo)簽取決于它鄰居節(jié)點(diǎn)的標(biāo)簽:假設(shè)節(jié)點(diǎn)z的鄰居節(jié)點(diǎn)有z1至zk,那么哪個(gè)社區(qū)包含z的鄰居節(jié)點(diǎn)最多z就屬于那個(gè)社區(qū)(或者說z的鄰居中包含哪個(gè)社區(qū)的標(biāo)簽最多,z就屬于哪個(gè)社區(qū))。優(yōu)點(diǎn)是收斂周期短,無需任何先驗(yàn)參數(shù)(不需事先指定社區(qū)個(gè)數(shù)和大小),算法執(zhí)行過程中不需要計(jì)算任何社區(qū)指標(biāo)。
時(shí)間復(fù)雜度接近線性:對(duì)頂點(diǎn)分配標(biāo)簽的復(fù)雜度為O(n),每次迭代時(shí)間為O(m),找出所有社區(qū)的復(fù)雜度為O(n+m),但迭代次數(shù)難以估計(jì)。