K-均值聚类算法学习心得

一、分类与聚类

  分类作为一种监督学习方法,要求必须事先明确知道各个类别的信息,并且断言所有待分类项都有一个类别与之对应。但是很多时候上述条件得不到满足,尤其是在处理海量数据的时候,如果通过预处理使得数据满足分类算法的要求,则代价非常大,这时候可以考虑使用聚类算法。聚类属于无监督学习,相比于分类,聚类不依赖预定义的类和类标号的训练实例。由于具有出色的速度和良好的可扩展性,Kmeans聚类算法算得上是最著名的聚类方法。

二、K均值聚类算法

2.1算法原理 

 Kmeans算法是一个重复移动类中心点的过程,把簇的中心点,也称重心(centroids),移动到其包含成员的平均位置,然后重新划分其内部成员。k是算法计算出的超参数,表示簇的数量;Kmeans可以自动分配样本到不同的簇,但是不能决定究竟要分几个簇。k必须是一个比训练集样本数小的正整数。如果用数据表达式表示,假设簇划分之间的随机数为k,则我们的目标是最小化平方误差E:

《K-均值聚类算法学习心得》

  μi是第k个簇的重心位置。E是各个类畸变程度(distortions)之和。每个簇的畸变程度等于该簇重心与其内部成员位置距离的平方和。若簇内部的成员彼此间越紧凑则簇的畸变程度越小,反之,若簇内部的成员彼此间越分散则类的畸变程度越大。求解成本函数最小化的参数就是一个重复配置每个簇包含的观测值,并不断移动簇重心的过程。首先,簇的重心是随机确定的位置。实际上,重心位置等于随机选择的观测值的位置。每次迭代的时候,Kmeans会把观测值分配到离它们最近的簇,然后把重心移动到该簇全部成员位置的平均值那里。

简而言之,k均值聚类算法由以下四步组成:

首先,随机选定k个初始类簇中心(不同的类簇中心会导致收敛速度和聚类结果有差别,有可能会陷入局部最优.)  

其次,计算每个点到每个类簇中心的距离,并将其分配到最近的类簇中  

第三,重新计算每个类簇的中心  

第四,重复第二步和第三步直到类簇中心不再发生变化,聚类停止

2.2代码实现

from numpy import *
import numpy as np  

#导入数据  
def loadDataSet(fileName):      #general function to parse tab -delimited floats  
    dataMat = []                #assume last column is target value  
    fr = open(fileName)  
    for line in fr.readlines():  
        curLine = line.strip().split('\t')   
        fltLine = list(map(float,curLine)) #map all elements to float()  
        #书上程序因为py2和3的的差别在这里没有对map进行一个定义,使用py3是需在map前加list
        dataMat.append(fltLine)  
    return dataMat  
  
def distEclud(vecA, vecB):  
    return sqrt(sum(power(vecA - vecB, 2))) #la.norm(vecA-vecB)  

#构建簇的质心  
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  
      
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):#寻找最近的质心  
            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  
  
#绘制图形程序  
import matplotlib.pyplot as plt  
def draw(dataMat,centroids,clusterAssment):  
    k=len(centroids)  
    fig = plt.figure()    
    ax = fig.add_subplot(111)    
    ax.scatter(centroids[:,0].tolist(),centroids[:,1].tolist(),marker='+',c='r')    
    markers=['o','s','v','*'];colors=['blue','green','yellow','red']    
    for i in range(k):    
        data_class=dataMat[nonzero(clusterAssment[:,0].A == i)[0]]    
        ax.scatter(data_class[:,0].tolist(),data_class[:,1].tolist(),marker=markers[i],c=colors[i])    
    plt.show()  
    
if __name__ =="__main__":    
    dataMat = mat(loadDataSet('testSet.txt'))        
    myCentroids, clustAssing = kMeans(dataMat,4)       
    draw(dataMat,myCentroids, clustAssing)

当选择簇的个数为4时绘制散点图图形如下:

《K-均值聚类算法学习心得》

当选择簇的个数为3时绘制散点图图形如下:

《K-均值聚类算法学习心得》

