簡介
又叫K-均值算法,是非監督學習中的聚類算法。
基本思想
k-means算法比較簡單。在k-means算法中,用cluster來表示簇;容易證明k-means算法收斂等同於所有質心不再發生變化。基本的k-means算法流程如下:
選取k個初始質心(作爲初始cluster,每個初始cluster只包含一個點);
repeat:
對每個樣本點,計算得到距其最近的質心,將其類別標爲該質心所對應的cluster;
重新計算k個cluster對應的質心(質心是cluster中樣本點的均值);
until 質心不再發生變化
repeat的次數決定了算法的迭代次數。實際上,k-means的本質是最小化目標函數,目標函數爲每個點到其簇質心的距離的平方和:
N是元素個數,x表示元素,c(j)表示第j簇的質心
即:
初始化選擇 k 個質心 (隨機選擇, 或者領域知識選擇), 該選擇決定了聚類的速度與效果
爲所有點劃分簇
計算該種劃分的損失
當該損失還能減小時循環以下:
對於每個質心(k個)執行:
a. 隨機選擇一個新的點
b. 以新的點代替這個質心
c. 重新對所有數據劃分簇
d. 計算劃分的損失
e. 若 新的損失小於原先的損失, 則 用新的點代替原質心
算法複雜度
時間複雜度是O(nkt) ,其中n代表元素個數,t代表算法迭代的次數,k代表簇的數目
優缺點
優點
簡單、快速;
對大數據集有較高的效率並且是可伸縮性的;
時間複雜度近於線性,適合挖掘大規模數據集。
缺點
k-means是局部最優,因而對初始質心的選取敏感;
選擇能達到目標函數最優的k值是非常困難的。
代碼
代碼已在github上實現,這裏也貼出來
# coding:utf-8
import numpy as np
import matplotlib.pyplot as plt
def loadDataSet(fileName):
'''
加載測試數據集,返回一個列表,列表的元素是一個座標
'''
dataList = []
with open(fileName) as fr:
for line in fr.readlines():
curLine = line.strip().split('\t')
fltLine = list(map(float,curLine))
dataList.append(fltLine)
return dataList
def randCent(dataSet, k):
'''
隨機生成k個初始的質心
'''
n = np.shape(dataSet)[1] # n表示數據集的維度
centroids = np.mat(np.zeros((k,n)))
for j in range(n):
minJ = min(dataSet[:,j])
rangeJ = float(max(dataSet[:,j]) - minJ)
centroids[:,j] = np.mat(minJ + rangeJ * np.random.rand(k,1))
return centroids
def kMeans(dataSet, k):
'''
KMeans算法,返回最終的質心座標和每個點所在的簇
'''
m = np.shape(dataSet)[0] # m表示數據集的長度(個數)
clusterAssment = np.mat(np.zeros((m,2)))
centroids = randCent(dataSet, k) # 保存k個初始質心的座標
clusterChanged = True
iterIndex=1 # 迭代次數
while clusterChanged:
clusterChanged = False
for i in range(m):
minDist = np.inf; minIndex = -1
for j in range(k):
distJI = np.linalg.norm(np.array(centroids[j,:])-np.array(dataSet[i,:]))
if distJI < minDist:
minDist = distJI; minIndex = j
if clusterAssment[i,0] != minIndex: clusterChanged = True
clusterAssment[i,:] = minIndex,minDist**2
print("第%d次迭代後%d個質心的座標:\n%s"%(iterIndex,k,centroids)) # 第一次迭代的質心座標就是初始的質心座標
iterIndex+=1
for cent in range(k):
ptsInClust = dataSet[np.nonzero(clusterAssment[:,0].A==cent)[0]]#get all the point in this cluster
centroids[cent,:] = np.mean(ptsInClust, axis=0)
return centroids, clusterAssment
def showCluster(dataSet, k, centroids, clusterAssment):
'''
數據可視化,只能畫二維的圖(若是三維的座標圖則直接返回1)
'''
numSamples, dim = dataSet.shape
if dim != 2:
return 1
mark = ['or', 'ob', 'og', 'ok','oy','om','oc', '^r', '+r', 'sr', 'dr', '<r', 'pr']
# draw all samples
for i in range(numSamples):
markIndex = int(clusterAssment[i, 0])
plt.plot(dataSet[i, 0], dataSet[i, 1], mark[markIndex])
mark = ['Pr', 'Pb', 'Pg', 'Pk','Py','Pm','Pc','^b', '+b', 'sb', 'db', '<b', 'pb']
# draw the centroids
for i in range(k):
plt.plot(centroids[i, 0], centroids[i, 1], mark[i], markersize = 12)
plt.show()
if __name__ == '__main__':
dataMat = np.mat(loadDataSet('./testSet')) #mat是numpy中的函數,將列表轉化成矩陣
k = 4 # 選定k值,也就是簇的個數(可以指定爲其他數)
cent, clust = kMeans(dataMat, k)
showCluster(dataMat, k, cent, clust)
測試地址:https://github.com/fuqiuai/datamining_algorithms/blob/master/K-means/testSet