西瓜書9.10:實現自動確定聚類數目的k_means演算法
阿新 • • 發佈:2019-01-07
問題:
試設計一個能自動確定聚類數的改進k均值演算法,程式設計實現並在西瓜資料集4.0上執行。
資料集:
西瓜資料集4.0
資料集描述:
該資料集共有30個樣本,每個樣本有密度和含糖度兩個特徵。
思路:
如何確定k的取值:
這裡希望每一類別內部樣本點距離較小而不同聚類之間的誤差較大,所以用:
作為損失函式,其值越小越好,希望取得區域性最優點,當值在k時增大,則k=k-1時最優
如何選擇較好的k個均值:
隨機選取的問題:
若是均值隨機選,則很可能尋找到兩個距離很近的均值點,在迭代過程中,這兩個均值點會逐漸靠近最後重合,使得最後聚類數目減少。
演算法思想:
首先隨機選取L個均值點,接著根據這L個均值點對樣本聚類,減去那些聚類內樣本較小的點。接著在剩下的L1個店中隨機選取一個均值點,並且尋找距離該點最遠的均值點,依次迭代直至選取到K個均值點。
演算法流程:
k_means演算法:
- 初始化:首先選取k個均值點
- 將樣本點劃分到距離自己最近的均值點所在類別
- 根據聚類結果更新均值點
- 重複步驟2.3直至均值點不再改變
- 輸出聚類結果
結果:
通過對損失函式計算,得到當k=3時,聚類結果最優,聚類結果以及圖形化展示如下:
原始碼 :
損失函式計算:
clu_unique=np.unique(cluster)
Di=[]
D=len(data)
E=0.0
for i in range(len(mean)):
Di.append(len(np.where(cluster==i)[0 ]))
for i in range(D):
E+=np.linalg.norm(data[i]-mean[cluster[i][0]],ord=2)
for i in range(len(mean)):
for j in range(len(mean)):
E+=np.linalg.norm(mean[i]-mean[j],ord=2)
E-=np.log(len(mean)/D)
return E
優化版本K個均值值選取:
def find_k_means(data,K):
L=int(K*np.log (K))
if L<K:
L=K
np.random.seed(int(time.time()))
r_index=random_unique(0,data.shape[0],L)
mean=data[r_index]#隨機選取L箇中心
cluster = classify(data,mean) # 紀錄每個樣本所屬類別
remove_index=remove_center(cluster,data.shape[0],L)#刪除以該中心開始聚類數目最少的中心點
new_mean=[]#新的中心點
for i in range(mean.shape[0]):
if (i not in remove_index) and (mean[i].tolist() not in new_mean):
new_mean.append(mean[i].tolist())
if len(new_mean)>K:#new_mean 裡面元素不同
k_mean=[]
r_i=np.random.randint(0,len(new_mean),1)[0]
old_v=new_mean[r_i]
k_mean.append(old_v)
while(len(k_mean)<K):
dis=[]
for i in range(len(new_mean)):
dis.append([new_mean[i],np.linalg.norm(np.array(new_mean[i])-np.array(old_v),ord=2)])
max=np.max(np.array(dis)[:,1])
max_index=np.where(np.array(dis)[:,1]==max)[0]
while(dis[max_index[0]][0] in k_mean):
dis.pop(max_index[0])
max = np.max(np.array(dis)[:, 1])
max_index = np.where(np.array(dis)[:, 1] == max)[0]
old_v=dis[max_index[0]][0]#上一個距離最遠的樣本
k_mean.append(old_v)
else:
k_mean=new_mean
return np.array(k_mean)
K均值演算法:
oldE=100
color = ['green', 'red', 'purple', 'pink', 'yellow','green','brown','tan','seashell','salmon']
mark=['^','o','*','.','#']
old_cluster=[]
for k in np.arange(2,len(data),1):
k_mean=find_k_means(data,k)
old_mean = np.zeros(shape=k_mean.shape)
cluster = np.zeros(shape=k_mean.shape)
while (not ((old_mean == k_mean).all())):#直到mean值不再改變 達到最優
cluster = classify(data, k_mean)
old_mean = k_mean
k_mean = update_means(data, cluster)
E = loss(data, cluster, k_mean)
if oldE<E:
print("在k="+str(k-1)+"次達到最優")
for i in np.unique(old_cluster):
index = np.where(old_cluster == i)
x = data[index, 0]
y = data[index, 1]
plt.scatter(x, y, color=color[i%10], marker=mark[i%5])
plt.show()
break
oldE=E
old_cluster=cluster
print("聚類結果:" + str(cluster.reshape((cluster.shape[1], cluster.shape[0]))))
print("聚類損失:" + str(E))