算法原理
模型
已知一組有n個樣本的數據集
其中,每個樣本都有個特徵,沒有標籤。現在的目的是希望利用樣本特徵將數據集分爲k類。
算法步驟
首先要明確一些概念:類是指根據特徵的不同將數據分成不同的類別。而不同數據樣本組成的集合稱爲簇。
Step1、已知樣本數據集,數據包含特徵,樣本間的距離採用歐式距離,聚類數設定爲k,計算前需要先對數據進行標準化。之後隨機選取k個樣本點,令這k個樣本分別作爲k個簇。
Step2、計算每個簇的中心向量
其中,表示第個簇中的樣本個數,表示第個簇中第個樣本的第個特徵。通過上式可得到k箇中心向量,之後計算樣本數據集中每個樣本與中心向量的歐式距離,每個樣本歸屬於與它距離最小的某個中心向量所在的簇。
Step3、將所有樣本分爲k個簇後,重複第2步,直至k箇中心向量不再改變。
程序實現
kmeans算法包含3個函數,分別是計算中心向量即均值向量,劃分數據樣本集,劃分主函數。
計算均值函數
def cal_mean_vector(x):
"""
參數
----
x:已分類的樣本組,類型:list, shape:[ndarray]:{n_samples, n_features}
返回
----
x_:均值向量,類型:ndarray, shape:{n_samples, n_features}
"""
res = []
for i in range(len(x)):
res.append(x[i].mean(axis=0))
return np.array(res)
劃分數據集函數
def split_vector(x, v):
"""根據中心向量對數據集進行分組
參數
----
x:特徵向量,類型:ndarray, shape:{n_samples, n_features}
v:中心向量,類型:ndarray, shape:{n_samples, n_features}
返回
----
按順序進行分類的特徵向量組
"""
label_dis = []
# 計算每個樣本與每個中心向量的距離,並存儲每個樣本的最小中心向量索引
for i in range(len(x)):
distance = np.sum((x[i]-v)**2, axis=1)
label_dis.append(np.argmin(distance))
label_dis = np.array(label_dis)
# 以每個中心向量爲單位,存儲數據樣本
split_vector = []
for i in range(len(v)):
split_vector.append(x[label_dis==i])
return split_vector
kmeans主函數
def kmeans(x, k):
"""
參數
----
x:特徵向量,類型:ndarray, shape:{n_samples, n_features}
k:聚類個數,類型:int
返回
----
x_:已分類的樣本組,類型:list,shape:[ndarray]:{n_samples, n_features}
v_:最終的中心向量,類型:ndarray,shape:{n_samples, n_features}
"""
# 隨機選取k個向量
index = np.random.choice(len(x), k, replace=False)
# 選取中心向量
v = x[index]
while 1:
# 根據中心向量將數據集劃分爲若干組
x_ = split_vector(x, v)
v_ = cal_mean_vector(x_)
if v_.tolist()==v.tolist():
return x_,v_
v = v_
演示
原始分類
color = ['red','yellow','blue']
for i in range(3):
plt.scatter(X[y==i,1], X[y==i,2], color=color[i])
聚類結果
X_test = kmeans(X,3)[0]
for i in range(3):
plt.scatter(X_test[i][:,1], X_test[i][:,2], color=color[i])
封裝成一個類
class KMeans:
def __init__(self, k=5,):
self.k = k
# 計算均值向量
def cal_mean_vector(self, x):
"""
參數
----
x:已分類的樣本組,類型:list, shape:[ndarray]:{n_samples, n_features}
返回
----
x_:均值向量,類型:ndarray, shape:{n_samples, n_features}
"""
res = []
for i in range(len(x)):
res.append(x[i].mean(axis=0))
return np.array(res)
# 劃分特徵向量
def split_vector(self, x, v):
"""根據中心向量對數據集進行分組
參數
----
x:特徵向量,類型:ndarray, shape:{n_samples, n_features}
v:中心向量,類型:ndarray, shape:{n_samples, n_features}
返回
----
按順序進行分類的特徵向量組
"""
label_dis = []
# 計算每個樣本與每個中心向量的距離,並存儲每個樣本的最小中心向量索引
for i in range(len(x)):
distance = np.sum((x[i]-v)**2, axis=1)
label_dis.append(np.argmin(distance))
label_dis = np.array(label_dis)
# 以每個中心向量爲單位,存儲數據樣本
split_vector = []
for i in range(len(v)):
split_vector.append(x[label_dis==i])
return split_vector
# kmeans算法主函數
def fit(self, x):
"""
參數
----
x:特徵向量,類型:ndarray, shape:{n_samples, n_features}
----
x_:已分類的樣本組,類型:list,shape:[ndarray]:{n_samples, n_features}
v_:最終的中心向量,類型:ndarray,shape:{n_samples, n_features}
"""
# 隨機選取k個向量
index = np.random.choice(len(x), self.k, replace=False)
# 選取中心向量
v = x[index]
while 1:
# 根據中心向量將數據集劃分爲若干組
x_ = self.split_vector(x, v)
v_ = self.cal_mean_vector(x_)
if v_.tolist()==v.tolist():
self.x_ = x_
self.v_ = v_
return
v = v_
def transform(self, x):
return self.split_vector(x, self.v_)
實例化演示
clf = KMeans(k=3)
clf.fit(X)
X_test_2 = clf.transform(X)
for i in range(3):
plt.scatter(X_test_2[i][:,1], X_test_2[i][:,2], color=color[i])
以上便是kmeans聚類算法的代碼實現,如有不對不妥之處,歡迎交流批評改正。
參考資料
周志華《機器學習》