1. 聚類
K-means算法是一種常用的聚類算法,所謂的聚類就是指給定個樣本的數據集,需要構造 個簇(類),使得這 個簇之間的關係最小。
2. K-means算法基本步驟
- 隨機初始化個點,作爲聚類中心
- 在第次迭代中,對於每個樣本點,選取距離最近的聚類中心,歸爲該類
- 遍歷一遍之後,更新聚類中心,其中更新規則爲:聚類中心取當前類的平均值
- 重複步驟2、3,直到滿足迭代次數,或者聚類狀態不發生改變
3. 算法優化
3.1 輪廓係數
輪廓係數(Silhouette Coefficient)結合了聚類的凝聚度(Cohesion)和分離度(Separation),用於評估聚類的效果。該值處於之間,值越大,表示聚類效果越好。具體計算方法如下:
- 對於每個樣本點,計算點與其同一個簇內的所有其他元素距離的平均值,記作,用於量化簇內的凝聚度。
- 選取i外的一個簇,計算與中所有點的平均距離,遍歷所有其他簇,找到最近的這個平均距離,記作,即爲的鄰居類,用於量化簇之間分離度。
- 對於樣本點 ,輪廓係數
- 計算所有i的輪廓係數,求出平均值即爲當前聚類的整體輪廓係數,度量數據聚類的緊密程度
3.2 K值的選擇
方法一:輪廓係數法
可以通過枚舉的方法,令 從 到 (隨便的一個固定值,但不能太大),對於每一個 值進行 K-means 算法,然後取輪廓係數最大的那個 作爲最終聚類的簇的數目。
方法二:誤差平方和法
定義誤差平方和公式如下:
其中 是第 個簇,其中 是 簇 的樣本, 是第 個簇的聚類中心
隨着聚類數 的增大,樣本劃分會更加精細,每個簇的聚合程度會逐漸提高,那麼誤差平方和SSE自然會逐漸變小。並且,當k小於真實聚類數時,由於的增大會大幅增加每個簇的聚合程度,故SSE的下降幅度會很大,而當到達真實聚類數時,再增加所得到的聚合程度回報會迅速變小,所以SSE的下降幅度會驟減,然後隨着值的繼續增大而趨於平緩,也就是說SSE和的關係圖是一個手肘的形狀,而這個肘部對應的值就是數據的真實聚類數。–轉自手肘法理論
3.3 初始點的選擇
- 從給定的樣本集合中隨機選擇一個樣本作爲第一個聚類中心
- 然後對於樣本集合中的每一個樣本點 ,計算離該樣本與最近的聚類中心的距離
- 概率的選擇D(x)較大的點作爲新的樣本中心
- 重複步驟2和3,直到聚類中心達到 個爲止
4. 代碼
以鳶尾花數據爲例,下面是k-means的代碼
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
# 加載數據
def loadData():
iris = load_iris()
#print(iris)
X = np.array(iris.data[:])
#print(X.shape)
# 以鳶尾花後兩個特徵作爲聚類特徵
# plt.scatter(X[:, 2], X[:, 3], c="red", marker='o', label='two features')
# plt.xlabel('petal length')
# plt.ylabel('petal width')
# plt.legend(loc=2)
# plt.show()
return X[:,2:]
# 第一次隨機獲取k箇中心點
def getCenterPoints(X, k):
m, n = X.shape
centerPoints = np.zeros((k, n))
for i in range(k):
id = int(np.random.uniform(0, m))
centerPoints[i, :] = X[id, :]
return centerPoints
#得到兩個數據點之間的歐氏距離
def getDis(x, y):
return np.sqrt(np.sum((x - y) **2 ))
# k-means算法
def KMeans(X, k):
m = X.shape[0]
cluster = np.mat(np.zeros((m, 2)))
#1. 隨機獲取中心點
centerPoints = getCenterPoints(X, k)
for times in range(100):
for i in range(m):
minDist, minID = 1000000.0, -1
for j in range(k):
dis = getDis(X[i, :], centerPoints[j, :])
if dis < minDist:
minDist, minID = dis, j
#2.對於每個樣本點,選取最近的中心點,歸爲該類
cluster[i, :] = minID, minDist**2
#3. 更新中心點
for i in range(k):
pointsInCluster = X[np.nonzero(cluster[:, 0].A == i)[0]] # 獲取簇類所有的點
centerPoints[i, :] = np.mean(pointsInCluster, axis=0) # 對矩陣的行求均值
return centerPoints, cluster
#對聚類好的數據進行畫圖
def showCluster(X, k, cluster, centerPoints):
m = X.shape[0]
mark = ['or', 'ob', 'og']
for i in range(m):
markIndex = int(cluster[i, 0])
plt.plot(X[i, 0], X[i, 1], mark[markIndex])
mark = ['Dr', 'Db', 'Dg']
# 繪製中心點
for i in range(k):
plt.plot(centerPoints[i, 0], centerPoints[i, 1], mark[i])
plt.show()
def main():
X, k = loadData(), 3
centerPoints, cluster = KMeans(X, k)
showCluster(X, k, cluster, centerPoints)
if __name__ == '__main__':
main()
分類效果: