机器学习:KMeans
引言
k-Means很早就接触了,大四做本科毕设的时候就用的k-Means,最近从新翻到机器学习实战书中讲到,再结合这几年看到的相关的文章,谈一谈KMeans
算法流程
首先数据集中的每个样本向量可看作高维空间中的一个点
那么我们开始的时候可以从数据集中任意选取K个数据点作为初始类中心,也可以创建符合在数据集范围中的k个质心,注意,这里的k个质心可能不是真实存在的k个点(机器学习实战中即是随机生成的在数据集范围内的k个质心),因为之后质心都要重新计算,所以这里并无不可
然后将每个数据点指派到离它最近的最近的类中,形成k个簇,这里要计算该点到其余所有类中心的距离
重新计算每个簇的类中心
直到簇不发生变化或达到最大迭代次数
算法复杂度
时间复杂度:O(tkmn) --- t为迭代次数,k为簇的数目,n为样本数,m为维数
空间复杂度:O(nm)
一般t,k,m均可认为是常量,所以时间和空间复杂度可以简化为O(n),即线性的
算法实现
首先是随机产生k个初始化类中心
def randCent(dataSet, k):
n = shape(dataSet)[1]
centroids = mat(zeros((k,n)))#create centroid mat
for j in range(n):#create random cluster centers, within bounds of each dimension
minJ = min(dataSet[:,j])
rangeJ = float(max(dataSet[:,j]) - minJ)
centroids[:,j] = mat(minJ + rangeJ * random.rand(k,1))
return centroids
该函数主要是随机生成每一维上最大最小值之间的一个值作为该维度上的数值,每一维上都产生一个值,组成一个质心点,共生成k个质心点
然后就是每个点的分配问题
在KMeans的主函数中定义一个clusterChanged,初始化为true,只要类还在变化,就一直迭代,直至类不再变化为止
对m个样本集中的每一个样本i进行循环,将样本i与k个质心都求一个距离,找到最小距离的质心,将该样本分配给这个质心所在的类
这里程序中用了一个clusterAssment矩阵,一个m×2的矩阵存储m个样本的类别以及样本到该质心的距离
重新计算质心
分配完后,重新计算质心
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
m = shape(dataSet)[0]
clusterAssment = mat(zeros((m,2)))#create mat to assign data points
#to a centroid, also holds SE of each point
centroids = createCent(dataSet, k)
clusterChanged = True
while clusterChanged:
clusterChanged = False
for i in range(m):#for each data point assign it to the closest centroid
minDist = 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
clusterAssment[i,:] = minIndex,minDist**2
print centroids
for cent in range(k):#recalculate centroids
ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#get all the point in this cluster
centroids[cent,:] = mean(ptsInClust, axis=0) #assign centroid to mean
return centroids, clusterAssment
衡量聚类好坏
一个度量聚类结果好坏的指标是SSE,误差平方和,也就是之前在clusterAssment矩阵中存的每个样本到质心的距离,SSE越小,代表数据点越接近它们的质心,聚类结果越好
确定K值的方法
多次运行尝试,寻找SSE最小的K值
常用的方法是多次运行,每次使用一组不同的随机初始质心,然后选择具有最小SSE(误差平方和)的簇集 >>> 简单但效果可能不好
在实际应用中,由于Kmean一般作为数据预处理,或者用于辅助分类贴标签。所以k一般不会设置很大。可以通过枚举,令k从2到一个固定值如10,在每个k值上重复运行数次kmeans(避免局部最优解),并计算当前k的SSE,最后选SSE最小的值对应的k作为最终的集群数目。
与层次聚类的结合
首先采用层次聚类算法决定结果中簇大概的数目,并找到一个初始聚类,然后用迭代重定位来改进该聚类
适用情况是(1)样本相对较小例如数百到数千,主要是层次聚类开销较大(2)k相对于样本大小较小
使用canopy算法进行初始划分
Canopy聚类在第一阶段选择简单、计算代价较低的方法计算对象相似性,将相似的对象放在一个子集中,这个子集被叫做Canopy ,通过一系列计算得到若干个Canopy
根据得到的Canopy个数,可以大致推断K的取值,避免了K的盲目性
Canopy算法
初始,我们有一组点集S,预设两个距离阈值,T1>T2
选择一个点P,计算它与S中其他点的距离(这里采用成本低的计算方法),并将此点作为此canopy的质心
将与P距离为T1以内的点放入该Canopy中,同时删除S中与此点P距离在T2内的点
这里是为了保证和中心P距离在 T2 以内的点不能再作为其他 Canopy 的中心
再从S中剩余的点中选取新的Canopy质心,最后点会形成如下形式
空聚类的处理
如果所有的点在指派过程中都没有分配到某个簇,就会得到空簇
这种情况下需要替补质心,否则平方误差将会偏大
一种方法是选择一个距离当前任何质心最远的点,这将消除当前对总平方误差影响最大的点
另一种方法是从具有最大平方误差的簇中选择一个替补的质心,这将分裂簇并降低聚类的总平方误差
适用范围
簇与簇之间区别明显,簇大小相近时,结果较理想
因为时间复杂度是tkmn的,与样本数量线性相关,所以,对于处理大数据集合,该算法非常高效,且伸缩性较好
但算法对K与初始聚类中心敏感,经常以局部最优结束
同时对噪声和孤立点敏感