日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

K-means学习笔记及简易代码实现

發(fā)布時間:2024/1/18 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 K-means学习笔记及简易代码实现 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

寫前思考

目前的幾個問題,如何找到簇的中心點(diǎn)。
如果要找簇的中心點(diǎn),只需要求出那個簇所在地點(diǎn)的均值,然后將其賦值給新的中心點(diǎn)就可以了。
如何增加或者減少k的值(如何選擇k值)
這還是一個沒有解決的問題,現(xiàn)在我要去百度下嘿嘿。可以是由sse方法,找到數(shù)據(jù)變化的點(diǎn)
沒錯,現(xiàn)在可以寫程序了。

什么是K-means

k均值聚類算法(k-means clustering algorithm)是一種迭代求解的聚類分析算法,其步驟是,預(yù)將數(shù)據(jù)分為K組,則隨機(jī)選取K個對象作為初始的聚類中心,然后計算每個對象與各個種子聚類中心之間的距離,把每個對象分配給距離它最近的聚類中心。聚類中心以及分配給它們的對象就代表一個聚類。每分配一個樣本,聚類的聚類中心會根據(jù)聚類中現(xiàn)有的對象被重新計算。這個過程將不斷重復(fù)直到滿足某個終止條件。終止條件可以是沒有(或最小數(shù)目)對象被重新分配給不同的聚類,沒有(或最小數(shù)目)聚類中心再發(fā)生變化,誤差平方和局部最小。

