用代码聊算法之k-means算法

来源:互联网 发布:mysql登录密码设置密码 编辑:程序博客网 时间:2024/06/06 02:40

一、简介

        k-means是一种常见的无监督机器学习算法,众所周知,无监督学习的分类往往是需要先聚类再分类的,而k-means算法就是最常见,使用频率最高的聚类算法。其核心思想是把一些未标注的数据(无标签)分类称为k类不相同的簇中(cluster),算法k-means名称中的k就是所分成的簇的数量。而同一簇之中的对象都是有某些相似之处,当簇中的点越相似,我们的分类效果就越好。每个簇的中心点是由该簇所有点的欧氏距离的平均值计算而成,因此也就得名means。

k-means除了应用在对无标签的数据进行聚类分类之外,也常常应用于有监督学习进行训练之前的对数据集特征的探索和表征。

k-means算法中的k一般是手动指定的,换句话说k是算法的输入,稍后我们会讨论如何不指定k值的情况下开始我们的算法。

二、数学表示

随机选择k个点作为起始质心(centroid)
循环质心(初始质心开始):
        对于数据集中到每一个点计算其到所有质心到的距离,把它归类于该质心的簇。
        移动质心,把每一簇之中所有点的平均值作新的质心
循环终止条件:
        若某次循环新质心与老质心位置一致,则循环结束

三、代码


1、两个点之间距离(欧氏距离)的计算函数:
def distEclud(vecA,vecB):    return np.sqrt(sum(vecA-vecB,2))

2、为数据集创建k个随机质心的函数:
def randCent(dataSet,k=2):    n=np.shape(dataSet)[1]    centroids=np.matrix(np.zeros(n,k))    for j in range(n):        minJ=min(dataSet[:,j])        rangeJ=float(max(dataSet[:,j])-minJ)        centroids[:,j]=minJ+rangeJ*np.random.rand(k,1)    return centroids


3、k-means主算法:
def kmeans (dataSet,k=2,distMess=distEclud,createCent=randCent):    m=np.shape(dataSet)[0]    clusterAssment = np.mat(np.zeros((m,2)))    centroids=createCent(dataSet,k)    clusterChanged = True    while clusterChanged:        clusterChanged = False        for i in range(m):            minDist = np.inf;            minIndex = -1;            for j in range(k):                distJI=distMess(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):            # 更新质心的位置            ptsInClust = dataSet[np.nonzero()]            centroids[cent,:]=np.mean(ptsInClust,axis=0)    return centroids,clusterAssment



四、实现

        上面的python代码是参考《机器学习实战》一书写出的代码,但是在实际工作之中,我们更多的是使用scikit-learn提供的k-means算法,scikit-learn是一个在机器学习领域被广泛应用的科学计算工具包,该项目最早由数据科学家 David Cournapeau 在2007 年发起,需要NumPy和SciPy等其他包的支持,是Python语言中专门针对机器学习应用而发展起来的一款开源框架。
      

        下面贴出scikit-learn的k-means示例代码[2]:

>>> from sklearn.cluster import KMeans>>> import numpy as np>>> X = np.array([[1, 2], [1, 4], [1, 0],...               [4, 2], [4, 4], [4, 0]])>>> kmeans = KMeans(n_clusters=2, random_state=0).fit(X)>>> kmeans.labels_array([0, 0, 0, 1, 1, 1], dtype=int32)>>> kmeans.predict([[0, 0], [4, 4]])array([0, 1], dtype=int32)>>> kmeans.cluster_centers_array([[ 1.,  2.],       [ 4.,  2.]])

scikit-learn的sklearn.cluster提供了一个完整的k-means算法的实现KMeans,其中参数的含义如下:

1、n_clusters:k值,即聚类簇的数量,该值默认值是8

2、max_iter:每次执行k-means算法单次最大循环次数,即终止条件,以防止迭代次数过多循环仍不结束,在最优解附近徘徊的情况,该值也是可选的,默认值300

3、n_init:随机生成质心最大次数,由于初始质心是随机生成的,每次生成有可能效果不好,故应循环n次,取效果最好的一次结果最为随机质心,该值默认值是10

4、init: 初始点的默认生成方法,可以选择"k-means++","random"或者是一个ndarray,默认初始化方法是"k-means++"方法,关于"k-means++"的初始化方法我们会在下面优化的部分进一步描述

5、copy_x:对数据进行处理时,是否复制,若True,则会复制一份数据处理,反之则会在原数据上进行处理,默认值是True

       除此之外还有algorithm、precompute_distances、toln_jobs、random_state、verbose等参数,对于本文讨论的算法原理关系不是很大,有兴趣的朋友可以点击这里自行了解。


调用了KMeans()方法之后,就可以进一步调用Kmeans模块的其他方法了,诸如:

1、fit(X[,y])方法:计算X的聚类(簇)中心,其中X是传入的矩阵(matrix或ndarray,矩阵的行是数据,列是特征),y是可省略的标签,默认值是none

2、fit_predict(X[,y])方法:对数据X进行聚类,并对X中每一条数据进行分类,相当于对X调用fit()方法,再调用label()方法

