1. 程式人生 > >層次聚類詳解(附程式碼)

層次聚類詳解(附程式碼)

本篇部落格主要介紹機器學習演算法中的層次聚類,層次聚類不同於傳統的K-Means聚類演算法,它在初始K值和初始聚類中心點的選擇問題上會存在優勢。 

層次聚類

層次聚類就是一層一層的進行聚類,可以由下向上,或由上向下進行聚類。先計算樣本之間的距離,每次將距離最近的點合併到同一個類。然後,再計算類與類之間的距離,將距離最近的類合併為一個大類。不停的合併,直到合成了一個類。其中類與類的距離的計算方法有:最短距離法,最長距離法,中間距離法,類平均法等。

層次分解順序為:自下向上和自上向下兩種,即凝聚的層次聚類演算法和分裂的層次聚類演算法,這兩種方法沒有優劣之分,只是在實際應用的過程中根據資料的特點以及想得到的類,來考慮具體那種方法計算時所花費的時間少,資料不同時間也會相應的不同。

層次聚類的流程

凝聚型層次聚類的策略是先將每個物件作為一個簇,然後合併這些原子簇為越來越大的簇,直到所有物件都在一個簇中,或者某個終結條件被滿足。絕大多數層次聚類屬於凝聚型層次聚類,它們只是在簇間相似度的定義上有所不同。這裡給出採用最小距離的凝聚層次聚類演算法流程:

  1. 將每個物件看作一類,計算兩兩之間的最小距離;
  2. 將距離最小的兩個類合併成一個新類;
  3. 重新計算新類與所有類之間的距離;
  4. 重複2、3,直到所有類最後合併成一類;

層次聚類的優缺點 

優點:1,距離和規則的相似度容易定義,限制少;2,不需要預先制定聚類數;3,可以發現類的層次關係;4,可以聚類成其它形狀

缺點:1,計算複雜度太高;2,奇異值也能產生很大影響;3,演算法很可能聚類成鏈狀

 K值的確定

往往我們在對無target的資料進行無監督的學習時,都會用聚類演算法進行學習,但是聚類演算法確定K值是最重要也是最困難的,有時我們可以根據經驗去確定我們的資料分成幾類,但同時我們也有很多定性的方法去輔助我們具體分為幾類。

  1. 最常用最簡單的方法視覺化資料,然後觀察出聚類聚成幾類比較合適,但是在資料量很大時,往往很難觀察;
  2. 繪製出k-average with cluster distance to centroid的圖表,觀察隨著k值的增加,曲線的下降情況,當曲線不再“急劇”下降時,就是合適的k值
  3. 計算不同k值下KMeans演算法的BICAIC值,BIC或AIC值越小,選擇該k值
  4. 使用輪廓係數來確定,選擇使係數較大所對應的k值
  5. 利用層次聚類,視覺化後認為地觀察認定可聚為幾類,確定k值,確定較粗的數目,並找到一個初始聚類,然後用迭代重定位來改進該聚類。

通常第二種方法是我們會比較多使用到的方法,通過曲線去確定具體的K值;但經常會產生較好的聚類結果的一個有趣策略是,首先採用層次凝聚演算法決定結果粗的數目,並找到一個初始聚類,然後用迭代重定位來改進該聚類。 但是在使用層次聚類時需要注意幾個問題,(1)由於層次聚類的演算法時間複雜度很高,所以採取隨機抽樣的方式去進行模型的訓練,大概幾千的樣本數量就可以;(2)由於要進行聚類演算法,所以最好對所有特徵進行歸一化處理;(3)如果進行歸一化後還是要將所有的特徵放在一起進行聚類,最好可以對每個特徵進行權重的賦值,對於權重較高的特徵給予高的權重,當然也可是視所有的特徵權重都為1,但在實際應用中還是要根據實際情況和業務而定;

過程步驟程式碼參考

def gave_data(df):
    # 訓練資料
    df_train = df.iloc[:,1:]
    # 隨機抽取樣本,從大樣本中每次隨機抽取小樣本
    sample_df = df_train.sample(n=1000)
    # 歸一化處理,對聚類資料進行歸一化處理
    min_max_scaler = preprocessing.MinMaxScaler()
    X_minMax = min_max_scaler.fit_transform(sample_df)
    # 一類一標籤
    # labelture = [i for i in range(sample_df.shape[0])]
    # 統一標籤
    labelture = [0 for i in range(sample_df.shape[0])]
    return X_minMax, labelture

 這裡我們通過pandas讀取一個csv檔案成DF,我們對資料進行隨機抽樣,此處我們只隨機抽樣1000條樣本,再對這1000條樣本進行歸一化處理,這裡沒有對每一個特徵進行權重處理,此處預設每一個特徵的特徵權重都為1,這裡我們返回一個處理過的資料集X_minMax和資料標籤label,此處預設label統一都為0。

def tree(X, labelture):
    # row_clusters = linkage(pdist(df, metric='euclidean'), method='complete')  # 使用抽祕籍距離矩陣
    row_clusters = linkage(X, method='complete', metric='euclidean')
    print (pd.DataFrame(row_clusters, columns=['row label1', 'row label2', 'distance', 'no. of items in clust.'],
                        index=['cluster %d' % (i + 1) for i in range(row_clusters.shape[0])]))

    # 層次聚類樹
    row_dendr = dendrogram(row_clusters, labels=labelture)
    plt.tight_layout()
    plt.ylabel('Euclidean distance')
    plt.show()

這裡我們通過此段程式碼來繪製分層樹,用到的引數即為上面得到的資料集X_minMax和標籤label。

通過上圖的分層樹可以發現,大概這1000個樣本可以分為4類

所以對於整個資料集,我們第一次分類可先讓K=4,進行聚類,隨後迭代得到最優的K值。

def cluster_test(data):
# 傳統聚類
	print "------------------------------------"
# 歸一化處理
	min_max_scaler = preprocessing.MinMaxScaler()
	X_minMax = min_max_scaler.fit_transform(data)
	print X_minMax[10:,:]
	kmean = KMeans(n_clusters=4, random_state=5)
	kmean.fit(X_minMax)
	# batch_size = 45
	# mbk = MiniBatchKMeans(n_clusters=10, batch_size=batch_size,n_init=10,         
                            max_no_improvement=10, verbose=0).score().fit(df_shop)
	print kmean.labels_
	print pd.Series(kmean.labels_).value_counts()
	print metrics.calinski_harabaz_score(X_minMax, kmean.labels_)
	label = pd.Series(kmean.labels_)
	data["label"] = label
	print "--------------------------------------"

最後我們用粗略的K值進行K-Means進行聚類(也可以令K=5、6,進行多次聚類,對比聚類效果),最終將得到的label儲存下來。