MATLAB实现k-means算法(k-均值)对无标签数据进行聚类,并通过肘部法则确定聚类类别

应一个小伙伴的要求介绍了一下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)的一种划分方案,使得聚类结果对应的损失函数最小。其中,损失函数可以定义为各个样本距离所属簇中心点的误差平方和:
J ( c , μ ) = ∑ i = 1 M ∣ ∣ x i − μ c i ∣ ∣ 2 J(c,\mu) = \sum_{i=1}^{M}||x_i-\mu_{c_i}||^2 J(c,μ)=i=1M∣∣xiμci2
  其中 x i x_i xi表示第 i i i个样本, c i c_i ci x i x_i xi所属的簇, μ c i \mu_{c_i} μci表示簇对应的中心点, M M M是样本数。
  图解算法:

2、k-means算法步骤

  k-means的核心目标是将给定的数据集划分成K个簇(K是超参),并给出每个样本数据对应的中心点。具体步骤非常简单,可以分为4步:
  (1)、数据预处理,主要是标准化、异常点过滤;
  (2)、随机选取 K K K个中心点,记为 μ 1 ( 0 ) , μ 2 ( 0 ) , ⋅ ⋅ ⋅ , μ k ( 0 ) \mu_1^{(0)},\mu_2^{(0)},···,\mu_k^{(0)} μ1(0),μ2(0),⋅⋅⋅,μk(0)
  (3)、定义损失函数: J ( c , μ ) = min ⁡ ∑ i = 1 M ∣ ∣ x i − μ c i ∣ ∣ 2 J(c,\mu) = \min\sum_{i=1}^{M}||x_i-\mu_{c_i}||^2 J(c,μ)=mini=1M∣∣xiμci2
  (4)、令 t t t=0,1,2,… 为迭代步数,重复如下过程直到 J J J收敛
  (4.1)、对于每一个样本 x i x_i xi,将其分配到距离最近的中心
c i t < − a r g m i n k ∣ ∣ x i − μ k t ∣ ∣ 2 c_i^t < -arg min_k||x_i-\mu_k^t||^2 cit<argmink∣∣xiμkt2
  (4.2)、对于每一个类中心 K K K,重新计算该类的中心
μ k ( t + 1 ) < − a r g m i n k ∑ i : c i t = k b ∣ ∣ x i − μ ∣ ∣ 2 \mu_k^{(t+1)} < -argmin_k\sum_{i:c_i^t = k}^{b}||x_i-\mu||^2 μk(t+1)<argminki:cit=kb∣∣xiμ2
  k-means最核心的部分就是先固定中心点,调整每个样本所属的类别来减少 ;再固定每个样本的类别,调整中心点继续减小 。两个过程交替循环, 单调递减直到最(极)小值,中心点和样本划分的类别同时收敛。
  注意: K K K均值聚类的最终聚类结果在一定程度上依赖于初始凝聚点或初始分类选择。
  算法流程图如下所示:

二、k-means算法MATLAB实现

1、函数介绍

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

1)、kmeans函数

  kmeans函数用来作K均值聚类,将 n n n个点(或者观测点)分为 k k k个类。聚类过程是动态的,通过迭代使得每一个点与所属类重心距离和达到最小。默认情况下,kmeans采用平方欧式距离,其调用格式如下:
  1、 I D X = k m e a n s ( X , k ) IDX = kmeans(X,k) IDX=kmeans(X,k)
  将 n n n个点(或者观测点)分为 k k k个类。输入参数 X X X n × p n \times p n×p的矩阵,矩阵的每一行对应一个点,每一列对应一个变量。输出参数 I D X IDX IDX是一个 n × 1 n \times 1 n×1的向量,其元素为每一个点所属类别的序列号。
  2、 [ I D X , C ] = k m e a n s ( X , k ) [IDX,C] = kmeans(X,k) [IDX,C]=kmeans(X,k)
  返回 k k k个类的类重心坐标矩阵 C C C C C C是一个 k × p k \times p k×p的矩阵,第 i i i行元素为第 i i i类的类重心坐标。
  3、 [ I D X , C , s u m d ] = k m e a n s ( X , k ) [IDX,C,sumd] = kmeans(X,k) [IDX,C,sumd]=kmeans(X,k)
  返回类内距离和(即类各点与类重心距离之和)向量 s u m d sumd sumd s u m d sumd sumd是一个 1 × k 1 \times k 1×k的向量,第 i i i行元素为第 i i i类的类内距离之和。
  4、 [ I D X , C , s u m d , D ] = k m e a n s ( X , k ) [IDX,C,sumd,D] = kmeans(X,k) [IDX,C,sumd,D]=kmeans(X,k)
  返回每个点与每个类重心之间的距离矩阵 D D D D D D是一个 n × k n \times k n×k的矩阵,第 i i i行第 j j j列元素是第 i i i个点与第 j j j类的类重心之间的距离。
  4、 [ ⋅ ⋅ ⋅ ] = k m e a n s ( ⋅ ⋅ ⋅ , p a r a m l 1 , v a l 1 , p a r a m 2 , v a l 2 , ⋅ ⋅ ⋅ ) [···] = kmeans(···,paraml1,val1,param2,val2,···) [⋅⋅⋅]=kmeans(⋅⋅⋅,paraml1,val1,param2,val2,⋅⋅⋅)
  允许用户设置更多的参数及参数值,用来控制kmeans函数所用的迭代算法。 p a r a m 1 , p a r a m 2 , ⋅ ⋅ ⋅ param1,param2,··· param1,param2,⋅⋅⋅为参数名, v a l 1 , v a l 2 , ⋅ ⋅ ⋅ val1,val2,··· val1,val2,⋅⋅⋅为相应的参数值。可用的参数名可以上MATLAB官网查看。

2)、silhouette函数

   s i l h o u e t t e silhouette silhouette函数用来根据 c l u s t e r 、 c l u s t e r d a t a 或 k m e a n s cluster、clusterdata 或kmeans clusterclusterdatakmeans函数的聚类结果绘制轮廊图,从轮廊图上能看出每个点的分类是否合理。轮康图上第 i i i个点的轮廊值 ( s i l h o u e t t e v a l u e ) (silhouette value) (silhouettevalue)定义为:
S ( i ) = m i n ( b ) − a max ⁡ [ a , min ⁡ ( b ) ] , i = 1 , 2 , ⋅ ⋅ ⋅ , n S(i) = \frac{min(b)-a}{\max[a,\min(b)]},i=1,2,···,n S(i)=max[a,min(b)]min(b)a,i=1,2,⋅⋅⋅,n
  其中, a a a是第 i i i个点与同类的其他点之间的平均距离, b b b为一个向量,其元素是第 i i i个点与不同类的类内各点之间的平均距离,例如 b b b的第个 k k k元素是第 i i i个点与第k类各点之间的平均距离。
  轮廓值 S ( i ) S(i) S(i)的取值范幽为[-1,1], S ( i ) S(i) S(i)值越大,说明第 i i i个点的分类越合理,当 S ( i ) S(i) S(i)<0 时,说明第 i i i个点的分类不合理,还有比目前分类更合理的方案。

   s i l h o u e t t e silhouette silhouette函数函数的调用格式如下:
  1、 s i l h o u e t t e ( X , c l u s t ) silhouette(X,clust) silhouette(X,clust)
  根据样本观测值短阵 X X X 和聚类结果 c l u s t clust clust绘制轮廓图。输人参数 X X X是一个 n × p n \times p n×p的矩阵,矩阵的每一行对应一个观测,每一列对应一个变量。 c l u s t clust clust是聚类结果,可以是由每个观测所属类的类序号构成的数值向量,也可以是由类名称构成的字符矩阵或字符串元胞数组。 s i l h o u e t t e silhouette silhouette函数会把 c l u s t clust clust 中的 N a N NaN NaN或空字符作为缺失数据,从而忽略 X X X中相应的观测。默认情况下, s i l h o u e t t e silhouette silhouette函数采用平方欧氏距离。
  2、 s = s i l h o u e t t e ( X , c l u s t ) s = silhouette(X, clust) s=silhouette(X,clust)
  返回轮廓值向量 s s s,它是一个 n × 1 n \times 1 n×1的向量,其元素为相应点的轮廓值。此时不会绘制轮廓图。
  3、 [ s , h ] = s i l h o u e t t e ( x , c l u s t ) [s,h] = silhouette(x,clust) [s,h]=silhouette(x,clust)
  绘制轮廓图,并返回轮廓值向量 s s s和图形句柄 h h h
  4、 [ ⋅ ⋅ ⋅ ] = s i l h o u e t t e ( x , c l u s t , m e t r i c ) [···]= silhouette(x,clust, metric) [⋅⋅⋅]=silhouette(x,clust,metric)
  指定距离计算的方法,绘制轮廓图。输人参数 m e t r i c metric metric为字符串或距离矩阵,用来指定距
