聚類是一種無監督的學習,它將相似的對象歸到同一個簇中。
這篇文章介紹一種稱為K-均值的聚類算法,之所以稱為K-均值是因為它可以發現k個不同的簇,且每個簇的中心采用簇中所含值的均值計算而成。
聚類分析視圖將相似對象歸入同一簇,將不相似對象歸到不同簇。
下面用Python簡單演示該算法實現的原理:
函數loadDataSet先將文本文件導入到一個列表中,並添加到dataSet中,返回的結果即為需加載的訓練數據。
def loadDataSet(fileName):
dataMat = []
fr = open(fileName)
for line in fr.readlines():
curLine = line.strip().split('\t')
fltLine = map(float,curLine)
dataMat.append(list(fltLine))
return dataMat
函數distEclud用於計算兩個向量的距離:
def distEclud(vecA, vecB):
return sqrt(sum(power(vecA - vecB, 2))) # 計算距離
def randCent(dataSet,k):
n = shape(dataSet)[1] # 列數
centroids = mat(zeros((k,n))) # k為簇質心的數量 n為每個點對應的坐標的數量
for j in range(n):
minJ = min(dataSet[:,j]) # 每列的最小值
rangeJ = float(max(dataSet[:,j]) - minJ) # 變化區間
centroids[:,j] = minJ + rangeJ * random.rand(k,1) # 生成坐標
return centroids
函數randCent有兩個參數,其中k為用戶指定的質心的數量(即最后分成的類的個數),該函數的作用是為給定的數據集dataSet構建一個包含k個隨機質心的集合(centroids)。
上面三個為輔助函數,下面為完整的K-均值算法:
def kMeans(dataSet,k,disMeas = distEclud, createCent = randCent):
m = shape(dataSet)[0] # 訓練數據集的數量
clusterAssent = mat(zeros((m,2))) # 用於保存每個點對應的質心
centroids = createCent(dataSet,k) #初始化質心並保存
clusterChanged = True
while clusterChanged:
clusterChanged = False
for i in range(m): # 遍歷所有數據點 計算每一個數據點到每個質心的距離
minDist = inf
minIndex = -1
for j in range(k): # 遍歷所有的質心點
distJI = disMeas(centroids[j,:], dataSet[i ,:])
if distJI < minDist:
minDist = distJI
minIndex = j
if clusterAssent[i,0] != minIndex:
clusterChanged = True # 任何一個點對應的質心發生變化需要重新遍歷計算
clusterAssent[i,:] = minIndex,minDist ** 2
print(centroids)
for cent in range(k): # 更新質心的位置
ptsInClust = dataSet[nonzero(clusterAssent[:,0].A == cent)[0]]
centroids[cent : ] = mean(ptsInClust, axis= 0)
return centroids,clusterAssent
思路大概為:
- 遍歷所有訓練數據點(m個數據點)
- 對所有質心(k個質心)
- 計算該數據點與質心之間的距離,並保存該數據點最近的質心
- 對比之前保存的該數據點的質心(即該數據點所屬的簇,保存在clusterAssent中),若發生更改,則證明未收斂,結束后需要從頭開始循環
- 對所有質心(k個質心)
- 對於每一個簇,計算簇中所有點的均值作為質心。
K-均值算法有時候會出現聚類效果較差,收斂到了局部最小值,而非全局最小值。一種用於度量聚類效果的指標是SSE(Sum of Squared Error,誤差平方和),SSE越小表示數據點越接近於它們的質心,聚類效果也越好。因此,要對聚類的結果進行改進的方法可以將具有最大SSE值的簇划分為兩個簇,同時為了保持簇總數不變,可以將某兩個簇進行合並。