3、fit_transform(X[,y])方法:对数据X进行聚类,返回聚类中心距离矩阵(返回矩阵的每一行都是数据X中所有点到k个中心点的距离),该方法等同于调用fit().translate()方法

4、get_params()方法:可返回KMeans()方法中设定的参数

5、predict()方法:预测方法,可预测新数据属于哪一类,要求输入的数据是一个拥有相同特征数的matrix、ndarry或list



对于返回的结果kmeans,则有如下几个属性:

1、cluster_centers_:聚类中心点坐标,会返回k个数组,每个数组对应一类聚类中心点

2、labels_ :每个数据的标签,这些数据在最终的训练结果会被分为k个不同的类别(簇),而这个ndarray则会返回每一行数据对应的标签

3、inertia_:数据库中每个点到它对应的聚类中心的点的距离之和,可用来衡量分类的好坏


五 、优化

       那么如果掌握上面的方法,我们的k-means算法就算基本可以使用了,而且算法原理部分也就基本结束了。但是请各位读者仔细想想,我们的算法似乎还有那么一点点不完美的地方。那么到目前为止,我们的k-means算法的缺陷究竟是哪里呢?通过看sklearn的KMeans()方法中的参数我们似乎也可以得之一二:
第一、我们的算法过度依赖随机产生的初始点
第二、需要手动选择聚类中心(簇)的数量k

       接下来我们就对这几个可能存在的缺陷进行一下算法上的改进:
       首先、我们算法的优劣性很大程度上取决于我们随机产生的初始化中心点,当随机产生的中心点恰好在真实的中心点附近的时候,我们的算法可能很快就可以迭代完成。但是,当我们随机产生的初始中心点距离真实的中心点不是很近的时候,我们的算法可能需要很多次的迭代才可以取得最优解,这对于计算资源是一种很大的浪费,而且如果初始中心点不理想,我们的算法可能只能取到局部最优解,而无法取得全局最优解,甚至有可能发生在最优解附近徘徊而无法取得最优解的极端情况。对此,我们常用的解决办法就是多次进行随机初始化中心点,然后对每一次随机初始化进行整个完整的k-means算法,再在其中选择效果最好(具体的评价标准我们后边会讲)作为本次k-means的初始点。但是这种方法,只有在k的值比较小的时候,可以取得较好的改善方法,如果k很大,我们则需要采取其他的方法了。

       那就是k-means++算法,k-means++算法是一种随机初始化中心点的优化算法,其步骤是:
                1、从数据集中随机选择一个点作为中心点
                2、对于数据集中的其他点,计算其与最近的中心点的距离D(x)
                3、在数据集的其他点中选择一个点作为中心点,选择原则是D(x)越大,被选中的概率越大
                4、重复步骤2、3直到选出k的聚类中心点
      这样可以以较大的概率得到分布比较均匀的聚类中心点,也就可以摆脱初始中心点过于集中的问题,因而也可以改进算法。


       对于k的数量的选择,有一个经典的方法叫“肘部法则”,Andrew教授的视频有详细的叙述,在此我大概复述如下:

       我们对于不同的k值运行k-means算法,得到对应的代价函数(评价指标,后面会讲),k依次增大,代价函数J依次减小,我们列出以k为横坐标,J为纵坐标的折线图,得到的图像大概如下所示:



图1.1 k-means“肘部法则”

      我们可以看到整个折线图就像人的手肘(elbow)一样,当过了肘点,代价函数的值没有很大的下降,而在肘点之前,代价函数的值在迅速降低,那么我们认为超过肘点再进行训练,消耗的系统资源和计算能力可能不能得到对应的收益,因此我们就认为肘点的k值是适合这个数据集的k值。

       除此之外,还有一种二分-kmeans算法,可以不指定聚类中心点数k,其实现思想如下:
       首先将所有数据集作为一个簇,然后将该簇一分为二。之后将熵值最大(误差平方和)的簇划分为两个簇,然后重复此过程,直到达到循环终止条件。循环终止的条件有很多,诸如簇的数量到达k,熵值小于某阈值等。二分-kmeans算法的优点在于,他不用每次计算所有的点以及到他们中心点的距离,因此可以大大减少计算量。而且由于不存在随机初始中心点的选取,减小了很多由于随机初始中心点带来的不确定性。 

六、衡量标准

     我们知道所有的机器学习算法都有一个衡量标准,来衡量算法的好坏,并作为优化的目标,一半这个衡量算法优劣的被称为代价函数(Lost Function)、也有叫误差函数(Error Function)的。k-means算法的代价函数的表达形式如下:

       其中 μc(i)代表与 x(i)最近的聚类中心点,公式表示代价函数J等于数据集中所有点到它对应的中心点的欧氏距离之和。我们的的优化目标便是找出使得代价函数最小 的 c(1),c(2),...,c(m)和 μ1,μ2,...,μk。
有了代价函数我们就可以评价算法的优劣、并依此选择算法中的参数等其他了。
       






        
       









原创粉丝点击