一.前述
Kmeans演算法一般在資料分析前期使用,選取適當的k,將資料分類後,然後分類研究不同聚類下資料的特點。
Kmeans演算法是一種無監督的演算法。
常用於分組,比如使用者偏好。
二.概念及原理
Kmeans原理:
1 隨機選取k箇中心點
2 遍歷所有資料,將每個資料劃分到最近的中心點中
3 計算每個聚類的平均值,並作為新的中心點
4 重複2-3,直到這k箇中線點不再變化(收斂了),或執行了足夠多的迭代。
樣本點之間的相似度距離計算:
1.歐氏距離相似度(常用!!!)
2.Jaccard相似度(用於比較樣本之間的相似性,係數值越大表示值越高)
3.餘弦相似度
餘弦相似度值在[-1,1]之間,值越趨近於1代表越相近,越趨近於-1,代表越相反,接近於0,代表兩個向量正交。
常用計算文字相似性!!!將兩個文字根據他們詞,建立兩個向量,計算這兩個向量的餘弦值,就可以知道兩個文字在統計學方法中他們的相似度情況。
4.Pearson相似度
5.相對熵(K-L距離)
總結:
對於高維空間點之間的度量,用歐氏距離。
對於集合度量Jaccard相似度。
對於自然語言處理用餘弦相似度。
對於函式度量用Pearson相似度
目標函式:
考慮歐幾里得距離的資料,使用誤差平方和(Sum of the Squared Error,SSE)作為聚類的目標函式,兩次執行K均值產生的兩個不同的簇集,我們更喜歡SSE最小的那個。
k表示k個聚類中心,ci表示第幾個中心,dist表示的是歐幾里得距離。
這裡有一個問題就是為什麼,我們更新質心是讓所有的點的平均值,這裡就是SSE所決定的。
通俗來說,每個點計算 到所在分配的中心店的距離,然後加和。
隨著k的增長損失函式,逐漸遞減。肘部法就是計算不同的K,然後計算SSE,求最大的兩組即可。
選擇一開始下降速度快,後來下降速度慢的。
肘部法圖解:
聚類評價方法:
常見的聚類評測指標有純度和 F 值,其中 F 值更為常用。
F 值的更普適的應用是資訊檢索的結果,其計算包括了兩個指標:召回率(Recall Rate)和準確率(Precision Rate)。
- 召回率的定義為:檢索出的相關文件數和文件庫中所有的相關文件數的比率,衡量的是檢索系統的查全率;
- 準確率的定義為:檢索出相關文件數與檢索出的文件總數的比率,衡量的是檢索系統的查準率;F 值為兩者的調和平均值。
以上評估方法都是基於原始資料事先知道一定的標籤,但這種似乎並不太常用,所以常用的評估模型的指標是輪廓係數,適用於在一開始不知道標籤的情況下。
具體如下:
輪廓係數它結合內聚度和分離度兩種因素。可以用來在相同原始資料的基礎上用來評價不同演算法、或者演算法不同執行方式對聚類結果所產生的影響。
方法:
1,計算樣本i到同簇其他樣本的平均距離ai。ai 越小,說明樣本i越應該被聚類到該簇。將ai 稱為樣本i的簇內不相似度。
簇C中所有樣本的a i 均值稱為簇C的簇不相似度。
2,計算樣本i到其他某簇Cj 的所有樣本的平均距離bij,稱為樣本i與簇Cj 的不相似度。定義為樣本i的簇間不相似度:bi =min{bi1, bi2, ..., bik}
bi越大,說明樣本i越不屬於其他簇。
3,根據樣本i的簇內不相似度a i 和簇間不相似度b i ,定義樣本i的輪廓係數:
4,判斷:
si接近1,則說明樣本i聚類合理;
si接近-1,則說明樣本i更應該分類到另外的簇;
若si 近似為0,則說明樣本i在兩個簇的邊界上。
結果為負數則可以直接放棄了!!!說明分類的很不好。
二分Kmeans原理:
為了得到k個簇,將所有點的集合分裂成兩個簇,從這些簇中選取一個繼續分裂,如此下去,直到產生k個簇。
比如要分成5個組,第一次分裂產生2個組,然後從這2個組中選一個目標函式產生的誤差比較大的,分裂這個組產生2個,這樣加上開始那1個就有3個組了,然後再從這3個組裡選一個分裂,產生4個組,重複此過程,產生5個組。這算是一中基本求精的思想。二分k均值不太受初始化的困擾,因為它執行了多次二分試驗並選取具有最小誤差的試驗結果,還因為每步只有兩個質心。
Kmeans++原理:
k-means++演算法選擇初始seeds的基本思想就是:初始的聚類中心之間的相互距離要儘可能的遠。
- 從輸入的資料點集合中隨機選擇一個點作為第一個聚類中心
- 對於資料集中的每一個點x,計算它與聚類中心(指已選擇的聚類中心)的距離D(x),然後對於每一個點/總和得出一個概率,則第二個點依據概率進行選擇。第三個點的選擇是其他所有的點到前兩個點的距離然後加和,再計算每一個點/總和的概率,再選擇一箇中心店。以此類推。。。。。
- 選擇一個新的資料點作為新的聚類中心,選擇的原則是:D(x)較大的點,被選取作為聚類中心的概率較大(概率化選擇)
利用Canopy計算初始類簇中心點。
不需要迭代,比較快
步驟:
1.首先定義兩個距離T1和T2,T1>T2.從初始的點的集合S中隨機移除一個點P,然後對於還在S中的每個點I,計算該點I與點P的距離。
2.如果距離小於T1,則將點I加入到點P所代表的Canopy中,如果距離小於T2,則將點I從集合S中移除,並將點I加入到點P所代表的Canopy中。
3.迭代完一次之後,重新從集合S中隨機選擇一個點作為新的點P,然後重複執行以上步驟。
圖示:
總結:與中心的距離大於T1時,這些點就不會被歸入到中心所在的這個canopy類中。然當距離小於T1大於T2時,這些點會被歸入到該中心所在的canopy中,但是它們並不會從D中被移除,也就是說,它們將會參與到下一輪的聚類過程中,成為新的canopy類的中心或者成員。亦即,兩個Canopy類中有些成員是重疊的。而當距離小於T2的時候,這些點就會被歸入到該中心的canopy類中,而且會從D中被移除,也就是不會參加下一次的聚類過程了。
三、程式碼
程式碼一:Kmeans實現
# !/usr/bin/python # -*- coding:utf-8 -*- import numpy as np import matplotlib.pyplot as plt import sklearn.datasets as ds import matplotlib.colors from sklearn.cluster import KMeans def expand(a, b): d = (b - a) * 0.1 return a-d, b+d if __name__ == "__main__": N = 400#400個資料點 centers = 4#4箇中心店 data, y = ds.make_blobs(N, n_features=2, centers=centers, random_state=2)#聚類測試點 建立高沙分佈的點 data2, y2 = ds.make_blobs(N, n_features=2, centers=centers, cluster_std=(1, 2.5, 0.5, 2), random_state=2)#每個類別的標準差不一樣,u是正態分佈,胖瘦 data3 = np.vstack((data[y == 0][:], data[y == 1][:50], data[y == 2][:20], data[y == 3][:5]))#垂直放 每個類別生成一百個資料 每個類別拿的資料不一樣。目的是每一個簇裡面的密度不一樣,看看聚類效果。 y3 = np.array([0] * 100 + [1] * 50 + [2] * 20 + [3] * 5)#自己構建label cls = KMeans(n_clusters=4, init='k-means++') y_hat = cls.fit_predict(data) y2_hat = cls.fit_predict(data2) y3_hat = cls.fit_predict(data3) m = np.array(((1, 1), (1, 3)))#矩陣點乘 ,相當於旋轉 data_r = data.dot(m) y_r_hat = cls.fit_predict(data_r) matplotlib.rcParams['font.sans-serif'] = [u'SimHei'] matplotlib.rcParams['axes.unicode_minus'] = False cm = matplotlib.colors.ListedColormap(list('rgbm')) plt.figure(figsize=(9, 10), facecolor='w') plt.subplot(421) plt.title(u'原始資料') plt.scatter(data[:, 0], data[:, 1], c=y, s=30, cmap=cm, edgecolors='none')#座標軸 x1_min, x2_min = np.min(data, axis=0) x1_max, x2_max = np.max(data, axis=0) x1_min, x1_max = expand(x1_min, x1_max) x2_min, x2_max = expand(x2_min, x2_max) plt.xlim((x1_min, x1_max)) plt.ylim((x2_min, x2_max)) plt.grid(True) plt.subplot(422) plt.title(u'KMeans++聚類') plt.scatter(data[:, 0], data[:, 1], c=y_hat, s=30, cmap=cm, edgecolors='none') plt.xlim((x1_min, x1_max)) plt.ylim((x2_min, x2_max)) plt.grid(True) plt.subplot(423) plt.title(u'旋轉後資料') plt.scatter(data_r[:, 0], data_r[:, 1], c=y, s=30, cmap=cm, edgecolors='none') x1_min, x2_min = np.min(data_r, axis=0) x1_max, x2_max = np.max(data_r, axis=0) x1_min, x1_max = expand(x1_min, x1_max) x2_min, x2_max = expand(x2_min, x2_max) plt.xlim((x1_min, x1_max)) plt.ylim((x2_min, x2_max)) plt.grid(True) plt.subplot(424) plt.title(u'旋轉後KMeans++聚類') plt.scatter(data_r[:, 0], data_r[:, 1], c=y_r_hat, s=30, cmap=cm, edgecolors='none') plt.xlim((x1_min, x1_max)) plt.ylim((x2_min, x2_max)) plt.grid(True) plt.subplot(425) plt.title(u'方差不相等資料') plt.scatter(data2[:, 0], data2[:, 1], c=y2, s=30, cmap=cm, edgecolors='none') x1_min, x2_min = np.min(data2, axis=0) x1_max, x2_max = np.max(data2, axis=0) x1_min, x1_max = expand(x1_min, x1_max) x2_min, x2_max = expand(x2_min, x2_max) plt.xlim((x1_min, x1_max)) plt.ylim((x2_min, x2_max)) plt.grid(True) plt.subplot(426) plt.title(u'方差不相等KMeans++聚類') plt.scatter(data2[:, 0], data2[:, 1], c=y2_hat, s=30, cmap=cm, edgecolors='none') plt.xlim((x1_min, x1_max)) plt.ylim((x2_min, x2_max)) plt.grid(True) plt.subplot(427) plt.title(u'數量不相等資料') plt.scatter(data3[:, 0], data3[:, 1], s=30, c=y3, cmap=cm, edgecolors='none') x1_min, x2_min = np.min(data3, axis=0) x1_max, x2_max = np.max(data3, axis=0) x1_min, x1_max = expand(x1_min, x1_max) x2_min, x2_max = expand(x2_min, x2_max) plt.xlim((x1_min, x1_max)) plt.ylim((x2_min, x2_max)) plt.grid(True) plt.subplot(428) plt.title(u'數量不相等KMeans++聚類') plt.scatter(data3[:, 0], data3[:, 1], c=y3_hat, s=30, cmap=cm, edgecolors='none') plt.xlim((x1_min, x1_max)) plt.ylim((x2_min, x2_max)) plt.grid(True) plt.tight_layout(2, rect=(0, 0, 1, 0.97)) plt.suptitle(u'資料分佈對KMeans聚類的影響', fontsize=18) # https://github.com/matplotlib/matplotlib/issues/829 # plt.subplots_adjust(top=0.92) # plt.show() plt.savefig('cluster_kmeans')
結果:
程式碼二:評估指標
# !/usr/bin/python # -*- coding:utf-8 -*- from sklearn import metrics#評估 if __name__ == "__main__": y = [0, 0, 0, 1, 1, 1] y_hat = [0, 0, 1, 1, 2, 2] h = metrics.homogeneity_score(y, y_hat) c = metrics.completeness_score(y, y_hat) print(u'同一性(Homogeneity):', h) print(u'完整性(Completeness):', c) v2 = 2 * c * h / (c + h) v = metrics.v_measure_score(y, y_hat) print(u'V-Measure:', v2, v) y = [0, 0, 0, 1, 1, 1] y_hat = [0, 0, 1, 3, 3, 3] h = metrics.homogeneity_score(y, y_hat) c = metrics.completeness_score(y, y_hat) v = metrics.v_measure_score(y, y_hat) print(u'同一性(Homogeneity):', h) print(u'完整性(Completeness):', c) print(u'V-Measure:', v) # 允許不同值 y = [0, 0, 0, 1, 1, 1] y_hat = [1, 1, 1, 0, 0, 0] h = metrics.homogeneity_score(y, y_hat) c = metrics.completeness_score(y, y_hat) v = metrics.v_measure_score(y, y_hat) print(u'同一性(Homogeneity):', h) print(u'完整性(Completeness):', c) print(u'V-Measure:', v) y = [0, 0, 1, 1] y_hat = [0, 1, 0, 1] ari = metrics.adjusted_rand_score(y, y_hat) print(ari) y = [0, 0, 0, 1, 1, 1] y_hat = [0, 0, 1, 1, 2, 2] ari = metrics.adjusted_rand_score(y, y_hat) print(ari)
均一性,一個簇中只包含一個類別樣本,Precision
完整性,同類別樣本被歸到同一個簇中,Recall
V-Measure: