应一个小伙伴的要求介绍了一下K均值聚类算法。本人也不是很专业,这是之前自学的,如果有错,大家可以提出来,共同进步嘛。


文章目录

  • 一、k-means算法(k-均值)
  • 1、k-means算法介绍
  • 2、k-means算法步骤
  • 二、k-means算法MATLAB实现
  • 1、函数介绍
  • 1)、kmeans函数
  • 2)、silhouette函数
  • 2、代码实现
  • 3、通过肘部法则对算法的聚类类别数进行确定


一、k-means算法(k-均值)

1、k-means算法介绍

  聚类属于非监督学习,K均值聚类是最基础常用的聚类算法。它的基本思想是,通过迭代寻找K个簇(Cluster)的一种划分方案,使得聚类结果对应的损失函数最小。其中,损失函数可以定义为各个样本距离所属簇中心点的误差平方和:

python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数

  其中python手肘法代码参数 手肘法确定k值matlab_matlab_02表示第python手肘法代码参数 手肘法确定k值matlab_算法_03个样本,python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_04python手肘法代码参数 手肘法确定k值matlab_matlab_02所属的簇,python手肘法代码参数 手肘法确定k值matlab_算法_06表示簇对应的中心点,python手肘法代码参数 手肘法确定k值matlab_算法_07是样本数。

  图解算法:

python手肘法代码参数 手肘法确定k值matlab_算法_08

2、k-means算法步骤

  k-means的核心目标是将给定的数据集划分成K个簇(K是超参),并给出每个样本数据对应的中心点。具体步骤非常简单,可以分为4步:

  (1)、数据预处理,主要是标准化、异常点过滤;

  (2)、随机选取python手肘法代码参数 手肘法确定k值matlab_聚类_09个中心点,记为python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_10

  (3)、定义损失函数:python手肘法代码参数 手肘法确定k值matlab_算法_11

  (4)、令python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_12=0,1,2,… 为迭代步数,重复如下过程直到python手肘法代码参数 手肘法确定k值matlab_聚类_13收敛

  (4.1)、对于每一个样本python手肘法代码参数 手肘法确定k值matlab_matlab_02,将其分配到距离最近的中心

python手肘法代码参数 手肘法确定k值matlab_算法_15

  (4.2)、对于每一个类中心python手肘法代码参数 手肘法确定k值matlab_聚类_09,重新计算该类的中心

python手肘法代码参数 手肘法确定k值matlab_算法_17

  k-means最核心的部分就是先固定中心点,调整每个样本所属的类别来减少 ;再固定每个样本的类别,调整中心点继续减小 。两个过程交替循环, 单调递减直到最(极)小值,中心点和样本划分的类别同时收敛。

  注意:python手肘法代码参数 手肘法确定k值matlab_算法_18均值聚类的最终聚类结果在一定程度上依赖于初始凝聚点或初始分类选择。

  算法流程图如下所示:

python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_19

二、k-means算法MATLAB实现

1、函数介绍

  MATLAB中与k-means算法相关的函数有:kmeans函数和silhouette函数。下面分别进行介绍。

1)、kmeans函数

python手肘法代码参数 手肘法确定k值matlab_k-means_20个点(或者观测点)分为python手肘法代码参数 手肘法确定k值matlab_聚类_21个类。聚类过程是动态的,通过迭代使得每一个点与所属类重心距离和达到最小。默认情况下,kmeans采用平方欧式距离,其调用格式如下:
  1、python手肘法代码参数 手肘法确定k值matlab_聚类_22
  将python手肘法代码参数 手肘法确定k值matlab_k-means_20个点(或者观测点)分为python手肘法代码参数 手肘法确定k值matlab_聚类_21个类。输入参数python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_25python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_26的矩阵,矩阵的每一行对应一个点,每一列对应一个变量。输出参数python手肘法代码参数 手肘法确定k值matlab_聚类_27是一个python手肘法代码参数 手肘法确定k值matlab_matlab_28的向量,其元素为每一个点所属类别的序列号。
  2、python手肘法代码参数 手肘法确定k值matlab_聚类_29
  返回python手肘法代码参数 手肘法确定k值matlab_聚类_21个类的类重心坐标矩阵python手肘法代码参数 手肘法确定k值matlab_matlab_31python手肘法代码参数 手肘法确定k值matlab_matlab_31是一个python手肘法代码参数 手肘法确定k值matlab_matlab_33的矩阵,第python手肘法代码参数 手肘法确定k值matlab_算法_03行元素为第python手肘法代码参数 手肘法确定k值matlab_算法_03类的类重心坐标。
  3、python手肘法代码参数 手肘法确定k值matlab_聚类_36
  返回类内距离和(即类各点与类重心距离之和)向量python手肘法代码参数 手肘法确定k值matlab_聚类_37python手肘法代码参数 手肘法确定k值matlab_聚类_37是一个python手肘法代码参数 手肘法确定k值matlab_聚类_39的向量,第python手肘法代码参数 手肘法确定k值matlab_算法_03行元素为第python手肘法代码参数 手肘法确定k值matlab_算法_03类的类内距离之和。
  4、python手肘法代码参数 手肘法确定k值matlab_k-means_42
  返回每个点与每个类重心之间的距离矩阵python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_43python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_43是一个python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_45的矩阵,第python手肘法代码参数 手肘法确定k值matlab_算法_03行第python手肘法代码参数 手肘法确定k值matlab_matlab_47列元素是第python手肘法代码参数 手肘法确定k值matlab_算法_03个点与第python手肘法代码参数 手肘法确定k值matlab_matlab_47类的类重心之间的距离。
  4、python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_50
  允许用户设置更多的参数及参数值,用来控制kmeans函数所用的迭代算法。python手肘法代码参数 手肘法确定k值matlab_k-means_51为参数名,python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_52为相应的参数值。可用的参数名可以上MATLAB官网查看。

2)、silhouette函数

python手肘法代码参数 手肘法确定k值matlab_matlab_53函数用来根据python手肘法代码参数 手肘法确定k值matlab_matlab_54函数的聚类结果绘制轮廊图,从轮廊图上能看出每个点的分类是否合理。轮康图上第python手肘法代码参数 手肘法确定k值matlab_算法_03个点的轮廊值python手肘法代码参数 手肘法确定k值matlab_聚类_56定义为:
python手肘法代码参数 手肘法确定k值matlab_matlab_57
  其中,python手肘法代码参数 手肘法确定k值matlab_k-means_58是第python手肘法代码参数 手肘法确定k值matlab_算法_03个点与同类的其他点之间的平均距离,python手肘法代码参数 手肘法确定k值matlab_k-means_60为一个向量,其元素是第python手肘法代码参数 手肘法确定k值matlab_算法_03个点与不同类的类内各点之间的平均距离,例如python手肘法代码参数 手肘法确定k值matlab_k-means_60的第个python手肘法代码参数 手肘法确定k值matlab_聚类_21元素是第python手肘法代码参数 手肘法确定k值matlab_算法_03个点与第k类各点之间的平均距离。
  轮廓值python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_65的取值范幽为[-1,1],python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_65值越大,说明第python手肘法代码参数 手肘法确定k值matlab_算法_03个点的分类越合理,当 python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_65<0 时,说明第python手肘法代码参数 手肘法确定k值matlab_算法_03个点的分类不合理,还有比目前分类更合理的方案。

python手肘法代码参数 手肘法确定k值matlab_matlab_53函数函数的调用格式如下:
  1、python手肘法代码参数 手肘法确定k值matlab_matlab_71
  根据样本观测值短阵python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_25 和聚类结果python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_73绘制轮廓图。输人参数python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_25是一个python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_26的矩阵,矩阵的每一行对应一个观测,每一列对应一个变量。python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_73是聚类结果,可以是由每个观测所属类的类序号构成的数值向量,也可以是由类名称构成的字符矩阵或字符串元胞数组。python手肘法代码参数 手肘法确定k值matlab_matlab_53函数会把python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_73 中的python手肘法代码参数 手肘法确定k值matlab_k-means_79或空字符作为缺失数据,从而忽略python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_25中相应的观测。默认情况下,python手肘法代码参数 手肘法确定k值matlab_matlab_53函数采用平方欧氏距离。
  2、python手肘法代码参数 手肘法确定k值matlab_k-means_82
  返回轮廓值向量python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_83,它是一个python手肘法代码参数 手肘法确定k值matlab_matlab_28的向量,其元素为相应点的轮廓值。此时不会绘制轮廓图。
  3、python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_85
  绘制轮廓图,并返回轮廓值向量python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_83和图形句柄python手肘法代码参数 手肘法确定k值matlab_matlab_87
  4、python手肘法代码参数 手肘法确定k值matlab_聚类_88
  指定距离计算的方法,绘制轮廓图。输人参数python手肘法代码参数 手肘法确定k值matlab_matlab_89为字符串或距离矩阵,用来指定距
