聚類:無監督學習,将相似的對象歸到同一個簇中
K-均值聚類算法:
僞代碼如下:
建立k個點作為起始軸心
當任意一個點的簇分類配置設定結果發生改變時
對資料集中的每個資料點
對每個質心
計算質心與資料點之間的距離
将資料點配置設定到距其最近的簇
對每一個簇,計算簇中所有點的均值并将均值作為質心
import numpy as np
def loadDataSet(fileName):
dataMat = {}
fr = open(fileName)
for line in fr.readline():
curLine = line.strip().split('\t')
fltLine = map(float,curLine)
dataMat.append(fltLine)
return dataMat
#計算兩個向量的歐式距離
def distEclud(vecA, vecB):
return np.sqrt(sum(np.power(vecA - vecB, 2)))
#為給定資料集建構一個包含k個随機質心的集合
def randCent(dataSet, k):
n = np.shape(dataSet)[1]
centroids = np.mat(np.zeros((k,n)))
for j in range(n):
minJ = min(dataSet[:j])
rangeJ = float(max(dataSet[:,j]) - minJ)
#生成0到1之間的随機數并通過取值範圍和最小值,以便確定随機點在資料的邊界之内
centroids[:,j] = minJ + rangeJ + np.random.rand(k,1)
return centroids
K-均值聚類算法
def KMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
#确定資料集中資料點的總數
m=np.shape(dataSet)[0]
#一列記錄簇索引值,第二列存儲誤差,誤差是指目前點到簇質心的距離
clusterAssment = np.mat(np.zeros((m,2)))
centroids = createCent(dataSet, k)
clusterChanged = True
while clusterChanged:
clusterChanged = False
for i in range(m):
minDist = np.inf;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 #任意簇發配置設定結果發生改變,更新clusterChanged的值
clusterAssment[i,:] = minIndex,minDist**2
print(centroids)
#周遊所有質心
for cent in range(k):
ptsInClust = dataSet[np.nonzero(clusterAssment[:,0].A==cent)[0]] #通過數組過濾來獲得給定簇的所有點
centroids[cent,:] = np.mean(ptsInClust, axis=0) #計算所有點的均值,給定列方向進行均值運算
return centroids,clusterAssment #傳回所有的類質心與點配置設定結果
度量聚類效果的名額SSE(誤差平方和),對應clusterAssment矩陣的第一列之和。SSE越小表示資料點越接近于它們的質心,聚類效果也越好,聚類的目标是保持簇數目不變的情況下提高簇的品質
二分K-均值算法:
首先将所有點作為一個簇,然後将該簇一分為2,之後選擇其中一個簇繼續進行劃分,選擇哪一個簇進行劃分取決于對其劃分是否可以最大程度降低SSE的值,上述基于SSE的劃分過程不斷重複,直到得到使用者指定的簇數目為止。
另一種做法是選擇SSE最大的簇進行劃分,直到簇數目達到使用者指定的數目為止
實作算法如下:
def biKmeans(dataSet, k, distMeas=distEclud):
m = np.shape(dataSet)[0]
clusterAssment = np.mat(np.zeros((m, 2))) #矩陣存儲資料集中每個點的簇配置設定結果及平方誤差
centroid0 = np.mean(dataSet, axis=0).tolist()[0] #計算整個資料集的質心
centList = [centroid0] #使用一個清單來保留所有的質心
for j in range(m):
clusterAssment[j,1] = distMeas(np.mat(centroid0), dataSet[j,:]**2)
while(len(centList) < k):
lowestSSE = np.inf #一開始将最小SSE設為無窮大
#周遊簇清單centList中的每一個簇
for i in range(len(centList)):
#将簇中所有點看成一個小的資料集ptsCurrCluster
ptsInCurrCluster = dataSet[np.nonzero(clusterAssment[:,0].A==i)[0],:]
#生成兩個質心(簇),同時給出每個簇的誤內插補點
centroidMat, splitClutserAss = KMeans(ptsInCurrCluster, 2 , distMeas)
#本次資料集的誤差
sseSplit = sum(splitClutserAss[:,1])
#剩餘資料集的誤差
sseNotSplit = sum(clusterAssment[np.nonzero(clusterAssment[:,0].A!=i)[0],1])
print("seeSplit, and notSplit:",sseSplit,sseNotSplit)
#誤差與剩餘資料集的誤差之和作為本次劃分的誤差
if(sseSplit + sseNotSplit) < lowestSSE:
bestCentToSplit = i
bestNewCents = centroidMat #更新簇
bestClustAss = splitClutserAss.copy() #将簇中所有點的配置設定結果進行修改
lowestSSE = sseSplit + sseNotSplit #更新lowestSSE
#會得到兩個編号分别為0和1的結果簇,将簇編号修改為劃分簇及新加簇的編号
bestClustAss[np.nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList)
bestClustAss[np.nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit
print('the bestCentTosplit is:',bestCentToSplit)
print('the len of bestClustAss is:',len(bestCentToSplit))
centList[bestCentToSplit] = bestNewCents[0,:]
centList.append(bestNewCents[1,:])
clusterAssment[np.nonzero(clusterAssment[:,0].A ==bestCentToSplit)[0],:] = bestClustAss
return np.mat(centList), clusterAssment