由此可以看出对此数据集而言,由四个质心所绘制的散点图更为合理,那么也就引出了接下来的问题,我们需要一种方法来确定我们的K值所选是正确的,才能生成最好的簇。

三、K值的确定方式

3.1肘部法则

  如果问题中没有指定k的值,可以通过肘部法则这一技术来估计聚类数量。肘部法则会把不同k值的成本函数值画出来。随着kk值的增大,平均畸变程度会减小;每个类包含的样本数会减少,于是样本离其重心会更近。但是,随着kk值继续增大,平均畸变程度的改善效果会不断减低。kk值增大过程中,畸变程度的改善效果下降幅度最大的位置对应的kk值就是肘部。为了让读者看的更加明白,下面让我们通过一张图用肘部法则来确定最佳的k值。下图数据明显可分成两类:

《K-均值聚类算法学习心得》

从图中可以看出,k值从1到2时,平均畸变程度变化最大。超过2以后,平均畸变程度变化显著降低。因此最佳的k是2。

3.2与层次聚类结合

  经常会产生较好的聚类结果的一个有趣策略是,首先采用层次凝聚算法决定结果粗的数目,并找到一个初始聚类,然后用迭代重定位来改进该聚类。

3.3 稳定性方法

  稳定性方法对一个数据集进行2次重采样产生2个数据子集,再用相同的聚类算法对2个数据子集进行聚类,产生2个具有kk个聚类的聚类结果,计算2个聚类结果的相似度的分布情况。2个聚类结果具有高的相似度说明kk个聚类反映了稳定的聚类结构,其相似度可以用来估计聚类个数。采用次方法试探多个k,找到合适的k值。

四、二份K均值算法

4.1算法原理

  为了克服k-均值算法的局部最小值问题,有人提出了二分k均值算法。该算法首先将所有点作为一个簇,然后讲该簇一分为二。之后选择其中一个簇继续划分,选择哪一个簇进行划分取决于对其划分是否可以最大程度降低SSE的值。上述基于SSE的划分过程不断重复,直到得到用户指定的簇数目为止。

4.2代码实现

def biKmeans(dataSet, k, distMeas=distEclud):
    m = shape(dataSet)[0]
    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):#calc initial Error
        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],:]#get the data points currently in cluster i
            centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)
            sseSplit = sum(splitClustAss[:,1])#compare the SSE to the currrent minimum
            sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])
            print ("sseSplit, and notSplit: ",sseSplit,sseNotSplit)
            if (sseSplit + sseNotSplit) < lowestSSE:
                bestCentToSplit = i
                bestNewCents = centroidMat
                bestClustAss = splitClustAss.copy()
                lowestSSE = sseSplit + sseNotSplit
        bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) #更新簇的分配结果
        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

仍然使用先前所使用的数据集,绘制散点图如下:

《K-均值聚类算法学习心得》

由此可见,相较于k均值算法而言,二份K均值算法在本数据集上的聚类更加成功。

五、算法总结

kmean算法的特点是不能保证该算法收敛域全局最优解,并且它常常终止于一个局部最优解。结果可能依赖于初始簇中心的随机选择,所以为了尽可能的得到好的结果,我们通常会选择不同的初始簇中心,来多疑运行K-均值算法。

算法优点:

1)原理比较简单,实现也是很容易,收敛速度快。

2)聚类效果较优。

3)算法的可解释度比较强。

4)主要需要调参的参数仅仅是簇数k。

算法缺点:

1)K值的选取不好把握 

2)对于不是凸的数据集比较难收敛

3)如果各隐含类别的数据不平衡,比如各隐含类别的数据量严重失衡,或者各隐含类别的方差不同,则聚类效果不佳。

4) 采用迭代方法,得到的结果只是局部最优。


参考资料:

《机器学习实战》  ——Peter Harrington著  人民邮电出版社

从零开始实现Kmeans聚类算法——CSDN博客  https://blog.csdn.net/u013719780/article/details/78413770

《机器学习》—— 周志华 著  清华大学出版社

    原文作者:聚类算法
    原文地址: https://blog.csdn.net/AmbiRF/article/details/80581560
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