离计算的方法或距离矩阵。 s i l h o u e t t e silhouette silhouette函数支持的各种距离可以上MATLAB官网查看。
  5、 [ ⋅ ⋅ ⋅ ] = s i l h o u e t t e ( x , c l u s t , d i s t f u n , p 1 , p 2 , ⋅ ⋅ ⋅ ) [···]= silhouette(x,clust, distfun,p1,p2,···) [⋅⋅⋅]=silhouette(x,clust,distfun,p1,p2,⋅⋅⋅)
  接受函数句柄作为第三个输入,即 d i s t f u n distfun distfun为函数句柄,用来自定义距离计算方法。 d i s t f u n distfun distfun对应的函数形式如下
d = d i s t f u n ( X 0 , X , p 1 , p 2 , ⋅ ⋅ ⋅ ) d = distfun(X0,X,p1,p2,···) d=distfun(X0,X,p1,p2,⋅⋅⋅)
  其中 X 0 X0 X0是一个 1 × p 1 \times p 1×p的向量,表示一个点的坐标。 X X X n × p n \times p n×p的矩阵, p 1 , p 2 , ⋅ ⋅ ⋅ p1,p2,··· p1,p2,⋅⋅⋅是可选的参数。 d d d n × 1 n \times 1 n×1的距离向量, d d d的第 k k k个元素是 X 0 X0 X0 X X X矩阵的第 k k k行之间的距离。

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)';

  结果如下所示:
MATLAB实现k-means算法(k-均值)对无标签数据进行聚类,并通过肘部法则确定聚类类别_第1张图片
  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

  结果如下所示:
MATLAB实现k-means算法(k-均值)对无标签数据进行聚类,并通过肘部法则确定聚类类别_第2张图片

  4、画出轮廓图

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

  结果如下所示:
MATLAB实现k-means算法(k-均值)对无标签数据进行聚类,并通过肘部法则确定聚类类别_第3张图片
  可以看出,每一个观测的轮廓值都是正的,这说明我们分为三类是合理的。
  全部代码如下所示:

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、通过肘部法则对算法的聚类类别数进行确定

  通常我们遇到的问题大多数都是无法确定确定聚类个数的,属于无监督机器学习。但是K均值聚类算法又要我们提前规定好聚类的类别数 K K K。这样就会导致如果给的聚类类别数 K K K不恰当,就会导致聚类效果很差。
  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

  结果试图如下:
MATLAB实现k-means算法(k-均值)对无标签数据进行聚类,并通过肘部法则确定聚类类别_第4张图片
  轮廓图如下所示:
MATLAB实现k-means算法(k-均值)对无标签数据进行聚类,并通过肘部法则确定聚类类别_第5张图片
  可以看出,有些点的轮廓值小于0,说明该些点的聚类结果较差,不合理。
  2、用肘部法则判断最佳聚类类别数。
  Elbow Method(肘部法则) :Elbow意思是手肘,如下图左所示,此种方法适用于 K 值相对较小的情况,当选择的k值小于真正的时,k每增加1,cost值就会大幅的减小;当选择的k值大于真正的K时, k每增加1,cost值的变化就不会那么明显。这样,正确的k值就会在这个转折点,类似elbow的地方。 如下图:
MATLAB实现k-means算法(k-均值)对无标签数据进行聚类,并通过肘部法则确定聚类类别_第6张图片
MATLAB实现k-means算法(k-均值)对无标签数据进行聚类,并通过肘部法则确定聚类类别_第7张图片
  下面用本道题通过对不同的 K K K值求解进行解释:
  代码如下:

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('簇内误差平方和')

  结果试图如下:

  由此图可以看出,当分类数 K = 3 K=3 K=3时折线的下降趋势骤缓,故可将类别数设置为 3。
  
  
  
  

喜欢的小伙伴麻烦点个赞奥,谢谢啦

MATLAB统计分析与应用:40个案例分析
数学建模清风

你可能感兴趣的:(聚类,算法,matlab,k-means)