离计算的方法或距离矩阵。python手肘法代码参数 手肘法确定k值matlab_matlab_53函数支持的各种距离可以上MATLAB官网查看。
  5、python手肘法代码参数 手肘法确定k值matlab_matlab_91
  接受函数句柄作为第三个输入,即python手肘法代码参数 手肘法确定k值matlab_聚类_92为函数句柄,用来自定义距离计算方法。python手肘法代码参数 手肘法确定k值matlab_聚类_92对应的函数形式如下
python手肘法代码参数 手肘法确定k值matlab_算法_94
  其中python手肘法代码参数 手肘法确定k值matlab_k-means_95是一个python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_96的向量,表示一个点的坐标。python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_25python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_26的矩阵,python手肘法代码参数 手肘法确定k值matlab_算法_99是可选的参数。python手肘法代码参数 手肘法确定k值matlab_算法_100python手肘法代码参数 手肘法确定k值matlab_matlab_28的距离向量,python手肘法代码参数 手肘法确定k值matlab_算法_100的第python手肘法代码参数 手肘法确定k值matlab_聚类_21个元素是python手肘法代码参数 手肘法确定k值matlab_k-means_95python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_25矩阵的第python手肘法代码参数 手肘法确定k值matlab_聚类_21行之间的距离。

2、代码实现

  本文数据采用了MATLAB自带的鸢尾花数据集
  1、导入数据集

clc 
clear;
load fisheriris

  2、用数据集的第三列和第四列数据将原数据画图呈现

X = meas(:,3:4);
figure;
plot(X(:,1),X(:,2),'k*','MarkerSize',5);
title 'Fisher''s Iris Data';
xlabel 'Petal Lengths (cm)'; 
ylabel 'Petal Widths (cm)';

  结果如下所示:

python手肘法代码参数 手肘法确定k值matlab_k-means_107


  3、使用kmeans将其聚类并将其可视化

rng(1); % 设置随机数种子,使得结果具有重现性
[idx,C] = kmeans(X,3);
figure;
plot(X(idx==1,1),X(idx==1,2),'r.','MarkerSize',12)
hold on
plot(X(idx==2,1),X(idx==2,2),'b.','MarkerSize',12)
plot(C(:,1),C(:,2),'kx',...
     'MarkerSize',15,'LineWidth',3) 
legend('Cluster 1','Cluster 2','Centroids',...
       'Location','NW')
title 'Cluster Assignments and Centroids'
hold off

  结果如下所示:

python手肘法代码参数 手肘法确定k值matlab_matlab_108

  4、画出轮廓图

[S, H] = silhouette(X,idx);    % 绘制轮廓图,并返回轮廓值向量S和图形句柄H

  结果如下所示:

python手肘法代码参数 手肘法确定k值matlab_算法_109


  可以看出,每一个观测的轮廓值都是正的,这说明我们分为三类是合理的。

  全部代码如下所示:

clc 
clear;
load fisheriris
X = meas(:,3:4);
figure;
plot(X(:,1),X(:,2),'k*','MarkerSize',5);
title 'Fisher''s Iris Data';
xlabel 'Petal Lengths (cm)'; 
ylabel 'Petal Widths (cm)';

rng(1); % 设置随机数种子,使得结果具有重现性
[idx,C] = kmeans(X,3);
figure;
plot(X(idx==1,1),X(idx==1,2),'r.','MarkerSize',12)
hold on
plot(X(idx==2,1),X(idx==2,2),'b.','MarkerSize',12)
hold on;
plot(X(idx==3,1),X(idx==3,2),'y.','MarkerSize',12)
hold on;
% plot(X(idx==4,1),X(idx==4,2),'y.','MarkerSize',12)
% hold on;
plot(C(:,1),C(:,2),'kx',...
     'MarkerSize',15,'LineWidth',3) 
legend('Cluster 1','Cluster 2','Cluster 3','Centroids',...
       'Location','NW')
title 'Cluster Assignments and Centroids'
hold off
[S, H] = silhouette(X,idx);    % 绘制轮廓图,并返回轮廓值向量S和图形句柄H

3、通过肘部法则对算法的聚类类别数进行确定

python手肘法代码参数 手肘法确定k值matlab_聚类_09。这样就会导致如果给的聚类类别数python手肘法代码参数 手肘法确定k值matlab_聚类_09不恰当,就会导致聚类效果很差。
  1、下面我们用刚才的数据,把聚类的类别数改为四类。
  代码如下:

clc 
clear;
load fisheriris
X = meas(:,3:4);
figure;
plot(X(:,1),X(:,2),'k*','MarkerSize',5);
title 'Fisher''s Iris Data';
xlabel 'Petal Lengths (cm)'; 
ylabel 'Petal Widths (cm)';

rng(1); % 设置随机数种子,使得结果具有重现性
[idx,C] = kmeans(X,4);
figure;
plot(X(idx==1,1),X(idx==1,2),'r.','MarkerSize',12)
hold on
plot(X(idx==2,1),X(idx==2,2),'b.','MarkerSize',12)
hold on;
plot(X(idx==3,1),X(idx==3,2),'y.','MarkerSize',12)
hold on;
plot(X(idx==4,1),X(idx==4,2),'y.','MarkerSize',12)
hold on;
plot(C(:,1),C(:,2),'kx',...
     'MarkerSize',15,'LineWidth',3) 
legend('Cluster 1','Cluster 2','Cluster 3','Cluster 4','Centroids',...
       'Location','NW')
title 'Cluster Assignments and Centroids'
hold off

  结果试图如下:

python手肘法代码参数 手肘法确定k值matlab_聚类_112


  轮廓图如下所示:

python手肘法代码参数 手肘法确定k值matlab_算法_113


  可以看出,有些点的轮廓值小于0,说明该些点的聚类结果较差,不合理。

  2、用肘部法则判断最佳聚类类别数。

  Elbow Method(肘部法则) :Elbow意思是手肘,如下图左所示,此种方法适用于 K 值相对较小的情况,当选择的k值小于真正的时,k每增加1,cost值就会大幅的减小;当选择的k值大于真正的K时, k每增加1,cost值的变化就不会那么明显。这样,正确的k值就会在这个转折点,类似elbow的地方。 如下图:

python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_114


python手肘法代码参数 手肘法确定k值matlab_python手肘法代码参数_115


  下面用本道题通过对不同的python手肘法代码参数 手肘法确定k值matlab_聚类_09值求解进行解释:

  代码如下:

clc 
clear;
load fisheriris
X = meas(:,3:4);
% figure;
% plot(X(:,1),X(:,2),'k*','MarkerSize',5);
% title 'Fisher''s Iris Data';
% xlabel 'Petal Lengths (cm)'; 
% ylabel 'Petal Widths (cm)';
% 
% rng(1); % 设置随机数种子,使得结果具有重现性
% [idx,C] = kmeans(X,4);
% figure;
% plot(X(idx==1,1),X(idx==1,2),'r.','MarkerSize',12)
% hold on
% plot(X(idx==2,1),X(idx==2,2),'b.','MarkerSize',12)
% hold on;
% plot(X(idx==3,1),X(idx==3,2),'y.','MarkerSize',12)
% hold on;
% plot(X(idx==4,1),X(idx==4,2),'y.','MarkerSize',12)
% hold on;
% plot(C(:,1),C(:,2),'kx',...
%      'MarkerSize',15,'LineWidth',3) 
% legend('Cluster 1','Cluster 2','Cluster 3','Cluster 4','Centroids',...
%        'Location','NW')
% title 'Cluster Assignments and Centroids'
% hold off
% [S, H] = silhouette(X,idx);    % 绘制轮廓图,并返回轮廓值向量S和图形句柄H
for k=2:8
    rng(100)
[lable,c,sumd,d]=kmeans(X,k,'dist','sqeuclidean');
% data,n×p原始数据向量
% lable,n×1向量,聚类结果标签;
% c,k×p向量,k个聚类质心的位置
% sumd,k×1向量,类间所有点与该类质心点距离之和
% d,n×k向量,每个点与聚类质心的距离
sse1 = sum(sumd.^2);
D(k,1) = k;
D(k,2) = sse1;
end

plot(D(2:end,1),D(2:end,2))
hold on;
plot(D(2:end,1),D(2:end,2),'or');

title('不同K值聚类偏差图') 
xlabel('分类数(K值)') 
ylabel('簇内误差平方和')

  结果试图如下:

python手肘法代码参数 手肘法确定k值matlab_聚类_117


  由此图可以看出,当分类数python手肘法代码参数 手肘法确定k值matlab_k-means_118时折线的下降趋势骤缓,故可将类别数设置为 3。