K-Means聚类算法(一):算法思路

K-Means聚类算法(一):算法思路

专栏刚刚成立,就有五十多人关注,让我这个小屌丝诚惶诚恐,仔细思索该为诸位读者大大们奉上怎样的文章。一番思索、最后决定先从K-Means写起,原因是,这个算法的思路好懂,不需要知道背后的数学背景也能玩的起来,实现简单(哪怕只要是能#include<math.h>的C语言都可以不是很复杂的构建起来),在工作中经常会用到(用来精简和压缩数据等等)。


最重要的一点是:经常有人用错地方,不能使用欧氏距离作为相似度指标的地方也敢用!然后效果怎样我就不说了!


如果您还不理解或者不知道K-Means:
我希望诸位读完(一)以后,我的文章能让诸位了解K-Means的原理,适用范围和参数整定方法,掌握Matlab的能使用Matlab的kmeans工具进行实验。
我希望(二)以后的时候每个掌握基础编程的人都有用自己熟悉的语言开发一个基础的K-Means的能力。
我希望在写到(三)的时候每个掌握其变形使用方法,能优化算法,并且添加自己的思路进去(比如数据权重)。
我希望写完(四)以后,大家会了解它的数学背景。


PS1:虽说是“做每个人都能懂的笔记”,但是让任何人都懂觉得还是难以做到,所以昨天在创建专栏的时候最后写成了“做大部分人都能读懂的数据科学笔记”

PS2:如果有任何不明白的地方欢迎讨论,如果您具有相应的基础知识(见下),但是觉得我写的您看不懂,请一定告诉我!


PS3:如果觉得不错,请点个,你们的是我写这些文章和代码的动力,我还有很多细心回答的答案,如果您觉得好也请点个。如果写的不好……不要走,我会改的,我真的会改的TAT




正文开始:

以下是读完这一文章需要的基础知识:
n维欧氏空间{R}^{n}{L}^{2}距离,Matlab编程基础知识。

我会遵循如下的顺序介绍K-Means:
1、算法概述概览(本文);
2、是算法思路;
3、代码实现和改进;
4、数学原理。

需要注意的是,数学原理并不是工作的过程或者算法的实现,这个实现起来很简单的算法有着不是那么单纯的背景,PRML一书中将其归类到“混合模型与EM”一章中去。而且,就算不知道原理我们也能把这个算法撸的很爽,所以在最后才会介绍。

我会在(二)中提供我们自己实现的K-Means的算法的源代码,但是没有接触过这一类算法的筒子们最好自己动手试一下,这算法简单,可以不依赖一些矩阵计算数学库就较为轻松的实现,我这里采用Matlab,主要是因为画图演示方便,自己实现的时候可以使用C,Python,C++,Java,VB,C#等任何支持数组,且你熟悉的语言完成。

所谓聚类算法是指将一堆没有标签的数据自动划分成几类的方法,这个方法要保证同一类的数据有相似的特征,如下图所示:


K-Means算法的特点是类别的个数是人为给定的,如果让机器自己去找类别的个数,我们有AP聚类算法,先不说,说了就跑题了。

K-Means的一个重要的假设是:数据之间的相似度可以使用欧氏距离度量,如果不能使用欧氏距离度量,要先把数据转换到能用欧氏距离度量,这一点很重要

(注:可以使用欧氏距离度量的意思就是欧氏距离越小,两个数据相似度越高)

我们来看个反例:




图中是一个瑞士卷形状的流形,这个时候我们表示相似度应该用测地距离。什么叫测地距离呢?就是在曲面上从A点走到B点(不允许离开曲面)的最短距离。

但是很明显:测地距离很远的两个点有的时候会分在同一类(图中同一种颜色),说明这个时候用欧氏距离就失效了。

这种情况怎么处理呢?以后在讲ISOMAP和LLE的时候提到如何使用KNN和最短路径算法等工具实现非欧空间到欧氏空间转化,先不说,说了就跑题了。

K-Means算法有个很著名的解释,就是牧师-村民模型(这个故事是谁和我说的我忘了,以下是我的复述,有知道原作者的提醒我一下):

有四个牧师去郊区布道,一开始牧师们随意选了几个布道点,并且把这几个布道点的情况公告给了郊区所有的居民,于是每个居民到离自己家最近的布道点去听课。
听课之后,大家觉得距离太远了,于是每个牧师统计了一下自己的课上所有的居民的地址,搬到了所有地址的中心地带,并且在海报上更新了自己的布道点的位置。
牧师每一次移动不可能离所有人都更近,有的人发现A牧师移动以后自己还不如去B牧师处听课更近,于是每个居民又去了离自己最近的布道点……
就这样,牧师每个礼拜更新自己的位置,居民根据自己的情况选择布道点,最终稳定了下来。

以上就是K-Means算法的一个过程。

我们考虑能用欧氏距离度量的情况:

那么这样一个在欧氏空间中用来聚类的算法是怎么工作的呢,那就具体说一下算法:

首先我们得到了

{R}^{n}

空间中的一堆点,这里取2维空间(方便查看),可以是酱婶的:


随机生成k个聚类中心点(真心随机生成,因为无所谓~但是尽量不要生成在一起):

而后分别计算每一个数据点对这些中心的距离,把距离最短的那个当成自己的类别,或者中心点。

就好比你去买菜,家门口有菜场的,我不会穿个城到另外一条街去买,对吧~

这样每个点都会有一个中心点了,这是随机生成的中心点的结果,可以看到聚类的并不准确:


这个时候,身为中心点,觉得自己不能离群众这么遥远,他要到群众中去,这样可以收复那些绿颜色的点,像这样:

那么他怎么知道移动到哪里呢?

当然是往群众的中心移动啦!所有的属于蓝色类的点的中心,或者说坐标的平均值,就是蓝色小圆圈要去的地方,于是他收获了更多的群众:


绿色的点也往群众的中心移动,但是却少了一批数据跟随他,不过没关系,反正这些变蓝的点和现在自己所在的绿色大本营的格调(欧氏坐标)格格不入(距离太远)。

于是仅仅2步,情况就成了这样:

蓝色的中心点又试着微调了几次,绿色和红色的也一样,微调之后发现自己不需要动了,自己的群众基础已经稳定了,乱动可能脱离群众,众叛亲离,所以这个时候算法就收敛了。

如果用伪代码描述,那么过程是这样的:

function K-Means(输入数据,中心点个数K)
    获取输入数据的维度Dim和个数N
    随机生成K个Dim维的点
    while(算法未收敛)N个点:计算每个点属于哪一类。
        对于K个中心点1,找出所有属于自己这一类的所有数据点
            2,把自己的坐标修改为这些数据点的中心点坐标
    end
    输出结果:
end


很多人已经急着想试一下了,那么,在正式编程开始之前,我们不妨先使用Matlab提供的内置的K-Means感受一下(代码在GitHub上自己下载,你的Matlab要支持K-Means哦,我的版本是r2015a)
DataScienceNote/TryKMeans.m at master · TsingJyujing/DataScienceNote · GitHub

实际编程的时候,我们会遇到如下问题:
1,怎么初始化才能更快收敛不震荡?
2,如果有一中心点初始化的时候离得远了,或者被其它中心点包围了,导致每个数据点都不肯找他怎么办?
3,如何评价算法是否收敛?
4,算法不收敛怎么办?
5,能不能给每个点设置权重?
……

还有这些奇奇怪怪的优化问题:
1,如何高效的使用Matlab计算欧氏距离?
2,如何快速计算出每一个点属于哪一类?
3,如何保存和输出结果?怎么设计数据结构?
……

这些在K-Means聚类算法的思路、实现、改进及原理(二):算法的实现里详细讨论。

编辑于 2016-01-09 02:44