Python機器學習演算法實踐——二分k-均值演算法
阿新 • • 發佈:2019-02-02
二分k-均值演算法步驟:
首先將所有點作為一個屬,然後將該簇-分為二,之 後選擇其中-個簇進續進行劃分,選擇哪一個簇進行劃取決於對其劃分是否可以最大程度降低SSE的值,上述基於SSE的別分過程不斷重複,直到得到使用者指定的屬數目為止,
將所有點看成一個簇
當簇數目小於k時
對於每一個簇:
計算總誤差
在給定的簇上面進行K-均值聚類(k=2)計算將該簇一分為二後的總誤差
選擇使得誤差最小的那個簇進行劃分操作
另一種做法選擇S正最大的簇進行劃分,直到簇數目達到使用者指定的數目為止。
Python實現
# -*- coding: utf-8 -*- """ Created on Thu Aug 16 11:09:04 2018 @author: wjw """ from numpy import * def loadDataSet(fileName): dataMat=[] fr=open(fileName) for line in fr.readlines(): arr=line.strip().split('\t') #map (回撥函式,列表)-》迴圈列表中的每個值,呼叫迴歸函式得到結果,存到map -》 list l=list(map(float,arr))#float 強制型別轉換 dataMat.append(l) return dataMat dataMat=loadDataSet('dataset/testSet.txt') #print(dataMat[0:5]) #距離 度量方式: #1.歐氏距離 def disEuclid(vecA,vecB): return sqrt(sum(power((vecA-vecB),2))) #初始質心選取 def randCenter(dataSet,k): n=shape(dataSet)[1]#n=..列 centers=mat(zeros((k,n))) #print(centers) for j in range(n): #每迴圈一次,產生的一個特徵值 #當前第j列的最小值,與最大值,求範圍 minJ=min(dataSet[:,j]) #print('minJ:',minJ) maxJ=max(dataSet[:,j]) rangeJ=float(maxJ-minJ) #生成隨機數 ,k行(代表k箇中心點)#0-1 centers[:,j]=minJ+rangeJ*random.rand(k,1)#random.rand(k,1)->k行一列的資料 randej標量 print(centers[:,j]) return centers def myKMeans(dataSet,k,disMea=disEuclid,initCenter=randCenter): ''' ''' m=shape(dataSet)[0] #這個zeros是一個m行2列的資料(記錄這個點所屬的簇的索引,,記錄這個點到其質心的距離) clusterAssment=mat(zeros((m,2))) #生成隨機質心 centers=initCenter(dataSet,k) clusterChanged=True while clusterChanged: clusterChanged=False #迴圈每個點,計算他與每個質心的位置 for i in range (m): #這個點到某質心的最小距離及質心所在的索引 minDist=inf #某個點距離質心點的最小距離 inf 無窮大 minIndex=-1#有最小距離的質心的索引——知道這個簇了 for j in range(k): distJI=disMea(centers[j,:],dataSet[i,:])#算距離 #print('===:',centers[j,:]) #print(']][][[][]]',dataSet[i,:]) #print('================================================================') if distJI < minDist: minDist=distJI minIndex=j #for I in range(m)迴圈完,表明已經找到了第i個數據點所屬的簇,且計算出了距離 if clusterAssment[i,0]!=minIndex: clusterChanged=True #更新這個點到質心的索引及誤差 clusterAssment[i,:]=minIndex,minDist**2 #============以上的迴圈個更新每個點的簇====================================================================== #遍歷所有的簇,重新找質心 for cent in range(k): flag=clusterAssment[:,0].A==cent #查詢這個 cent 簇所有的點 #print('flag:',flag) pointsInCluster=dataSet[nonzero(flag)[0]]#第cent個簇所有點 #print('pointsInCluster:',pointsInCluster) centers[cent,:]=mean(pointsInCluster,axis=0)#對於這個簇中每個點的列取均值,更新中心點centers[cent] return centers,clusterAssment dataMat=mat(loadDataSet('dataset/testSet.txt')) centers,clusterAssment=myKMeans(dataMat,4) print(centers) print(clusterAssment)#第一個列為簇的編號,第二列是當前點到這個簇的質心的距離 def biKmeans(dataSet,k,distMea=disEuclid): #1.將所有點看成一個簇 #2.取出這個簇的中心點 #3.計算這個簇中每個點到中心點的距離 #4.這個zeros是一個m行2列的資料(記錄這個點所屬的簇的索引,,記錄這個點到其質心的距離) #clusterAssment=mat(zeros((m,2))) #存所有的中心點 m=shape(dataSet)[0] #這個zeros是一個m行2列的資料(記錄這個點所屬的簇的索引,,記錄這個點到其質心的距離) clusterAssment=mat(zeros((m,2))) center0=mean(dataSet,axis=0).tolist()[0] print('第一個中心點:',center0) centList=[center0] for j in range(m): clusterAssment[j,1]=distMea(mat(center0),dataSet[j,:])**2 #迴圈來產生質心 while(len(centList)<k):#當簇數目小於k時 lowestSSE=inf #初始化最小 誤差平方和 #迴圈每個簇 for i in range (len(centList)): #到dataset中篩選出屬於第i個簇的資料樣本 pointsInCluster=dataSet[nonzero(clusterAssment[:,0].A==i)[0],:] #對這個簇中的樣本進行一次k=2的聚類 centroidMat,splitClustAss=myKMeans(pointsInCluster,2,distMea) sseSplit=sum(splitClustAss[:,1]) #剩餘的資料集的誤差 #sseSplit=sum(splitClustAss[:,1])-sseSplit sseNotSplit=sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1]) #總誤差 totalSplit=sseSplit+sseNotSplit if totalSplit<lowestSSE: bestCentToSplit=i#最好的質心索引 bestNewCent=centroidMat bestClustAss=splitClustAss.copy() lowestSSE=totalSplit #2分k聚類返回係數0或1,需要把1換成當前簇數目,以免造成重複 bestClustAss[ nonzero(bestClustAss[:,0].A==1)[0],0 ]=len(centList) #返回的是第幾行第幾行 #把0換成別切分的簇,或者與上面的交換賦值也可以 bestClustAss[ nonzero(bestClustAss[:,0].A==0)[0],0]=bestCentToSplit #print('===:',bestCentToSplit) #將centlist指定位置上的質心換成分割後的質心 centList[bestCentToSplit]=bestNewCent[0,:].tolist()[0] #將另一個質心新增上去 centList.append(bestNewCent[1,:].tolist()[0]) #將劃分後的新質點及點分佈賦值給結果矩陣 clusterAssment[nonzero(clusterAssment[:,0].A==bestCentToSplit)[0],:]=bestClustAss return mat(centList),clusterAssment datMat=mat(loadDataSet('dataset/testSet.txt')) centList,clusterAssment=biKmeans(dataMat,4) print(centList)
注:biKmeans 函式就是二分k-均值演算法
可藉助spyder的debug模式進行除錯,幫助理解程式碼,如果spyder版本太低可以如下命令進行升級
conda update spyder