1. 程式人生 > >西瓜書9.10:實現自動確定聚類數目的k_means演算法

西瓜書9.10:實現自動確定聚類數目的k_means演算法

問題:

試設計一個能自動確定聚類數的改進k均值演算法,程式設計實現並在西瓜資料集4.0上執行。

資料集:

西瓜資料集4.0

資料集描述:

該資料集共有30個樣本,每個樣本有密度和含糖度兩個特徵。

思路:

如何確定k的取值:

這裡希望每一類別內部樣本點距離較小而不同聚類之間的誤差較大,所以用:
這裡寫圖片描述
作為損失函式,其值越小越好,希望取得區域性最優點,當值在k時增大,則k=k-1時最優

如何選擇較好的k個均值:

隨機選取的問題:

若是均值隨機選,則很可能尋找到兩個距離很近的均值點,在迭代過程中,這兩個均值點會逐漸靠近最後重合,使得最後聚類數目減少。

演算法思想:

首先隨機選取L個均值點,接著根據這L個均值點對樣本聚類,減去那些聚類內樣本較小的點。接著在剩下的L1個店中隨機選取一個均值點,並且尋找距離該點最遠的均值點,依次迭代直至選取到K個均值點。

演算法流程:

這裡寫圖片描述

k_means演算法:

  1. 初始化:首先選取k個均值點
  2. 將樣本點劃分到距離自己最近的均值點所在類別
  3. 根據聚類結果更新均值點
  4. 重複步驟2.3直至均值點不再改變
  5. 輸出聚類結果

結果:

通過對損失函式計算,得到當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))