jjzjj

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

爱听雨的犬猫 2024-04-25 原文

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

文章目录

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

  结果如下所示:

  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

  结果如下所示:

  4、画出轮廓图

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

  结果如下所示:

  可以看出,每一个观测的轮廓值都是正的,这说明我们分为三类是合理的。
  全部代码如下所示:

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

  结果试图如下:

  轮廓图如下所示:

  可以看出,有些点的轮廓值小于0,说明该些点的聚类结果较差,不合理。
  2、用肘部法则判断最佳聚类类别数。
  Elbow Method(肘部法则) :Elbow意思是手肘,如下图左所示,此种方法适用于 K 值相对较小的情况,当选择的k值小于真正的时,k每增加1,cost值就会大幅的减小;当选择的k值大于真正的K时, k每增加1,cost值的变化就不会那么明显。这样,正确的k值就会在这个转折点,类似elbow的地方。 如下图:


  下面用本道题通过对不同的 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算法(k-均值)对无标签数据进行聚类,并通过肘部法则确定聚类类别的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  4. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  5. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  6. Matlab imread()读到了什么 (浅显 当复习文档了) - 2

    matlab打开matlab,用最简单的imread方法读取一个图像clcclearimg_h=imread('hua.jpg');返回一个数组(矩阵),往往是a*b*cunit8类型解释一下这个三维数组的意思,行数、数和层数,unit8:指数据类型,无符号八位整形,可理解为0~2^8的数三个层数分别代表RGB三个通道图像rgb最常用的是24-位实现方法,即RGB每个通道有256色阶(2^8)。基于这样的24-位RGB模型的色彩空间可以表现256×256×256≈1670万色当imshow传入了一个二维数组,它将以灰度方式绘制;可以把图像拆分为rgb三层,可以以灰度的方式观察它figure(1

  7. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  8. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  9. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  10. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

随机推荐