K-means原理及Matlab实现

来源:互联网 发布:ubuntu jira 汉化 编辑:程序博客网 时间:2024/05/17 00:18

前言

作为励志在机器学习界闯出一片天地的小女子,在此整理学习到的机器学习方法,并使用Matlab及Python实现。希望可以和大家互相交流和探讨。
联系方式:shitianqi1994@163.com


K-means原理

k-means应该是入门机器学习最早接触的算法之一了,它使用简单富有美感的算法深刻地表达了教机器学习的思想,其中蕴含的EM思想会在后续的博文中详细讲解。

从它的名字上解析一下,k代表了你要将数据分为几类(也就是后文提到的seed个数,别急,后面你会深刻理解),而means即为平均值,这也是此算法的核心。

宏观上来看它属于无监督学习,(无监督学习指:数据仅给出了特征值,未给出数据的标签,监督学习则同时给出了特征值和标签)

它具体的思想是什么呢,容我细细道来。


假设我们要将以下情况的点分开:

这里写图片描述

在这个例子中,我们用肉眼可以观察到这个数据大致可以分成两类。而需要注意的是在实际情况的应用上,可以直接观察到类别个数基本是不可能的,主要原因有以下两点:

  1. 实际数据特征向量的维数很高,实现可视化是十分困难的。
  2. 实际数据通常耦合程度高,没有清晰的分界线。

所以在这里需要提到一个题外话,在现实生活中往往是根据实际需要确定分类类别。举个栗子:你是一家制衣厂的老板,你有一大堆用户身高体重肩宽腰围等等的数据,你希望可以对用户群体分个类,来确定s,m,l码的衣服分别应该适合多大身材维度的用户。这里就可以使用kmeans。在这里,你就按照实际情况直接将k设置成了3。


好了,聊了一些闲话,现在重回主题,我们希望将上图数据进行分类,并且确定了k=2,即分成两类。接下来,我们在所有数据点中随机选取两个种子(seed),播种下这两颗种子,一切交给机器来学习吧!

这里写图片描述


所有点将会和两个seed进行比较,和哪个更加相似就加入哪个seed的阵营。

这里需要引入一个相似的概念,在数学上,表征相似程度的参数有许多:距离,相关系数等等。本文代码采用简单的欧式距离,有心的小伙伴可以尝试不同的方法进行尝试。

这里写图片描述


上图中所有的点都已经确定了自己的红蓝阵营。此时重新计算seed值,即所有红色点的特征值求平均作为新的红seed,所有蓝色点的特征值求平均作为新的蓝seed。

这里写图片描述


不断重复以上的过程,即可完成最终分类。

这里写图片描述

这里写图片描述


Matlab实现

这是一个实现kmeans的函数:

function [seed_new all_data] = kmeans_f(data,k)% % 输入data是所有原始数据构成的数组,k是选定的分类个数% % 输出seed_new是最终seed,all_data是最终data的特征和label 值(第一列为label)[m,n]=size(data);length=m;feature_number=n;r=zeros(k);% % 根据k初始化seed的indexr=randperm(length,k);% %保存所有的seedseed_new=zeros(k,feature_number);for j=1:k    seed_new(j,:)=data(r(j),:);endall_data=zeros(length,feature_number+1);% % % while 1是相当于do while循环while 1    seed_old=seed_new;    for j=1:length        choosen_point=data(j,:);        dist_list=zeros(k,1);        for i=1:k            seed=seed_old(i,:);            dist=norm(choosen_point-seed);            dist_list(i,:)=dist;        end% %     返回最小距离的cluster index        cluster_index=find(dist_list==min(dist_list));% %         有时返回好几个值        cluster_index_point=cluster_index(1);% %  将j点写入第cluster-index的类中,all_data是feature_number+1维数组,其中增加了第一列为其属于cluster的编号。     all_data(j,1)=cluster_index_point;     all_data(j,2:end)=choosen_point;    end% % 计算得到新的seed矩阵,注意一定要按照cluster-index的顺序排列    seed_new_sum=zeros(k,feature_number);    seed_number=zeros(k,1);    for i=1:length        for j=1:k          if all_data(i,1)==j              seed_new_sum(j,:)=seed_new_sum(j,:)+all_data(i,2:end);% %               统计每个cluster中点的个数              seed_number(j,:)=seed_number(j,:)+1;          end        end    end% %    计算出新的seed   seed_new=zeros(k,feature_number);   for i=1:feature_number       seed_new(:,i)=seed_new_sum(:,i)./seed_number;   end% % 跳出循环的条件是seed的改变量非常小。    judge=norm(seed_new-seed_old)    if judge<=0.01        break;    endendend

对这个函数进行测试:

clear all;close all;clc;%第一类数据mu1=[0 0 0];  %均值S1=[0.3 0 0;0 0.35 0;0 0 0.3];  %协方差data1=mvnrnd(mu1,S1,100);   %产生高斯分布数据% %第二类数据mu2=[1.25 1.25 1.25];S2=[0.3 0 0;0 0.35 0;0 0 0.3];data2=mvnrnd(mu2,S2,100);% %第三个类数据mu3=[-1.25 1.25 -1.25];S3=[0.3 0 0;0 0.35 0;0 0 0.3];data3=mvnrnd(mu3,S3,100);% %显示数据plot3(data1(:,1),data1(:,2),data1(:,3),'+');hold on;plot3(data2(:,1),data2(:,2),data2(:,3),'+');plot3(data3(:,1),data3(:,2),data3(:,3),'+');grid on;

测试得到的结果如下:

原始数据:

原始数据(未分类)

分类后的数据:

这里写图片描述

结语

感谢大家看到这里,欢迎随时沟通交流~

原创粉丝点击