前言
人們常說“物以類聚,人以群分”,在生物學中也對生物從界門綱目科屬種中進行了划分。在統計學中,也有聚類分析法
,通過把相似的對象通過靜態分類的方法分成不同的組別或者更多的子集,從而讓同一個子集中的成員都有相似的一些屬性,然后對這些子集中的數據進行分析,其關鍵則在於聚類。這系列文章將來講講各種聚類方法,這篇開篇文章將介紹下聚類的相關概念以及最基本的算法 K-Means。
聚類
我們都知道,在機器學習中,一般分為有監督、無監督、半監督學習三類。其中無監督學習常用的方法便是聚類。
將一個數據集分為多類后,每一類又稱為簇,同一簇中的樣本盡可能的相似,而不同簇中的樣本盡可能不同。即具有高類內相似性和低類間相似性的特點。
聚類的方法大致可分為兩種
- 分區(Partitional algorithms)
- 基於原型:K-Means,GMM等
- 基於密度:DBACAN,MeanShift等
- 分層(Hierarchical algorithms)
- 自頂向下
- 自底向上
這里,就不禁產生一個疑問,我們以什么為標准進行聚類?這也就涉及到了相似度的問題,即我們如何判斷兩個樣本數據是不是相似的?
如果數據特征只有二維,那我們把數據中的點放置在二維坐標系中,如果屬於一類,那這些點肯定是會離得比較近,這個近實際上就是我們的相似度度量標准,即距離。那當特征維數增加,在超平面中來划分類,自然也可以通過距離來度量相似性。
常見的距離
- Minkowski 距離
- \(D_{mk}(x,z)=(\sum_{i=1}^{n}|x_i-z_i|^p)^{\frac{1}{p}}\)
- 當 \(p=2\) 時,為歐氏距離 \(D_{ed}(x,z)=||x-z||=\sqrt{\sum_{i=1}^{n}|x_i-z_i|^2}\)
- 當 \(p=1\) 時,為曼哈頓距離(城市距離) \(D_{man}(x,z)=||x-z||_1=\sum_{i=1}^{n}|x_i-z_i|\)
- 若 \(p=+\infty\) 時,為 sup 距離 \(D_{sup}=||x-z||_{\infty}=max_{i=1}^{n}|x_i-z_i|\)
- Hamming 距離
- 當特征為二值特征時,Minkowski 距離又被稱為 Hamming 距離
- 皮爾森相關系數
- \(S_p(x,z)=\frac{\sum_{i=1}^{n}(x_i-\bar{x})(z_i-\bar{z})}{\sqrt{\sum_{i=1}^{n}(x_i-\bar{x})^2 \times \sum_{i=1}^{n}(z_i-\bar{z})^2}}\)
- 余弦距離
- \(S_c(x,z)=\frac{X^Tz}{||x||||z||}\)
K-Means
算法流程
K-Means 算法很好理解,首先需要指定聚類簇的個數 K。隨機設定 K 個聚類中心 \(\mu_1,\mu_2,...,\mu_K \in \mathbb{R}^n\) ,然后不斷迭代更新這些中心點,直到收斂:
-
for i=1 to m
計算 \(x^{(i)}\) 距離最近的聚類簇的中心,將其作為 \(x^{(i)}\) 的類別,即 \(y^{(i)}=\arg\min_k{||x^{(i)}-\mu_k||^2}\)
-
for k=1 to K
更新聚類簇的中心,用所有屬於第 k 個簇的樣本的均值去更新 \(\mu_k\) ,即 \(\mu_k=avg(x^{(i)}|y^{(i)}=k)\)
從上面的介紹可以看出來, K-Means 的目標函數為
K 值選取
之前提到過 K 值是需要自己設定的,那么,我們要如何才能取到合適的 K 值呢?(之前面試給問到這個問題,當時一時間懵了。。。面試官說是可以算出來的一般選擇在 \(\sqrt{\frac{n}{2}}\) 附近做調整,沒試過真不太清楚)
一般有兩種方式,分別是手肘法
和輪廓系數法
手肘法
采用指標 SSE(sum of the sqared errors,誤差平方和)
其中,\(C_i\) 表示第 i 個簇,p為 \(C_i\) 中的樣本點,\(\mu_i\) 為 \(C_i\) 的質心。
其核心思想是隨着聚類數 k 的增大,樣本划分不斷精細,SSE 會不斷減小。當 k 小於真實聚類樹時,k 的增大會大幅度增加每個簇的聚合程度,SSE 的下降幅度會驟減,然后隨着 k 值得繼續增大而趨於平緩。
輪廓系數法
該方法的核心指標時輪廓系數(silhouette Coefficient),某個樣本點 \(x_i\) 的輪廓系數定義為
其中,a 是 \(x_i\) 與同簇的其它樣本的平均距離,稱為凝聚度,b 是 \(x_i\) 與最近簇中所有樣本的平均距離,稱為分離度。最近簇的定義為
其中 p 是某個簇 \(C_k\) 中的樣本。
求出所有樣本的輪廓系數之后再求平均值就得到了平均輪廓系數,一般來說,簇內樣本的距離越近,簇間樣本距離越遠,平均平均輪廓系數越大,效果越好。
代碼實現
#隨機初始化centroids
def kMeansInitCentroids(X, K):
"""
隨機初始化centroids
:param X: 訓練樣本
:param K: 聚類簇個數
:return: 初始化的centroids
"""
np.random.seed(5)
i = np.random.randint(0, len(X), K)
centroids = X[i, :]
return centroids
def findClosestCentroids(X, centroids):
"""
尋找每個樣本離之最近的centroid
:param X: 訓練集
:param centroids:聚類簇中心
:return: 索引集
"""
K = centroids.shape[0]
m = X.shape[0]
index = np.zeros((m))
for i in range(m):
dis = np.sum(np.power(X[i, :] - centroids, 2), axis=1)
index[i] = np.argmin(dis)
return index
def computeCentroids(X, index, K):
"""
更新聚類簇中心
:param X: 訓練集
:param index: 索引集
:param K: 聚類簇個數
:return: 更新的聚類簇中心
"""
[m, n] = X.shape
centroids = np.zeros((K, n))
for i in range(K):
idx = np.where(index==i)
centroids[i, :] = np.mean(X[idx, :], axis=1)
return centroids
centroids = kMeansInitCentroids(X, K)
l = 10 # 迭代次數
for i in range(l):
#計算索引集index
index = findClosestCentroids(X, centroids)
#更新centroids
centroids = computeCentroids(X, index, K)
結果圖
聚類中心移動