K-Means


版权声明:本文由callback发布于http://blog.csdn.net/u010248552/article/details/78476934, 本文可以被全部的转载或者部分使用,但请注明出处,如果有问题,请留言站内。谢谢合作!

#前言

KMeans算法试图使集群数据分为 n n n组独立数据样本,使n组集群间的方差相等,数学描述为最小化惯性或集群内的平方和。该算法的缺点在于需要提前确定数据集群的类别 n n n,虽然有些预处理算法可以确定 n n n的取值,但是会增加很大一部分的计算复杂度。规模适合大量的样品,被使用在在许多不同的领域。

在介绍 k-means 的具体步骤之前,让我们先来看看它对于需要进行聚类的数据的一个基本假设吧:对于每一个 cluster ,我们可以选出一个中心点(center),使得该cluster中的所有的点到该中心点的距离小于到其他 cluster 的中心的距离。虽然实际情况中得到的数据并不能保证总是满足这样的约束,但这通常已经是我们所能达到的最好的结果,而那些误差通常是固有存在的或者问题本身的不可分性造成的。例如下图所示的两个高斯分布,从两个分布中随机地抽取一些数据点出来,混杂到一起,现在要让你将这些混杂在一起的数据点按照它们被生成的那个分布分开来:

gaussian

由于这两个分布本身有很大一部分重叠在一起了,例如,对于数据点2.5来说,它由两个分布产生的概率都是相等的,你所做的只能是一个猜测;稍微好一点的情况是2,通常我们会将它归类为左边的那个分布,因为概率大一些,然而此时它由右边的分布生成的概率仍然是比较大的,我们仍然有不小的几率会猜错。而整个阴影部分是我们所能达到的最小的猜错的概率,这来自于问题本身的不可分性,无法避免。因此,我们将 k-means 所依赖的这个假设看作是合理的。

#数学建模

k-means算法将 n n n组样本划分为不相交的集群 C k C_k Ck每个集群中用样本的均值 μ k \mu_k μk所描述的。这些均值通常称为集群“重心”(加权即为重心,不加权就是中心)。请注意,一般来说,他们不是每个集群 C k C_k Ck中的点,尽管重心和 C k C_k Ck中的点在同一个空间。k - means算法旨在选择重心,减少惯性,或集群内平方和的准则:

J = ∑ n = 1 N ∑ k = 1 K r n k ∥ x n − μ k ∥ 2 \displaystyle J = \sum_{n=1}^N\sum_{k=1}^K r_{nk} \|x_n-\mu_k\|^2 J=n=1Nk=1Krnkxnμk2

这个函数,其中 r n k r_{nk} rnk在数据点 $n $被归类到 $cluster k $的时候为 1 ,否则为 0 。直接寻找 r n k r_{nk} rnk 和$ \mu_k $来最小化 J 并不容易,不过我们可以采取迭代的办法:先固定 μ k \mu_k μk ,选择最优的$ r_{nk}$ ,很容易看出,只要将数据点归类到离他最近的那个中心就能保证 J 最小。下一步则固定$ r_{nk}$,再求最优的 μ k \mu_k μk。将$ J$ 对$ \mu_k$ 求导并令导数等于零,很容易得到 $J $最小的时候 μ k \mu_k μk 应该满足:
μ k = ∑ n r n k x n ∑ n r n k \displaystyle \mu_k=\frac{\sum_n r_{nk}x_n}{\sum_n r_{nk}} μk=nrnknrnkxn

亦即 $\mu_k $的值应当是所有 cluster k 中的数据点的平均值。由于每一次迭代都是取到 J 的最小值,因此 J 只会不断地减小(或者不变),而不会增加,这保证了 k-means 最终会到达一个极小值。虽然 k-means 并不能保证总是能得到全局最优解,但是对于这样的问题,像 k-means 这种复杂度的算法,这样的结果已经是很不错的了。

惯性或集群内平方和准则,可以被视为衡量内部是如何耦合的标准。它存在各种各样的缺点:

  1. 惯性必须假设集群数据是凸和各向同性的,然而数据并非总是能保持这个特性。细长的集群,或不规则形状将响应差。
  2. 惯性不是一个标准化的指标:我们只知道更低的值是更好的和零是最优的。但在高维空间,欧几里得距离往往会变得膨胀(这是所谓的“维数的诅咒”的一个实例)。在使用k-means聚类之前运用如PCA降维可以缓解这个问题,加速计算。
    kmeans

k-means是基于欧几里得距离公式来计算的,所以它是适用于任何高维空间的,当然这个思想其实可以扩展到任意可以用来评价相似性的基础公式,如切比雪夫距离和相关性公式等。为了方便展示,下面就使用二维空间说明。

#算法
##算法说明:

  1. 选定 K 个中心 μ k \mu_k μk的初值。这个过程通常是针对具体的问题有一些启发式的选取方法,或者大多数情况下采用随机选取的办法。因为前面说过k-means并不能保证全局最优,而是否能收敛到全局最优解其实和初值的选取有很大的关系,所以有时候我们会多次选取初值跑 k-means,并取其中最好的一次结果。
  2. 将每个数据点归类到离它最近的那个中心点所代表的 cluster 中。
  3. 用公式 μ k = 1 N k ∑ j ∈ cluster k x j \mu_k = \frac{1}{N_k}\sum_{j\in\text{cluster}_k}x_j μk=Nk1jclusterkxj计算出每个 cluster 的新的中心点。
  4. 重复第二步,一直到迭代了最大的步数或者前后的 J 的值相差小于一个阈值为止。

##代码

%% kmeans_function
%% author: callback 17.11.7


%N是数据一共分多少类
%data是输入的不带分类标号的数据
%u是每一类的中心
%re是返回的带分类标号的数据
function [re,u ]=KMeans(data,N)   
    [m, n]=size(data);   %m是数据个数,n是数据维数
    ma=zeros(n);        %每一维最大的数
    mi=zeros(n);        %每一维最小的数
    u=zeros(N,n);       %随机初始化,最终迭代到每一类的中心位置
    for i=1:n
       ma(i)=max(data(:,i));    %每一维最大的数
       mi(i)=min(data(:,i));    %每一维最小的数
       for j=1:N
            u(j,i)=ma(i)+(mi(i)-ma(i))*rand();  %随机初始化,不过还是在每一维[min max]中初始化好些
       end      
    end

    while 1
        pre_u=u;            %上一次求得的中心位置
        for i=1:N
            tmp{i}=[];      % 公式一中的x(i)-uj,为公式一实现做准备
            for j=1:m
                tmp{i}=[tmp{i};data(j,:)-u(i,:)];
            end
        end

        quan=zeros(m,N);
        for i=1:m        %公式一的实现
            c=[];
            for j=1:N
                c=[c norm(tmp{j}(i,:))];
            end
            [junk index]=min(c);
            %quan(i,index)=norm(tmp{index}(i,:)); 
            quan(i,index)=1;
        end

        for i=1:N            %公式二的实现
           for j=1:n
                u(i,j)=sum(quan(:,i).*data(:,j))/sum(quan(:,i));
           end           
        end

        if norm(pre_u-u)<0.1  %不断迭代直到位置不再变化,也可以在while设定个循环多少次退出
            break;
        end
    end

    re=[];
    for i=1:m
        tmp=[];
        for j=1:N
            tmp=[tmp norm(data(i,:)-u(j,:))];
        end
        [junk index]=min(tmp);
        re=[re;data(i,:) index];
    end
    re = re(:,end);
end

上述为k-means的具体实现,下面为k-means的实验结果:

%% kmeans_test
%% author: callback 17.11.7

clear 
close all
clc

load data2000.mat
color{1}='r.';
color{2}='b.';
color{3}='k.';
color{4}='g.';

color1{1}='rx';
color1{2}='bx';
color1{3}='kx';
color1{4}='gx';
% color = {'r*','b*','k*','g*'};
% color1 = {'rx','bx','kx','gx'};
% cluster = zeros(size(data,1),4);
for i = 1:4
    [cluster,C] = KMeans(data,i);
for j = 1:i
    subplot(1,4,i);
    plot(data(cluster==j,1),data(cluster==j,2),color{j});
    hold on
    plot(C(j,1),C(j,2),color1{j},'MarkerSize',14,'LineWidth',2);
    hold on
end
title([num2str(i) 'clustering']);
end

