1. 程式人生 > >Canopy聚類演算法分析

Canopy聚類演算法分析

Canopy聚類演算法是可以並行執行的演算法,資料並行意味著可以多執行緒進行,加快聚類速度,開源ML庫Mahout使用。

一、概念

與傳統的聚類演算法(比如 K-means )不同,Canopy 聚類最大的特點是不需要事先指定 k 值( 即 clustering 的個數),因此具有很大的實際應用價值。與其他聚類演算法相比,Canopy聚類雖然精度較低,但其在速度上有很大優勢,因此可以使用 Canopy 聚類先對資料進行“粗”聚類,(摘自於Mahout一書:Canopy演算法是一種快速地聚類技術,只需一次遍歷資料集得到結果,無法給出精確的簇結果,但能給出最優的簇數量。可為K均值演算法優化超引數..K....

)得到 k 值後再使用 K-means 進行進一步“細”聚類。這種Canopy + K-means的混合聚類方式分為以下兩步:

Step1、聚類最耗費計算的地方是計算物件相似性的時候,Canopy 聚類在第一階段選擇簡單、計算代價較低的方法計算物件相似性,將相似的物件放在一個子集中,這個子集被叫做Canopy ,通過一系列計算得到若干Canopy,Canopy 之間可以是重疊的,但不會存在某個物件不屬於任何Canopy的情況,可以把這一階段看做資料預處理;

Step2、在各個Canopy 內使用傳統的聚類方法(如K-means),不屬於同一Canopy 的物件之間不進行相似性計算。

從這個方法起碼可以看出兩點好處:首先,Canopy 不要太大且Canopy 之間重疊的不要太多的話會大大減少後續需要計算相似性的物件的個數;其次,類似於K-means這樣的聚類方法是需要人為指出K的值的,通過Stage1得到的Canopy 個數完全可以作為這個K值,一定程度上減少了選擇K的盲目性。

二、聚類精度

對傳統聚類來說,例如K-means、Expectation-Maximization、Greedy Agglomerative Clustering,某個物件與Cluster的相似性是該點到Cluster中心的距離,那麼聚類精度能夠被很好保證的條件是:對於每個Cluster都存在一個Canopy,它包含所有屬於這個Cluster的元素。

如果這種相似性的度量為當前點與某個Cluster中離的最近的點的距離,那麼聚類精度能夠被很好保證的條件是:對於每個Cluster都存在若干個Canopy,這些Canopy之間由Cluster中的元素連線(重疊的部分包含Cluster中的元素)。

資料集的Canopy劃分完成後,類似於下圖:



三、Canopy演算法流程

(1)將資料集向量化得到一個list後放入記憶體,選擇兩個距離閾值:T1和T2,其中T1 > T2,對應上圖,實線圈為T1,虛線圈為T2,T1和T2的值可以用交叉校驗來確定;

(2)從list中任取一點P,用低計算成本方法快速計算點P與所有Canopy之間的距離(如果當前不存在Canopy,則把點P作為一個Canopy),如果點P與某個Canopy距離在T1以內,則將點P加入到這個Canopy;

(3)如果點P曾經與某個Canopy的距離在T2以內,則需要把點P從list中刪除,這一步是認為點P此時與這個Canopy已經夠近了,因此它不可以再做其它Canopy的中心了;

(4)重複步驟2、3,直到list為空結束。 

注意:Canopy聚類不要求指定簇中心的個數,中心的個數僅僅依賴於距離度量,T1和T2的選擇。

Python程式碼:

#-*- coding:utf-8 -*-   
''''' 
'''  
import numpy as np  
import matplotlib as nlp  
  
#The first op  
import scipy as sp    
import scipy.sparse.linalg    
import time    
from Old_regression import crossValidation  
  
#使用K均值  
import kMeans as km  
  
def canopyClustering(datalist):  
    state =[];  
    #交叉驗證獲取T1和T2;  
    T1,T2 = crossValidation(datalist);  
    #canopy 預聚類  
    canopybins= canopy(datalist, T1 , T2);  
    #使用K均值聚類  
    k =len(canopybins);  
    createCent = [canopy[0] for canopy in canopybins];#獲取canopybins中心  
    dataSet    = datalist;  
    centroids, clusterAssment =km.kMeans(dataSet, k, distMeas=distEclud, createCent);  
    return clusterAssment;  
  
#得到一個list後放入記憶體,選擇兩個距離閾值:T1和T2,其中T1 > T2  
#Canopy聚類不要求指定簇中心的個數,中心的個數僅僅依賴於舉例度量,T1和T2的選擇。  
def canopy(datalist, T1 , T2):  
    #state  = [];datalist  = [];  
    #初始化第一個canopy元素  
    canopyInit = datalist.pop();  
    canopyCenter= calCanopyCenter([canopyInit] );  
      
    canopyC = [canopyInit];#建立第一個canopy  
    canopybins = [];  
    canopybins.append([canopyCenter,canopyC ] );  
      
    while not(len(datalist) ==0 ):  
        PointNow =datalist[len(datalist)-1 ];#PointNow =datalist.pop();  
        counter = 0;  
        for canopy in canopybins:  
            dis =calDis(PointNow, canopy[0]);  
            #如果點P與某個Canopy距離在T1以內,則將點P加入到這個Canopy;  
            if dis<T1:  
                canopy[1].append(PointNow);  
                counter +=1;  
                #break;  
            if dis<T2:  
                #點P曾經與某個Canopy的距離在T2以內,則需要把點P從list中刪除,  
                #這一步是認為點P此時與這個Canopy已經夠近了,因此它不可以再做其它Canopy的中心了  
                if not(counter ==0):#保證必須率屬於一個canopy  
                    del list[len(datalist)-1 ];  
                    break;  
                else:#建立一個新的Canopy  
                    canopyC = [PointNow];  
                    canopyCenter= PointNow;  
                    canopybins.append([canopyCenter,canopyC ] );  
                     
    return canopybins;  
  
def calDis(va,vb):  
    dis =0;  
    for i in range(len(va) ):  
        dis += va[i]*va[i]+ vb[i]*vb[i];  
    return dis;  
  
#計算canopy中心  
def calCanopyCenter(datalist):  
    center =datalist[0];  
    for i in len(range(center) ):  
        center[i]=0;  
          
    for data in datalist:  
        center +=data;  
    center /= len(center);  
      
    return center;