分析k-means的算法流程

  • 預(yù)將數(shù)據(jù)分為K組,則隨機(jī)選取K個對象作為初始的聚類中心;
  • 計算每個對象與各個種子聚類中心之間的距離,把每個對象分配給距離它最近的聚類中心。聚類中心以及分配給它們的對象就代表一個聚類。
  • 重新計算各個聚類的中心,并將其作為新的聚類中心。
  • 這個過程(步驟1-3)將不斷重復(fù)直到滿足某個終止條件。終止條件可以是沒有(或最小數(shù)目)聚類中心再發(fā)生變化,誤差平方和局部最小。
  • 主要函數(shù)的編寫

    首先,隨機(jī)產(chǎn)生k個聚類中心,這里我們可以使用np的函數(shù)。
    但是,我們要先得出這個數(shù)據(jù)集的邊界,也就是x,y的最大最小值。
    步驟: 首先讀取數(shù)據(jù)集,然后使用np.max方法求最大最小值。然后生成一個(0,1)的矩陣。
    之后使用生成的矩陣乘增量+初始值的方式。可能這個方式有點(diǎn)原始,有好的辦法之后,會更新。

    def create_centroids(k,data_set):min_x,max_x=min(data_set[:,0]),max(data_set[:,0])min_y,max_y=min(data_set[:,1]),max(data_set[:,1])centroids =np.random.random((k,2))centroids_x=min_x+centroids[:,0]*(max_x-min_x)centroids_y=min_y+centroids[:,1]*(max_y-min_y)centroids=[]for i in range(len(centroids_x)):centroids.append([centroids_x[i],centroids_y[i]])return np.array(centroids)

    下面這個函數(shù)是劃分樣本到最近的聚類中心:

    def findClosestCentroids(data,centroids):cluster_indexs=[]for i in range(len(data)):diff=data[i]-centroids#這樣減法會生成K行,這樣一下子就能算出來 一個樣本到k個聚類中心的坐標(biāo)差# 下面求歐氏距離dist=0for j in range(len(diff[0])):dist+=diff[:,j]**2 # 求x,和y的平方和min_index=np.argmin(dist) #然后找到距離最小的值 并將其標(biāo)注為簇編號cluster_indexs.append(min_index)return np.array(cluster_indexs) # 返回對應(yīng)索引的簇編號

    使用上述函數(shù),我們就可以計算出,每個樣本距離最近的聚類中心,接下來我們需要找出這個簇的真實(shí)中心,怎么找呢?
    我們只需要找這個簇中樣本的平均坐標(biāo)就行了,同時np.mean()方法可以提供很好的幫助。
    此外可以使用一點(diǎn)小技巧劃分?jǐn)?shù)據(jù)集。Datas[clustering==i]先舉個例子。
    如果數(shù)據(jù)集為datas=[[1,2],[1,3],[1,4]],那么datas[True ,False,True]=[[1,2],[1,4]] 所以我們可以用i對比整個簇編號列表。就會得到一個布爾型的列表,如果 和i相同 就會顯示True 否則就是False 。這樣就一步取出了所有的屬于i的樣本,然后在使用numpy.mean()方法求均值。

    def computMeans(Datas, clustering):centroids = []# print(np.unique(clustering))for i in range(len(np.unique(clustering))): # np.unique計算聚類個數(shù)u_k = np.mean(Datas[clustering==i], axis=0) # 求每列的平均值centroids.append(u_k)return np.array(centroids)

    然后就完成了步驟1-3的主要函數(shù)的編寫,下面只要重復(fù)這些步驟,就可以求出來。
    下面開始編輯主函數(shù)

    def K_means(k,data_set):# 隨機(jī)生成k矩陣centroids =create_centroids(k,data_set)# 使用生成算法生成聚類中心centroids_list=[]# 用于記錄中心點(diǎn)的移動軌跡for i in range(30):# 訓(xùn)練三十遍clustering=findClosestCentroids(data_set,centroids)centroids_new=computMeans(data_set,clustering)centroids_list.append(centroids1)# 說明有點(diǎn),沒有被任何樣本選中,說明中心點(diǎn)已經(jīng)多了#比如說,我們自動生成了4個點(diǎn),但是樣本選擇離自己最近的點(diǎn)的時候,發(fā)現(xiàn)沒有一個樣本選擇該點(diǎn),所以計算簇的中心點(diǎn)的時候,會有一個點(diǎn)是空的。所以返回的矩陣會少一行。這樣兩個矩陣就不一樣了。但是這也說明,三個點(diǎn)就足夠了。所以就不用繼續(xù)迭代了if centroids.shape!=centroids_new.shape:print("出現(xiàn)未選中的中心點(diǎn)")return centroids_list,cluster,False#返回中心點(diǎn)的移動軌跡和最終的簇和退出狀態(tài)if np.max(centroids-centroids_new)==0: # 如果兩個矩陣沒有差別了,就可以退出循環(huán)breakcentroids=centroids_newcluster=findClosestCentroids(data_set,centroids)# 找到最終的簇return centroids_list,cluster,True #返回中心點(diǎn)的移動軌跡和最終的簇和退出的狀態(tài)

    現(xiàn)在,主函數(shù)已經(jīng)結(jié)束了,但是我們還不知道最優(yōu)的k,這就是我開頭說的第二個難題:
    我們可以使用誤差平方和( sum of squared errors)SSE。

    手肘法求k值

    手肘法的核心思想:隨著聚類數(shù)k的增大,樣本劃分會更加精細(xì),每個簇的聚合程度會逐漸提高,那么誤差平方和SSE自然會逐漸變小。并且,當(dāng)k小于真實(shí)聚類數(shù)時,由于k的增大會大幅增加每個簇的聚合程度,故SSE的下降幅度會很大,而當(dāng)k到達(dá)真實(shí)聚類數(shù)時,再增加k所得到的聚合程度回報會迅速變小,所以SSE的下降幅度會驟減,然后隨著k值的繼續(xù)增大而趨于平緩,也就是說SSE和k的關(guān)系圖是一個手肘的形狀,而這個肘部對應(yīng)的k值就是數(shù)據(jù)的真實(shí)聚類數(shù)。當(dāng)然,這也是該方法被稱為手肘法的原因。
    S S E = ∑ i = 1 k ∑ p ∈ C i ∣ p ? m i ∣ 2 SSE=\sum_{i=1}^{k} \sum_{p\in C_i} |p-m_i|^2 SSE=i=1k?pCi??p?mi?2其中,Ci是第i個簇,p是Ci中的樣本點(diǎn),mi是Ci的質(zhì)心(Ci中所有樣本的均值),SSE是所有樣本的聚類誤差,代表了聚類效果的好壞。將其轉(zhuǎn)化為代碼,則如下:

    def calculate_sse(centroids,clustering,data_set):sum=0for i in range(data_set.shape[0]):diff= data_set[i]-centroids[clustering[i]]sum+=diff[0]**2+diff[1]**2return sum

    我們再看下sse和k值的變化關(guān)系圖:

    如上圖所示,k=2 是手肘的肘部。我們可以看出來,但是怎么讓計算機(jī)能看懂呢?我又想了一個土辦法:讓i+1的sse值除i的sse值,這樣會得出一個小于1的值,比值越小,變化越巨大。
    然后我們在判斷前后兩個比值的差,差最大的就是肘部。我們默認(rèn)會設(shè)置8個分類。所以可以很好的劃分。
    如果差值一最大,但是他在差值列表中的索引為0,但是肘部的k值為2。所以索引和k值得差為2。

    def find_k_index(lis_k):ratios=[ lis_k[i+1]/lis_k[i] for i in range(len(lis_k)-1)]diff=[ ratios[i+1]-ratios[i] for i in range(len(ratios)-1)]return np.argmax(diff)+2

    .接下來,我們要窮盡k值,進(jìn)行聚類,然后找出效果最好的,并將圖畫出來。

    def Confirm_K_value(data_set):# 計算最優(yōu)的k值#在一個范圍內(nèi)隨機(jī)生成矩陣,lis_k=[]for k in range(1,MAX_CLUSTER):centroids_list,cluster,state=K_means(k,data_set)if not state:# 如果發(fā)現(xiàn)有空值,那么直接終止增加Kbreaklis_k.append(calculate_sse(centroids_list[-1],cluster,data_set))print("SSE值",lis_k)max_=find_k_index(lis_k)print("共發(fā)現(xiàn){}個簇".format(max_))centroids_list,cluster,state=K_means(max_,data_set)if not state:print("erro")return# 展示數(shù)據(jù)show(centroids_list,cluster,data_set)

    下面描述下畫圖函數(shù),代碼略有繁瑣,后期優(yōu)化。

    def show(centroids_list,cluster,data_set):#簇中心的位置變化情況,樣本對應(yīng)的簇編號,樣本集合centroid_x = []centroid_y = []for centroid in centroids_list:centroid_x.append(centroid[:,0])centroid_y.append(centroid[:,1])plt.plot(centroid_x, centroid_y, 'r*--',c="blue", markersize=14)# 接下來是畫點(diǎn)lis_x=[[] for i in range(np.unique(cluster).shape[0])]lis_y=[[] for i in range(np.unique(cluster).shape[0])]for i in range(len(data_set)):lis_x[cluster[i]].append(data_set[i][0])lis_y[cluster[i]].append(data_set[i][1])colors=['red','brown','orange','green','cyan','purple','pink','blue','#FFA07A','#20B2AA','#87CEFA','#9ACD32']for i in range(np.unique(cluster).shape[0]):plt.scatter(lis_x[i],lis_y[i],alpha=0.5,c=colors[i])#畫一個散點(diǎn)圖plt.show()

    【注這個數(shù)據(jù)集在這個文件同目錄的data目錄下】,下載后需要放到相同位置。數(shù)據(jù)集

    源代碼:

    import pandas as pd import numpy as np import matplotlib.pyplot as plt df= pd.read_csv('./data/兩坨散點(diǎn).csv') data_set=np.array(df)def findClosestCentroids(data,centroids):cluster_indexs=[]for i in range(len(data)):diff=data[i]-centroids# 下面求歐氏距離dist=0for j in range(len(diff[0])):dist+=diff[:,j]**2min_index=np.argmin(dist)cluster_indexs.append(min_index)return np.array(cluster_indexs)# print(np.unique(clustering))# 使用unique可以進(jìn)行去重復(fù)操作。 # 根據(jù)聚類重新計算中心點(diǎn)函數(shù): def computMeans(Datas, clustering):centroids = []# print(np.unique(clustering))for i in range(len(np.unique(clustering))): # np.unique計算聚類個數(shù)u_k = np.mean(Datas[clustering==i], axis=0) # 求每列的平均值centroids.append(u_k)return np.array(centroids)def create_centroids(k,data_set):min_x,max_x=min(data_set[:,0]),max(data_set[:,0])min_y,max_y=min(data_set[:,1]),max(data_set[:,1])centroids =np.random.random((k,2))centroids_x=min_x+centroids[:,0]*(max_x-min_x)centroids_y=min_y+centroids[:,1]*(max_y-min_y)centroids=[]for i in range(len(centroids_x)):centroids.append([centroids_x[i],centroids_y[i]])return np.array(centroids)def calculate_sse(centroids,clustering,data_set):sum=0for i in range(data_set.shape[0]):diff= data_set[i]-centroids[clustering[i]]sum+=diff[0]**2+diff[1]**2return sum def K_means(k,data_set):# 隨機(jī)生成k矩陣centroids =create_centroids(k,data_set)centroids_list=[]for i in range(30):clustering=findClosestCentroids(data_set,centroids)centroids1=computMeans(data_set,clustering)centroids_list.append(centroids1)if centroids.shape!=centroids1.shape:print("出現(xiàn)未選中樣本")# 說明 有點(diǎn),沒有被任何樣本選中,說明中心點(diǎn)已經(jīng)多了return centroids_list,[],False #返回中心點(diǎn)的移動軌跡和最終的簇和退出的狀態(tài)if np.max(centroids-centroids1)==0:# print("find")breakcentroids=centroids1# print(centroids)cluster=findClosestCentroids(data_set,centroids)return centroids_list,cluster,True #返回中心點(diǎn)的移動軌跡和最終的簇和退出的狀態(tài)def show(centroids_list,cluster,data_set):centroid_x = []centroid_y = []for centroid in centroids_list:centroid_x.append(centroid[:,0])centroid_y.append(centroid[:,1])plt.plot(centroid_x, centroid_y, 'r*--',c="blue", markersize=14)lis_x=[[] for i in range(np.unique(cluster).shape[0])]lis_y=[[] for i in range(np.unique(cluster).shape[0])]for i in range(len(data_set)):lis_x[cluster[i]].append(data_set[i][0])lis_y[cluster[i]].append(data_set[i][1])colors=['red','brown','orange','green','cyan','purple','pink','blue','#FFA07A','#20B2AA','#87CEFA','#9ACD32']for i in range(np.unique(cluster).shape[0]):plt.scatter(lis_x[i],lis_y[i],alpha=0.5,c=colors[i])#畫一個散點(diǎn)圖plt.show() MAX_CLUSTER=8 def find_k_index(lis_k):ratios=[ lis_k[i+1]/lis_k[i] for i in range(len(lis_k)-1)]diff=[ ratios[i+1]-ratios[i] for i in range(len(ratios)-1)]return np.argmax(diff)+2 def Confirm_K_value(data_set):# 計算最優(yōu)的k值#在一個范圍內(nèi)隨機(jī)生成矩陣,lis_k=[]for k in range(1,MAX_CLUSTER):centroids_list,cluster,state=K_means(k,data_set)if not state:breaklis_k.append(calculate_sse(centroids_list[-1],cluster,data_set))print("SSE值",lis_k)max_=find_k_index(lis_k)print("共發(fā)現(xiàn){}個簇".format(max_))centroids_list,cluster,state=K_means(max_,data_set)if not state:print("erro")return# 展示數(shù)據(jù)show(centroids_list,cluster,data_set)Confirm_K_value(data_set)

    測試結(jié)果

  • 兩個簇情況下:
    SSE值 [897.1674886919908, 156.58714828390853, 119.03323146797621, 93.86925045546373, 81.41218770686388]
    共發(fā)現(xiàn)2個簇

  • 三個簇情況下:

    SSE值 [2668.4175360883137, 1139.838296827762, 238.7100927716138, 206.82792773131195, 192.94401994160415]
    共發(fā)現(xiàn)3個簇

  • 五個簇情況下:

    SSE值 [54790.63847837179, 19023.567985249738, 10365.750809115409, 6078.364568727137, 3892.1958462961006, 3695.49840412411, 3418.4365266913137]
    共發(fā)現(xiàn)5個簇
    【存在問題】本次實(shí)現(xiàn)的聚類算法,在五個簇的數(shù)據(jù)集中表現(xiàn)十分不穩(wěn)定,有待改進(jìn)。

  • 總結(jié)

    以上是生活随笔為你收集整理的K-means学习笔记及简易代码实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。