kmeans_test
##Note
图中就是一个较为长条的数据,在分为1-4类时表现出来的分类结果,叉点即为每类的中心点。可以看出,kmeans是无法搜索到数据的连续性信息,它只是关注的数据间的距离关系。
前面我们谈到数据的收敛和随机性,所以当数据不是凸和各向同性的和随机的初始化,会使k-means在迭代过程中有可能出现不能得到最优解的最终结果,如下图所示。下图中第一行过程就是我们希望得到的分类结果,但是第二行的过程在迭代过程中得到了局部最优。这是我们需要注意的,我们的终止条件有几种,第一种是迭代的重心几乎不改变,第二种是迭代有限次数。当然前者比后者更容易到达全局最优。还有一种处理方法就是多次迭代,迭代的结果所有重心多次到某个(全局/局部)最优是在一定阈值范围内相同位置,我们就可以判断是否到了全局最优。当然这部分有个逻辑前提,那就是该数据集是具有凸和各向同性的。
kmeans_compare
我们会发现就我们人眼观察,当 n = 2 n=2 n=2时是最好的k值,那么这个k值的选取就是k-means使用过程中最让人烦恼的问题。为了这个预知的k值,下面将给出数种方法给出 K K K值结果和初始化指导。

优点:

1、 算法快速、简单;

2、 容易解释

3、 聚类效果中上

4、 适用于高维

缺陷:

1、 对离群点敏感,对噪声点和孤立点很敏感(通过k-centers算法可以解决)

2、 K-means算法中聚类个数k的初始化

3、初始聚类中心的选择,不同的初始点选择可能导致完全不同的聚类结果。

#初始化

思想,初始的聚类中心之间相互距离尽可能远.

##法1(kmeans++)

1、从输入的数据点集合中随机选择一个点作为第一个聚类中心

2、对于数据集中的每一个点x,计算它与最近聚类中心(指已选择的聚类中心)的距离D(x)

3、选择一个新的数据点作为新的聚类中心,选择的原则是:D(x)较大的点,被选取作为聚类中心的概率较大

4、重复2和3直到k个聚类中心被选出来

5、利用这k个初始的聚类中心来运行标准的k-means算法

从上面的算法描述上可以看到,算法的关键是第3步,如何将D(x)反映到点被选择的概率上,一种算法如下:

1、先从我们的数据库随机挑个随机点当“种子点”

2、对于每个点,我们都计算其和最近的一个“种子点”的距离D(x)并保存在一个数组里,然后把这些距离加起来得到Sum(D(x))。

3、然后,再取一个随机值,用权重的方式来取计算下一个“种子点”。这个算法的实现是,先取一个能落在Sum(D(x))中的随机值Random,然后用Random -= D(x),直到其<=0,此时的点就是下一个“种子点”。

4、重复2和3直到k个聚类中心被选出来

5、利用这k个初始的聚类中心来运行标准的k-means算法

##法2
选用层次聚类或Canopy算法进行初始聚类,然后从k个类别中分别随机选取k个点,来作为kmeans的初始聚类中心点

#K值

大数据法

《大数据》中提到:给定一个合适的类簇指标,比如平均半径或直径,只要我们假设的类簇的数目等于或者高于真实的类簇的数目时,该指标上升会很缓慢,而一旦试图得到少于真实数目的类簇时,该指标会急剧上升。
类簇的直径是指类簇内任意两点之间的最大距离。
类簇的半径是指类簇内所有点到类簇中心距离的最大值。
下图是当K的取值从2到9时,聚类效果和类簇指标的效果图:
2_9cluster
plot
2_9clusterplot
类簇指标是K个类簇的平均质心距离的加权平均值。从上图中可以明显看到,当K取值5时,类簇指标的下降趋势最快,所以K的正确取值应该是5.为以下是具体数据:

