K均值聚類是一種無監督學習聚類算法。
介紹
對於$n$個$m$維特征的樣本,K均值聚類是求解最優化問題:
$\displaystyle C^*=\text{arg}\min\limits_{C}\sum\limits_{l = 1}^K\sum\limits_{x\in C_l}||x-x_l||^2$
其中$C$表示某種樣本的划分方式,$x\in C_l$表示被划分在第$l$類的樣本,$x_l$表示被划分在第$l$類的所有樣本的中心,也就是均值。所以上式表示最小化所有類別內樣本到其對應的類別樣本均值的距離之和。最優化這個問題是NP難問題,所以現實采用類似貪心的迭代算法來逼近最優解(不一定最優)。具體流程如下(每個樣本只能屬於一個類別):
0、初始化$K$個類的均值為隨機$m$維向量。
1、將每個樣本划分到與之距離最小的類別均值對應的類別中。
2、根據划分進的樣本,每個類別重新計算類別均值,並記錄。
3、比較連續兩次的類別均值,如果差別小於一定閾值就結束,否則回到1。
通過計算可以知道時間復雜度是$O(mnk)$。
代碼實現
Numpy手動實現
首先使用由正態分布生成的兩簇點集來實驗,每個簇各200個點。可能由於這兩個點集比較靠近,所以分類完全錯誤,如圖:
重新生成5簇正態分布點集,這次每個集合之間相對較遠,除了少數沒有正確聚類外(兩類交匯處),表現不錯:
代碼如下:
#%%獲取數據 import matplotlib.pyplot as plt import numpy as np import xlrd table = xlrd.open_workbook('test.xlsx').sheets()[0]#讀取Excel數據 data = [] for i in range(0,table.nrows):#假設第一行是表頭不讀入 data.append(table.row_values(i)) data = np.array(data) #%%聚類 def distance(a,b): d = a-b return np.dot(d,d) def clusterize(data,class_m):#關於類均值對樣本分類 for i in data: min_dis = np.inf for j in range(len(class_m)): t = distance(i[:-1],class_m[j]) if t<min_dis: min_dis=t i[-1]=j def calc_mean(data,class_m):#以類中樣本計算均值 class_m -= class_m num = np.zeros([len(class_m)]) for i in data: num[int(i[-1])]+=1 class_m[int(i[-1])]+=i[:-1] for i in range(len(class_m)): class_m[i]/=num[i] def updated_mean(dif):#計算前后兩次更新的均值距離,傳入前后差值 sum_ = 0 for i in dif: sum_ += np.dot(i,i) return sum_ def k_means_cluster(data,K): class_mean = data[0:K,:-1] class_mean_old = -class_mean t = updated_mean(class_mean-class_mean_old) ii = 0 while t>0.0001: class_mean_old = class_mean.copy() clusterize(data,class_mean) print(class_mean) print(class_mean_old) calc_mean(data,class_mean) print(class_mean) print(class_mean_old) t = updated_mean(class_mean-class_mean_old) print(t) ii+=1 print(ii) data1 = data.copy() np.random.shuffle(data1) k_means_cluster(data1,5) #要分幾類直接這里設置################################## #%%繪制結果 import matplotlib.pyplot as plt fig = plt.figure() ax1 = fig.add_subplot(121) ax2 = fig.add_subplot(122) ax1.scatter(data1[:,0],data1[:,1],c = data1[:,-1]) ax1.set_title("K-Means") ax2.scatter(data[:,0],data[:,1],c = data[:,-1]) ax2.set_title("Should be") plt.show()
Sklearn
使用封裝好的Sklearn,代碼如下:
#%%獲取數據 import matplotlib.pyplot as plt import numpy as np import xlrd table = xlrd.open_workbook('test.xlsx').sheets()[0]#讀取Excel數據 data = [] for i in range(0,table.nrows):#假設第一行是表頭不讀入 data.append(table.row_values(i)) data = np.array(data) #%%聚類 from sklearn.cluster import KMeans kmeans = KMeans(n_clusters=5) kmeans.fit(data[:,:-1]) y = kmeans.predict(data[:,:-1]) fig = plt.figure() ax1 = fig.add_subplot(121) ax1.scatter(data[:,0],data[:,1],c = y) ax1.set_title("K-Means") ax2 = fig.add_subplot(122) ax2.scatter(data[:,0],data[:,1],c=data[:,-1]) ax2.set_title("Should be") plt.show()
結果圖: