機器學習實戰之K-means
阿新 • • 發佈:2019-02-12
1 演算法思想
聚類的基本思想是將資料集中的樣本劃分為若干個通常是不相交的子集,每個子集稱為一個”簇”(cluster)。劃分後,每個簇可能有相同對應的概念(性質)。K-均值演算法就是一個使用很廣泛的聚類演算法,其中的K就表示簇的數量,K-means簡單的說就是通過質心來將樣本劃分成K個不同的簇。
虛擬碼為:
工作流程:
(1)事先選定K個聚類中心
(2)計算待測樣本a到每個聚類中心的距離,然後將樣本a分配到距離最近的聚類中
(3)計算每個聚類樣本的均值來動態更新原聚類質心
(4)不斷迭代(2),(3),直到簇質心不再改變。
2 實現
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent) :
m = shape(dataSet)[0] #m=80L
clusterAssment = mat(zeros((m,2)))
#創造一個m個列表,每個列表中2個元素的全零變數
#在matlab中相當於建立一個m行2列的矩陣
#第一列存簇索引值,第二列存當前點到簇質心的距離
centroids = createCent(dataSet, k) #隨機建立k個簇心
clusterChanged = True #建立標註標量,用來達到條件就終止迴圈
while clusterChanged:
clusterChanged = False
for i in range(m):#遍歷每個樣本
minDist = inf #初始樣本到簇質心的距離 nif 表示正無窮;-nif表示負無窮
minIndex = -1
for j in range(k): #遍歷每個簇質心
distJI = distMeas(centroids[j,:],dataSet[i,:]) #計算樣本點到簇質心的距離
if distJI < minDist: #尋找距離最近的簇質心
minDist = distJI
minIndex = j #將樣本分配到距離最小的質心那簇
if clusterAssment[i,0] != minIndex: clusterChanged = True #如果樣本分配結果發生變化,更改標誌變數
#如果分配後結果不變,就將結果存入clusterAssment,結束while迴圈
clusterAssment[i,:] = minIndex,minDist**2
print centroids
for cent in range(k):#遍歷每個簇質心
#找到每個簇質心對應的樣本
ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#get all the point in this cluster
centroids[cent,:] = mean(ptsInClust, axis=0) #計算這些樣本的均值,作為該簇的新質心
return centroids, clusterAssment
3 二分K-均值演算法
K-means雖然實現很容易,但卻很容易收斂到區域性最小,達不到全域性最小,所以有人提出二分K-均值。二分K-均值簡單來說就是將所有資料集分成兩簇,然後再選擇誤差最小的那個簇再進行二分K-均值劃分。
二分K-均值虛擬碼:
3.1 實現
def biKmeans(dataSet, k, distMeas=distEclud):
m = shape(dataSet)[0] #60L
clusterAssment = mat(zeros((m,2)))
centroid0 = mean(dataSet, axis=0).tolist()[0] #計算每維的均值 如果axis=1,就是計算每個列表元素的均值
centList =[centroid0] #用每維均值建立初始簇質心,也就是說,將整個資料集當成一簇
for j in range(m):#遍歷每個樣本
#儲存樣本到每個簇質心的距離
#所以此處shape(clusterAssment)=(60L,2L)
clusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:])**2
while (len(centList) < k): #如果初始的簇個數小於k
lowestSSE = inf
for i in range(len(centList)): #遍歷初始的那簇
#提取當前簇中的所有樣本點
ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]
#將這些樣本點進行kmeans操作,其中K設為2.
# 也就是說,把之前那簇資料集分成兩簇,分別計為:0簇和1簇,同時返回新分成的兩簇中的每簇的質心和誤差
#shape(centroidMat)=(2L,2L) shape(splitClustAss)=(60L,2L)
centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)
sseSplit = sum(splitClustAss[:,1])# 所有誤差相加
sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])
print "sseSplit, and notSplit: ",sseSplit,sseNotSplit
#選擇誤差小的簇繼續二分K-均值劃分
if (sseSplit + sseNotSplit) < lowestSSE:
bestCentToSplit = i
bestNewCents = centroidMat
bestClustAss = splitClustAss.copy()
lowestSSE = sseSplit + sseNotSplit
#將簇編號(0,1)修改成劃分簇及新加簇的編號
bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) #change 1 to 3,4, or whatever
bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit
print 'the bestCentToSplit is: ',bestCentToSplit
print 'the len of bestClustAss is: ', len(bestClustAss)
#新劃分的質點更新到centList中
centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]#replace a centroid with two best centroids
centList.append(bestNewCents[1,:].tolist()[0])
#新劃分的結果更新到clusterAssment中
clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss#reassign new clusters, and SSE
return mat(centList), clusterAssment