Matlab实现简单K-means聚类算法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27914913/article/details/61922916

K-means算法简要思想:

算法接受参数 k ;然后将事先输入的n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。
(1)适当选择k个类的初始中心;
(2)在第k次迭代中,对任意一个样本,求其到各中心的距离,将该样本归到距离最短的中心所在的类;
(3)利用均值等方法更新类的中心值;
(4)对于所有的c个聚类中心,如果利用(2)(3)的迭代法更新后,中心值保持不变或者满足变化距离小于一个精度或者达到最大迭代次数,则迭代结束,否则继续迭代。

实现步骤:

实验提供了两组数据(二维和三维),算法都能适用,这里我们以三维数据作为说明。首先读入数据,用scatter3将三维数据通过散点图显示出来:

load 3d-data.mat;  %load数据,存在变量r中
figure(1);scatter3(r(:,1),r(:,2),r(:,3),'k');title('原始样本');

结果见下图1:
三维原始数据

                        图1 三维样本集原始数据散点图    

通过图1可以明显看出,样本集聚为7个簇,因此可将K初始化为7,然后随机选取7个中心点,存于clusters矩阵中:

clusters=zeros(K,cols);
for i=1:K
    clusters(i,:)=r(floor(rand*rows),:);  %随机选取K个质心
end

质心选取后,需要计算每个样本点与K个质心的距离,存于dist矩阵中,并从此处开始迭代(for i=1:maxiter):

dist=zeros(rows,K);
for iter=1:maxiter
    for i=1:rows
        for j=1:K
            dist(i,j)=sqrt(sum(( r(i,:)-clusters(j,:)).^2)); %dist存的是每一个样本数据与K个质心的距离,大小为rows*K
        end
    end

矩阵dist中存了每个样本集与K个质心的距离,维数为rows*K,由于我们需要选出距离最小的那个质心,也就是要从矩阵dist每一行中选出最小数所在的位置,我们可以先将dist中每一行升序排序,最小的那个数就在每一行的首列,然后与原来dist一行数进行比较,找出最小距离的位置,即可分类:

distsort=sort(dist,2);  %对dist中每一行数据进行升序排序
    label=zeros(rows,1);
    for i=1:rows
        for j=1:K
            if(distsort(i,1)==dist(i,j))
                label(i,1)=j; %每个样本所属的类别
            end
        end
    end

类别存于矩阵label中,维数为rows*1。接下来,需要对每一类中的所有样本点进行求平均,具体操作是将所有同类样本点相加后除于个数,新质心存于new_clusters矩阵中:

sum_sample=zeros(K,cols);  %每个簇的样本相加和
    num_sample=zeros(K,1);   %每个簇的样本个数
    for i=1:K
        for j=1:rows
            if(label(j,1)==i)
                sum_sample(i,:)=sum_sample(i,:)+r(j,:);
                num_sample(i,1)=num_sample(i,1)+1;
            end
        end
    end
    new_clusters=zeros(K,cols);  
    for i=1:K
        new_clusters(i,:)=sum_sample(i,:)/num_sample(i,1); %每个簇的所有样本相加求平均作为新质心
    end

当前后两次质心之间的距离变化小于一个设定精度时,判定此时达到收敛。由于要保证所有质心距离变化都小于某个精度,因此需要计算质心变化距离并判断变化不大的质心个数是否为K(即全部k个质心都不发生明显变化):

PRECISION=0.001;  %精度
    clu_dist=zeros(K,1);
    for i=1:K
        clu_dist(i,1)=sqrt(sum((new_clusters(i,:)-clusters(i,:)).^2)); %求前后两次质心距离
    end
    count=0;  %质心不发生明显变化的质心个数
    for i=1:K
        if(clu_dist(i,1)<PRECISION)
            count=count+1;
        end
    end

当全部K个质心都不发生明显变化时,打印输出此时迭代了多少次,并将聚类后的散点图画出,由于要用不同颜色标出,而且画散点图是一个一个画,因此需要多个判断语句:

if(count==K) %K个质心都不发生明显变化
        fprintf('收敛于第 %d 次迭代\n',iter); %输出 第几次达到收敛
        figure(2);
        for i=1:rows
            if(label(i,1)==1)
                scatter3(r(i,1),r(i,2),r(i,3),'b');hold on; %蓝色
            else if(label(i,1)==2)
                    scatter3(r(i,1),r(i,2),r(i,3),'g');hold on; %绿色
                else if(label(i,1)==3)
                        scatter3(r(i,1),r(i,2),r(i,3),'r');hold on; %红色
                    else if(label(i,1)==4)
                            scatter3(r(i,1),r(i,2),r(i,3),'k');hold on; %黑色
                        else if(label(i,1)==5)
                                scatter3(r(i,1),r(i,2),r(i,3),'c');hold on; %青色
                            else if(label(i,1)==6)
                                    scatter3(r(i,1),r(i,2),r(i,3),'y');hold on; %黄色
                                else if(label(i,1)==7)
                                        scatter3(r(i,1),r(i,2),r(i,3),'m');hold on; %粉红
                                    end
                                end
                            end
                        end
                    end
                end
            end
        end
        title('k-means聚类后');
        break;  %跳出迭代
    else clusters=new_clusters;  %否则继续迭代
    end

收敛后,输出聚类后的样本点并跳出迭代,否则将此时的质心作为新质心继续迭代。至此,算法结束。图2是二维数据算法结果,图3-图6是三维数据点聚类后结果:
这里写图片描述

                       图2 二维数据聚类前后对比

这里写图片描述

                           图3 三维数据聚类后    

这里写图片描述

                           图4 三维数据聚类后

这里写图片描述

                           图5 三维数据聚类后

这里写图片描述

                          图6 三维数据聚类前后对比

分析:

由图3-6可看出,该算法得出的结果并不稳定,原因就是该算法对初始质心选取很敏感,随机选取质心可能会得到错误结果并且迭代次数也会变大,若初始质心刚好分别处于7个簇之中,那么算法结果就会稳定了。初始质心的距离不应太近,因此可做以下优化:

a)  从输入的数据点集合中随机选择一个点作为第一个聚类中心
b) 对于数据集中的每一个点,计算它与已选择的聚类中心中最近聚类中心的距离D(x)          c) 选择一个新的数据点作为新的聚类中心,选择的原则是D(x)较大的点,
被选取作为聚类中心的概率较大
d) 重复b和c直到选择出k个聚类质心
e) 利用这k个质心来作为初始化质心去运行标准的K-Means算法

这是对初始质心选取的优化,还有一些距离计算的优化等,都是对K-means的改进。

总结:

K-means是个简单实用的算法,原理简单,实现容易,收敛速度快,结果较优;缺点是K值的选取不好把握,初始质心不好选取,对不是凸的数据集比较难收敛,迭代得到的结果只是局部最优,并且对噪声点敏感。

猜你喜欢

转载自blog.csdn.net/qq_27914913/article/details/61922916