K-Means聚类算法
1、原理
第一步:选K个初始聚类中心,z1(1),z2(1),…,zK(1),其中括号内的序号为寻找聚类中心的迭代运算的次序号。聚类中心的向量值可任意设定,例如可选开始的K个模式样本的向量值作为初始聚类中心。
第二步:逐个将需分类的模式样本{x}按最小距离准则分配给K个聚类中心中的某一个zj(1)。
假设i=j时, ,则 ,其中k为迭代运算的次序号,第一次迭代k=1,Sj表示第j个聚类,其聚类中心为zj。
第三步:计算各个聚类中心的新的向量值,zj(k+1),j=1,2,…,K
求各聚类域中所包含样本的均值向量:
其中Nj为第j个聚类域Sj中所包含的样本个数。以均值向量作为新的聚类中心,可使如下聚类准则函数最小:
在这一步中要分别计算K个聚类中的样本均值向量,所以称之为K-均值算法。
第四步:若 ,j=1,2,…,K,则返回第二步,将模式样本逐个重新分类,重复迭代运算;
若 ,j=1,2,…,K,则算法收敛,计算结束。
2、程序实现
def biKmeans(dataSet, k, distMeas=distEclud):
m = shape(dataSet)[0]
# 这里第一列为类别,第二列为SSE
clusterAssment = mat(zeros((m,2)))
# 看成一个簇是的质心
centroid0 = mean(dataSet, axis=0).tolist()[0]
centList =[centroid0] #create a list with one centroid
for j in range(m): #计算只有一个簇是的误差
clusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:])**2
# 核心代码
while (len(centList) < k):
lowestSSE = inf
# 对于每一个质心,尝试的进行划分
for i in range(len(centList)):
# 得到属于该质心的数据
ptsInCurrCluster =\ dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]
# 对该质心划分成两类
centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)
# 计算该簇划分后的SSE
sseSplit = sum(splitClustAss[:,1])
# 没有参与划分的簇的SSE
sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])
print "sseSplit, and notSplit: ",sseSplit,sseNotSplit
# 寻找最小的SSE进行划分
# 即对哪一个簇进行划分后SSE最小
if (sseSplit + sseNotSplit) < lowestSSE:
bestCentToSplit = i
bestNewCents = centroidMat
bestClustAss = splitClustAss.copy()
lowestSSE = sseSplit + sseNotSplit
# 较难理解的部分
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[bestCentToSplit] = bestNewCents[0,:].tolist()[0]#replace a centroid with two best centroids
centList.append(bestNewCents[1,:].tolist()[0])
clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss#reassign new clusters, and SSE
return mat(centList), clusterAssment
3、结论1. K值需要预先给定,属于预先知识,很多情况下K值的估计是非常困难的,对于像计算全部微信用户的交往圈这样的场景就完全的没办法用K-Means进行。对于可以确定K值不会太大但不明确精确的K值的场景,可以进行迭代运算,然后找出Cost Function最小时所对应的K值,这个值往往能较好的描述有多少个簇类。
2. K-Means算法对初始选取的聚类中心点是敏感的,不同的随机种子点得到的聚类结果完全不同
3. K均值算法并不是很所有的数据类型。它不能处理非球形簇、不同尺寸和不同密度的簇,银冠指定足够大的簇的个数是他通常可以发现纯子簇。
4. 对离群点的数据进行聚类时,K均值也有问题,这种情况下,离群点检测和删除有很大的帮助。