%% 2-9 cluster
2 个聚类  
所有类簇的半径的加权平均值 8.51916676443  
所有类簇的平均质心距离的加权平均值 4.82716260322  
3 个聚类  
所有类簇的半径的加权平均值 7.58444829472  
所有类簇的平均质心距离的加权平均值 3.37661824845  
4 个聚类  
所有类簇的半径的加权平均值 5.65489660064  
所有类簇的平均质心距离的加权平均值 2.22135360453  
5 个聚类  
所有类簇的半径的加权平均值 3.67478798553  
所有类簇的平均质心距离的加权平均值 1.25657641195  
6 个聚类  
所有类簇的半径的加权平均值 3.44686996398  
所有类簇的平均质心距离的加权平均值 1.20944264145  
7 个聚类  
所有类簇的半径的加权平均值 3.3036641135  
所有类簇的平均质心距离的加权平均值 1.16653919186  
8 个聚类  
所有类簇的半径的加权平均值 3.30268530308  
所有类簇的平均质心距离的加权平均值 1.11361639906  
9 个聚类  
所有类簇的半径的加权平均值 3.17924400582  
所有类簇的平均质心距离的加权平均值 1.07431888569

Gap statistic

这是一篇Robert发表在2001的数据聚类决策文章,Estimating the number of clusters in a data set via the gap statistic,从文章名字就可以知道他是提出了一种统计学方法来预知 k k k值。(等我填坑吧)

Note

k k k值的选取方法还有很多,就如[6]余文毅中所说,有基于这种方法出发的,各个领域都有很多方法解决 k k k值的选取的方法。

  1. 数据的先验知识,或者数据进行简单分析能得到。
  2. 基于变化的算法:即定义一个函数,随着K的改变,认为在正确的K时会产生极值。如Gap Statistic(Estimating the number of clusters in a data set via the gap statistic, Tibshirani, Walther, and Hastie 2001),Jump Statistic (finding the number of clusters in a data set, Sugar and James 2003)。
  3. 基于结构的算法:即比较类内距离、类间距离以确定K。这个也是最常用的办法,如使用平均轮廓系数,越趋近1聚类效果越好;如计算类内距离/类间距离,值越小越好;等。
  4. 基于一致性矩阵的算法:即认为在正确的K时,不同次聚类的结果会更加相似,以此确定K。
  5. 基于层次聚类:即基于合并或分裂的思想,在一定情况下停止从而获得K。
  6. 基于采样的算法:即对样本采样,分别做聚类;根据这些结果的相似性确定K。如,将样本分为训练与测试样本;对训练样本训练分类器,用于预测测试样本类别,并与聚类的类别比较。

#matlab工具的那些事
matlab自带kmeans函数,效果还不错。调用方法如下,

varargout = kmeans(X, k, varargin)

属性很多,自行查看文档,可以调节采用的距离计算公式,并行运算等等。
k值的判定方法,有evalclusters函数[8],调用如下:

obj = evalclusters(X, YorFun, crit,varargin)
%   EVA = EVALCLUSTERS(X, CLUST, CRITERION)
%   EVA is an object of a particular class based on the value of CRITERION:
%               Value              CLASS
%              'CalinskiHarabasz'  CalinskiHarabaszEvaluation
%              'Silhouette'        SilhouetteEvaluation
%              'gap'               GapEvaluation
%              'DaviesBouldin'     DaviesBouldinEvaluation

可以直接判定k值的好坏。CRITERION不同算法的判定公式也可自查。

#Reference
[1]http://blog.pluskid.org/?p=17 绝对大牛的博客,聚类写的很系统
[2]http://www.cnblogs.com/kemaswill/archive/2013/01/26/2877434.html?utm_source=tuicool 预初始化和k值
[3]http://scikit-learn.org/stable/modules/clustering.html#clustering sklearn
[4]Estimating the number of clusters in a data set via the gap statistic, Tibshirani, Walther, and Hastie 2001
[5]finding the number of clusters in a data set, Sugar and James 2003
[6]https://www.zhihu.com/question/29208148
[7]http://www.cnblogs.com/dudumiaomiao/p/5839905.html
[8]http://blog.csdn.net/daisy9212/article/details/48323829#reply 聚类有效性估计

在这里插入图片描述
闲在写了个公众号,本公众号其实主打夯实基础,也许后期后做些视野型的文章写些关注发展前言,但是在我和众多同行交流学习过程中我发现很多学者只触及到了应用层,理论层极度欠缺,本公众号将分享和梳理基础理论知识,也许有错误的地方,也请同行多指点。最后,欢迎大家关注,你的关注是我不断更新的动力!

  • 7
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值