kmeans 中k值一直是個令人頭疼的問題,這里提出幾種優化策略。
手肘法
核心思想
1. 肉眼評價聚類好壞是看每類樣本是否緊湊,稱之為聚合程度;
2. 類別數越大,樣本划分越精細,聚合程度越高,當類別數為樣本數時,一個樣本一個類,聚合程度最高;
3. 當k小於真實類別數時,隨着k的增大,聚合程度顯著提高,當k大於真實類別數時,隨着k的增大,聚合程度緩慢提升;
4. 大幅提升與緩慢提升的臨界是個肘點;
5. 評價聚合程度的數學指標類似 mse,均方差,是每個類別的樣本與該類中心的距離平方和比上樣本數;
示例代碼
import numpy as np import matplotlib.pyplot as plt from matplotlib.font_manager import FontProperties from sklearn.cluster import KMeans from scipy.spatial.distance import cdist # 1 數據可視化 cluster1 = np.random.uniform(0.5, 1.5, (2, 10)) cluster2 = np.random.uniform(3.5, 4.5, (2, 10)) X = np.hstack((cluster1, cluster2)).T plt.figure() plt.axis([0, 5, 0, 5]) plt.grid(True) plt.plot(X[:, 0], X[:, 1], 'k.') plt.show() # 2 肘部法求最佳K值 K = range(1, 10) mean_distortions = [] for k in K: kmeans = KMeans(n_clusters=k) kmeans.fit(X) mean_distortions.append( sum( np.min( cdist(X, kmeans.cluster_centers_, metric='euclidean'), axis=1)) / X.shape[0]) plt.plot(K, mean_distortions, 'bx-') plt.xlabel('k') font = FontProperties(fname=r'c:\windows\fonts\msyh.ttc', size=20) plt.ylabel(u'平均畸變程度', fontproperties=font) plt.title(u'用肘部法確定最佳的K值', fontproperties=font) plt.show()
輸出手肘圖
可以明顯看出紅色圓圈是個肘點。
缺點
1. 不是所有的數據都能呈現這樣明顯的肘點;
2. 單純地以數據選擇k值,可能脫離實際;
補充
在實際任務中,我們可能根據業務來確定 k 值,如區分男女,k=2,區分人種,k=3,黃黑白;
輪廓系數法
結合類內聚合度和類間分離度來評價聚類效果。
計算方法
1. 計算樣本 i 到同簇內其他樣本的平均距離 ai;【ai越小,說明該樣本越應該被分到該簇,故可將 ai 視為簇內不相似度】
2. 計算簇內所有樣本的 ai;
3. 計算樣本 i 到其他簇內所有樣本的平均距離 bi,並取min;【bi 視為 i 的類間不相似度,bi為i到其他類的所有bi中min,bi越大,越不屬於其他類】
4. 樣本 i 的簇內不相似度 ai 和類間不相似度 bi,計算輪廓系數
□ s_i 越接近1, 則說明樣本 i 聚類合理。
□ s_i 越接近-1,說明樣本 i 更適合聚到其他類
□ s_i 越接近0,則說明樣本 i 在兩個簇的邊界上
這種方法計算量大,視情況使用。
參考資料:
https://blog.csdn.net/xiligey1/article/details/82457271
https://www.jianshu.com/p/f2b3a66188f1