机器学习-Kmeans聚类
Kmeans聚類
簡介
之前提到的算法大多數都是監督學習算法,主要代表是分類和回歸問題,但是很多時候想要從數據中發現一些潛在的規律,而數據是沒有指標驅動的,即沒有標簽,這種自學習算法稱為非監督學習(Unsupervised Learning)。非監督學習算法的典型代表是聚類(Clustering),分類問題是給定x和y,要求找到一種假設擬合數據的分布,將不同的類別區分開,而聚類問題則是只有x,從中發現一些規律將樣本分成區別較大的多個簇。聚類的應用較分類在業界更為廣泛,因為標注成本大且易錯,最為典型的聚類算法是Kmeans算法。
原理
K-means是一種給定聚類數目k的聚類算法,它通過迭代不斷調整聚類中心達到聚類的目的,由于聚類中心的調整是基于當前簇的均值決定的,故叫做k均值聚類算法。下面以樣本都是二維特征的數據為例(方便可視化),詳述算法思路。
上述的Kmeans算法存在一些問題,如k個聚類中心的初始化,一旦出現極端情況將很難更新(所有樣本離某個中心都是最近,其他中心無法形成簇)。
首先來看Kmeans的優化目標,記c(i)c^{(i)}c(i)為第i個樣本的所屬簇(類別)的編號,μk\mu_kμk?表示第k個簇的聚類中心。那么優化的目標函數就如下式所示。
J(c(1),…,c(m),μ1,…,μK)=1m∑i=1m∥x(i)?μc(i)∥2J\left(c^{(1)}, \ldots, c^{(m)}, \mu_{1}, \ldots, \mu_{K}\right)=\frac{1}{m} \sum_{i=1}^{m}\left\|x^{(i)}-\mu_{c^{(i)}}\right\|^{2} J(c(1),…,c(m),μ1?,…,μK?)=m1?i=1∑m?∥∥∥?x(i)?μc(i)?∥∥∥?2
優化函數即為如下,只要找到使得J最小的ccc和μ\muμ即找到了最優解。
min?c(1),…,c(m)μ(1),…,μ(m)J(c(1),…,c(m),μ1,…,μK)\min _{c^{(1)}, \ldots, c^{(m)} \\ \mu^{(1)}, \ldots, \mu^{(m)}} J\left(c^{(1)}, \ldots, c^{(m)}, \mu_{1}, \ldots, \mu_{K}\right) c(1),…,c(m)μ(1),…,μ(m)min?J(c(1),…,c(m),μ1?,…,μK?)
算法優化
上述的算法原理其實并不是很復雜,但是該算法的應用會出現很多問題,如之前提到的聚類中心初始化,不合適的初始化很容易使得算法收斂到局部最優解,這不是我們希望看到的。
比較常用且有效的聚類中心初始化方法是隨機挑選k個訓練樣本作為聚類初始中心,這種方法叫做隨機初始化(random initialization)。
當然,即使隨機初始化也可能陷入局部最優,因此多次嘗試隨機初始化,綜合多次聚類結果會是不錯的選擇,綜合的方法一般是選擇使得代價函數J最小的聚類參數。
還有一個比較值得研究的問題就是如何確定聚類的簇數目K,這引起了較多的研究也產生了一些方法,但是最實用的其實還是人工選擇。這是因為,當數據的特征維度較大時,一方面可視化困難,另一方面不同的人對數據的理解不同,所以設計一個自動選擇k值的算法是非常困難的。
曾今,肘部法則被使用過,它的思路是繪制代價函數值J關于K的函數,會出現一個明顯的拐點,這個點對應的K值就是選擇的K值(下圖左)。但是,很多時候并不會出現這個拐點,因此難以選出合適的K。所以更加常用的實際上是任務驅動的選擇,怎樣的K對Kmeans下游任務更加有效,就選這個K。
聚類實戰
實戰基于鳶尾花數據集,其中的標簽列不作為訓練數據,且選擇兩列作為特征,方便二維可視化。
數據集的真實分布如下圖,可以感受到,由于數據的確實,二維特征區分度不明顯,聚類還是比較難的。
實戰的源碼如下。
""" Author: Zhou Chen Date: 2019/12/9 Desc: 簡單實現Kmeans聚類算法 """ import numpy as np from matplotlib import pyplot as plt import random from sklearn.datasets import load_iris plt.style.use('fivethirtyeight')def load_data():data, target = load_iris()['data'], load_iris()['target']# 選取前兩列特征,方便可視化return data[:, :2], targetdef plot_data(x, y):plt.scatter(x[:, 0], x[:, 1], c=y)plt.savefig('rst.png')plt.show()def rand_centroids(data, k):m = data.shape[0]# 隨機選擇k個樣本作為初始化的聚類中心sample_index = random.sample(list(range(m)), k)centroids = data[sample_index]# 循環遍歷特征值return centroidsdef compute_dist(vecA, vecB):return np.linalg.norm(vecA - vecB)def kMeans(data, k):m, n = np.shape(data) # 樣本量和特征量labels = np.array(np.zeros((m, 2)))centroids = rand_centroids(data, k)cluster_changed = True # 是否已經收斂while cluster_changed:cluster_changed = Falsefor i in range(m):min_dist = np.infmin_index = -1for j in range(k):distJI = compute_dist(centroids[j, :], data[i, :])if distJI < min_dist:min_dist = distJImin_index = jif labels[i, 0] != min_index:cluster_changed = Truelabels[i, :] = min_index, min_dist ** 2for cent in range(k):ptsInClust = data[np.nonzero(labels[:, 0] == cent)[0]]centroids[cent, :] = np.mean(ptsInClust, axis=0)# 返回所有的類質心與點分配結果即類別return centroids, labelsif __name__ == '__main__':data, target = load_data()# plot_data(data, target)_, label = kMeans(data, 3)plot_data(data, label[:, 0])最終的聚類效果如下圖,可以看出,和原來的類別比較,聚類效果較為不錯。
補充說明
本文簡單敘述了Kmeans這一聚類模型的簡單思想,思路參照吳恩達的機器學習課程(Coursera),除此以外后來產生了很多更為優秀的聚類算法,后續會介紹。
- 本系列相關的博文和代碼開放于Github,歡迎訪問項目。同時博客也同步在我的個人博客網站,歡迎訪問查看其他文章。
- 由于能力和時間有限,如有錯誤,歡迎評論指正。
總結
以上是生活随笔為你收集整理的机器学习-Kmeans聚类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Xmanager远程桌面教程
- 下一篇: Numpy实现BP神经网络(包含Drop