简介一份围绕K-means聚类算法Matlab实现的文档型资源适合正在学习无监督学习、需要在Matlab中快速上手聚类分析的数据分析人员与科研初学者。文档系统梳理了K-means的基本原理、迭代步骤、距离度量选择并给出可直接运行的Matlab代码示例覆盖数据初始化、调用kmeans函数、获取簇索引与质心、绘制带聚类结果的可视化图形等关键环节同时对聚类数K设定、异常值敏感、初始化随机性等常见局限进行了说明并介绍了K-means、二分K-means及核K-means等改进思路。资源包共1个docx文件大小约15KB内容紧凑、便于离线查阅已有441人学习下载。对于希望边读边练的读者而言通过示例代码可直观理解聚类结果输出与图形绘制的对应关系也可据此延伸调整自己的数据集是一份实用的小型算法参考资料。1. kmeans聚类算法MATLAB代码这份文档到底能帮你解决什么拿到这份标题为“kmeans聚类算法matlab代码”的文档多数人第一反应是“里面有一份能直接跑的代码我抄下来就能用”。但如果你真这么干大概率会在自己的数据上翻车——不是跑报错而是结果一塌糊涂还看不出问题在哪。kmeans这算法代码不过三五十行真正耗时间的从来不是“写”而是“选K、预处理、判断结果合不合理”这三件事。这份文档的方向是对的它把算法、代码和用法打包在一起适合三类人第一类是刚接触聚类、想快速在MATLAB里跑通一个最小例子的新手第二类是有数据要分群、但不确定K值怎么选、结果怎么判断的从业者第三类是面试前想手推一遍算法、加深理解的备考者。接下来我会沿着“原理讲解 → 可复现代码 → 参数调优 → 避坑”这条线把它拆成一套可以直接落地的操作流程。2. 先别急着写代码kmeans的算法逻辑与MATLAB实现选型2.1 kmeans的核心逻辑迭代收敛的那几步每一步在做什么kmeans的思路通俗讲就是“按距离把点分给离它最近的中心然后挪动中心到这一簇点的均值处重复直到中心不再动”。数学表达很简单给定数据集Xn行d列n个样本d个特征以及预设的簇数K算法就是在最小化一个目标函数即所有样本到其所属簇中心的距离平方和通过交替执行“分配”与“更新”两步来逼近最优解。先看分配步对每个样本xi计算它与K个中心c1到cK的距离把xi划给距离最近的那个簇中心。这里距离度量默认为欧氏距离这也是kmeans对特征量纲敏感的根本原因——某个特征数值范围大的话它会主导整个距离计算。再看更新步对每个簇重新计算该簇内所有样本的均值向量作为新的簇中心。这两个步骤交替迭代直到簇中心的变化量小于某个阈值或达到了最大迭代次数。这个目标函数是非凸的所以kmeans不保证收敛到全局最优最终结果强烈依赖初始中心的位置。这也是同一份代码多次运行结果不一致的根源。理解了这两步和这个特性后面调参数和排查问题就都有了依据。2.2 MATLAB里两条路内置kmeans函数与手写实现分别什么时候用MATLAB里实现kmeans有两条路直接调用内置函数这是实践中最常用的方式它会自动处理迭代收敛、空簇重初始化等细节还能通过参数控制初始中心的选择。另一条路是自己手写一个kmeans循环好处是能深入理解每个细节且可以按需修改算法结构。结论是如果你不是在做教学数据规模也正常直接用内置函数把时间省下来去处理K值选择和数据预处理。有些场景我会选择手写比如数据量特别大、需要自己控制内存占用时比如要换一种距离度量方式、内置的几种都不满足时又比如学习阶段想验证自己是否真正理解了算法。下表可以帮你快速做选型场景推荐方式原因常规聚类分析、快速看结果内置函数代码量小参数齐全稳定可靠学习原理、演示算法流程手写实现能直观看到“分配-更新”循环细节大数据集或特殊距离度量手写修改版内置函数的内存分配和距离选项受限需要可复现的生产脚本内置函数 固定随机种子结果稳定且便于嵌入自动化流程2.3 这份文档的代码应该怎么读三分看代码、七分看参数这类kmeans代码文档拿到手的第一步不是急着复制跑而是先看它的数据组织方式。大多数示例会用一批二维散点数据做演示因为二维数据可以直接画图、直观看到聚类效果。你要做的是把二维数据换成自己真实场景里的数据这涉及两个基本操作数据矩阵X必须是n行乘d列的格式每一行是一个样本每一列是一个特征很多文档会提供手动构造测试数据或加载外部Excel数据的示范。第二步是看它用了哪些参数。这里单独看一眼内置函数最常用的调用形式代码里的参数一般包括K簇数、距离选项、最大迭代次数、重复次数等。设置不同的参数运行时间与聚类结果会有不小差异。关于K怎么选后面会单独展开。3. 用MATLAB内置kmeans跑通最小聚类代码从数据准备到可视化3.1 数据准备与第一版最小可运行代码先造一份最简测试数据方便你验证整条链路跑通再替换成自己的数据。我这里用随机生成三团高斯分布数据模拟真实的聚类输入并写好最简调用代码。% 生成三类测试数据每类200个点二维特征 rng(1); % 固定随机种子保证结果可重现 X [randn(200,2) * 0.5 [ 1, 1]; randn(200,2) * 0.6 [-1, 2]; randn(200,2) * 0.4 [ 2,-2]]; % X为600行2列的样本矩阵 K 3; % 指定聚类数为3 [idx, C] kmeans(X, K); % idx为每个样本的类别标签C为3个簇中心坐标这段代码把三团数据按不同均值和方差叠在一起600个点各带二维坐标。rng(1)用于锁定随机数生成器否则每次生成的测试数据点都不同影响你对代码本身的验证。kmeans返回的idx是600乘1的向量元素取值是1到K范围内的整数代表每个样本最终被分到的簇编号C是K乘2的矩阵每一行是簇中心的坐标。第一次跑完的检查习惯是确认idx里每个类别的样本数量是否合理用histcounts(idx, K)看一眼如果出现某个簇数量为0说明初始中心选择出了问题或K值不匹配数据分布。可视化这一步马上补上。3.2 画图验证聚类效果散点图染色与质心标注代码跑出idx和C之后必须画图确认结果是否符合直觉因为kmeans任何时候都可能给出一个局部最优局部最优和全局最优有时候差得非常远。对二维数据画图最直观高维数据也能用前两个主成分降维后近似展示但要注意降维投影可能夸大或掩盖真实的聚类边界。figure; gscatter(X(:,1), X(:,2), idx); % 按聚类结果给散点染色 hold on; plot(C(:,1), C(:,2), kx, MarkerSize, 12, LineWidth, 2); % 黑色叉号标出簇中心 legend(Cluster 1, Cluster 2, Cluster 3, Centroids); title(K-Means Clustering Result (K3)); xlabel(Feature 1); ylabel(Feature 2); hold off;gscatter是分组散点图函数按idx中的类别值自动分配颜色和图例比手写scatter循环省事得多。簇中心用黑色叉号叠加在散点图上是为了肉眼判断中心是否落在各簇的“几何重心”位置如果中心偏离明显通常说明初始中心选到了边界位置算法陷入了局部最优后面会用Replicates参数来解决。3.3 返回值的完整解读idx之外还有两个值值得用kmeans函数完整返回格式是[idx, C, sumd, D]多数示例只用了前两个但后两个在生产场景里有大用处。sumd是K乘1向量每个元素是该簇内所有样本到中心距离的累加和它是手肘法则和簇内紧密程度评估的直接依据D是n乘K矩阵每个样本到每个中心的距离都在里面可用于后续的样本级别的离群分析或二次聚类。[idx, C, sumd, D] kmeans(X, K); fprintf(每个簇的簇内距离总和\n); disp(sumd);有了sumd可以算出整体簇内平方和也就是所有簇距离加总记为SSE。SSE是选择K值时手肘法则的纵轴数值。而D矩阵可以用于查看哪些样本同时到两个簇中心的距离都很近这些样本通常是聚类最容易出错的“边界样本”。我一般会在聚类后计算每个样本到其所属中心的距离排序找出距离最大的前5%单独标记出来检查它们是不是数据里的噪声点或异常点。4. 参数调优三个硬指标K值选择、数据标准化、初始中心设置4.1 手肘法则为什么经常翻车SSE曲线的“肘部”怎么看才靠谱手肘法则的思路是遍历K从1到10对每个K计算整体SSE然后画出K与SSE的关系曲线找曲线下降趋势由陡转缓的拐点那个K就是比较合适的聚簇数。理论上很完美实际画出来经常没有明显“手肘”曲线平滑下降找不到拐点这时再纠结就属于玄学了。经验是手肘法则在数据本身存在清晰簇结构时有效当数据分布连续、没有天然分群时曲线几乎是一条平滑递减线看不到肘点。有两个增强方法让手肘更可用。第一对SSE曲线做差分或二阶差分看下降幅度突变的位置相当于把“肉眼找拐点”变成“数值找拐点”第二结合轮廓系数交叉验证用两个指标互相印证如果两个指标给出的K不一致优先相信与业务场景解释一致的那个。Klist 1:8; sse zeros(size(Klist)); for k Klist [~, ~, sumd] kmeans(X, k, Replicates, 5); sse(k) sum(sumd); % 该K下所有簇的簇内距离总和 end plot(Klist, sse, o-); xlabel(K); ylabel(SSE); title(Elbow Method for Choosing K);这段循环手动汇总不同K下的整体SSE。中间设置Replicates, 5很关键因为单个K值下的kmeans结果可能是局部最优同一个K跑5次取最优的SSE会更平滑、更少毛刺。有明显“肘部”时选拐点没明显拐点时把K的确定交给业务目标——比如做用户分群时你预算只有3套运营策略那就选K等于4以内最小且得分合理的值。4.2 标准化对kmeans是刚需什么时候用zscore、什么时候用归一化kmeans使用欧氏距离意味着每个特征都在距离计算里以“数值大小”的方式投票。如果你有一个特征取值0到100另一个特征全是0到1距离计算基本会被第一个特征主导第二个特征等于白给。这一点几乎每个第一次用kmeans的人都会踩所以标准化预处理不是可选项而是必选项。MATLAB里最省事的标准化命令是zscore它将每一列变成均值0、标准差1的分布适合大多数连续型特征。如果你有量纲一致但尺度差异大的特征比如都是百分比或都在同一个业务口径下也可以直接用normalize做范围缩放。两者不是二选一的关系——通常我会默认zscore遇到需要保留稀疏性或0值含义的场景另说但kmeans本来就不擅长处理稀疏数据。X_std zscore(X); % 每个特征列分别做零均值单位方差标准化 [idx, C] kmeans(X_std, K);使用标准化后的数据做聚类迭代收敛通常会更快因为各方向上尺度一致后距离计算更均衡。需要记住的是后续画图或解读中心点时C里的坐标是标准化之后的值要还原成原始尺度需要自己做逆变换即乘回原标准差再加回原均值否则业务人员看到中心点坐标会一头雾水。4.3 初始中心与随机性问题Replicates、Start参数的正确打开方式kmeans的结果依赖初始中心MATLAB内置函数默认使用随机选择K个样本点作为初始中心。同一数据跑两次结果可能完全不同——轻则簇标签序号变了重则整体聚类结构都不同。解决思路有两条多次重启取最优以及固定随机种子保证可复现。Replicates参数在一次调用里重复多次聚类每次用不同的随机初始中心最终返回整体SSE最小的那次结果。Start参数可以指定初始中心矩阵或选用的初始化算法比较实用的是plus对应kmeans策略它让初始中心尽量彼此远离显著降低落到坏局部最优的概率。rng(42); % 固定全局随机种子保证整个流程可复现 [idx, C, sumd] kmeans(X, K, Replicates, 10, Start, plus);这行的实际意义是用kmeans方式选初始中心重复10次聚类输出效果好的一次。稳定的生产脚本必须能复现所以我习惯在脚本开头固定rng种子再配合Replicates取最优。如果你发现换种子后聚类结果有较大差异说明数据本身没有稳定簇结构这时候再调参意义不大先回头检查数据分群是否合理、是否应该换算法或换特征。4.4 轮廓系数K值选得对不对用它做第二重验证手肘法看整体紧密程度但簇与簇之间的分离程度没法体现这时看轮廓系数。轮廓系数对单个样本计算取值范围负一到一接近1说明该样本离自己簇内近、离邻近簇远聚类效果好接近负一说明样本可能被分错了簇。总体轮廓系数是全部样本的平均值它可以作为K值选择的量化指标一般大于0.25就说明有一定聚类结构大于0.5说明结构明显。for k 2:8 [idx] kmeans(X, k, Replicates, 5, Start, plus); s silhouette(X, idx); % 计算每个样本的轮廓值 meanS(k) mean(s); % 记录该K下的平均轮廓系数 end plot(2:8, meanS(2:8), o-);silhouette函数既能返回每个样本的轮廓值也能画轮廓图轮廓图里每个簇的样本宽度与颜色能直观表达簇内紧密程度。多次运行取平均是必要的因为单次kmeans结果不稳定会直接影响轮廓值。把轮廓系数最优的K和手肘法则的拐点K对照取两者都指向的那个值通常就是数据支持的簇数两者冲突时优先看业务上哪个分群更有解释力——kmeans解决的是无监督探索问题算法指标永远为业务解释服务。5. 避坑指南kmeans在MATLAB里最常见的五个翻车现场5.1 每次运行聚类结果都不一样完全没法稳定复现现象同一份数据、同一段代码连续运行两次idx结果不同甚至画出的聚类图差异很大。原因kmeans初始中心随机选择非凸目标函数存在多个局部最优算法不知道自己落在哪个最优上。解决固定随机种子加Replicates参数。固定种子用rng(20)写在脚本开头能让每次运行走同一个随机序列Replicates设为10以上能显著提高输出质量。如果固定种子和多次重启后结果依旧跳变核心问题就是数据没有明显簇结构这不是调参能解决的。5.2 聚类结果里出现了空簇某个类别数量为0现象K设为5跑完发现其中一个簇的样本数为0画图只有4个类kmeans没有报错。原因初始中心可能有两个选到了同一簇内或者某个中心离所有样本都太远在迭代中被“饿死”了。解决使用Start, plus让初始中心尽可能分散同时检查数据是否存在大量离群点。标准化的数据一般能缓解这个问题。若空簇仍然存在把K调小一档再试或考虑改用密度型聚类算法处理这种形状的数据。5.3 聚类结果被单个特征主导另一个特征完全没起作用现象数据有两个特征一个取值0到1000一个取值0到1聚类图看起来几乎只按第一个特征在切分。原因欧氏距离计算时数量级大的特征天然权重更大数量级小的特征贡献被稀释。解决聚类前必须做特征标准化zscore或normalize都行。这是一条硬规则凡是用距离类算法先标准化再建模别无脑直接喂原始数据。5.4 聚类标签的序号每次都不一样1、2、3的含义不稳定现象两次运行结果从直观上看聚类结构一样但标签序号对不上第一次的簇1第二次变成簇3。原因簇标签本身是任意编号的kmeans输出的类别序号没有固定含义算法不保证两次运行标签编号一致。解决需要用簇中心的数值顺序来给簇“命名”。常见做法是聚类完成后按簇中心的某个特征列排序再重映射标签让编号保持稳定。这个坑不影响聚类本身质量但影响程序后续处理。5.5 高维数据画不出聚类效果降维投影图掩盖了真实结构现象10个特征的聚类结果用前两个主成分画散点图看起来所有点挤成一团分不出聚类结构但轮廓系数明明不低。原因降维只是线性投影必然丢失一部分信息前两个主成分不一定包含全部可分离信息。解决先看轮廓系数和簇内距离这些量化指标做判断不要过度依赖二维投影图高维数据可以改用t-SNE或UMAP这类非线性降维辅助可视化但只用于展示不建议拿降维后的坐标做聚类输入。这种场景靠二维图判断聚类好坏最容易误判。提示遇到任何kmeans结果和业务常识严重冲突的情况先停下来把样本点拿出来逐条看不要盲目相信算法输出。6. 进阶技巧手写kmeans框架以及验证聚类稳定性的一个习惯内置函数用熟了以后你很快就会想把它替换成手写版本——可能因为你需要在自定义距离度量中加业务规则也可能因为你需要处理内存放不下的数据。手写版其实不复杂核心循环就比内置函数多几行而已。下面给一个最小框架功能等价于内置函数的基础模式。function [idx, C] my_kmeans(X, K, maxIter) % 最小手写kmeans默认欧氏距离最大迭代100次 if nargin 3, maxIter 100; end [n, d] size(X); rng(1); % 从样本中随机选取K个初始中心 C X(randperm(n, K), :); for iter 1:maxIter % 分配步计算每个样本到每个中心的距离取最近中心 D pdist2(X, C, euclidean); [~, idx] min(D, [], 2); % 更新步每个簇内样本的均值作为新中心 for k 1:K if any(idx k) C(k, :) mean(X(idx k, :), 1); end end end每一轮迭代先通过pdist2计算n乘K的距离矩阵再用min沿行方向找出每个样本最近的簇中心更新步则逐簇重新计算中心点坐标。手写版本没有内置函数那么多保护机制比如空簇处理所以代码里加了any(idx k)的判断否则空簇会让mean计算出错。这套框架够应付常规学习和二次改造真正生产环境还是推荐内置函数——稳定性和性能都更好。另一个我长期保留的习惯是聚类稳定性验证对同一份数据用不同的随机种子跑10到20次kmeans检查两次运行间有多少样本的簇归属发生变化。如果变化比例超过10%说明数据本身没有稳定的簇结构这时候的聚类结果是脆弱的不适合直接交付给业务用。这个验证没有现成函数手写十几行就能完成但它能帮你避开“拿一份偶然结果当真”的坑。我给自己定的习惯是拿到任何kmeans代码先跑通、再改参数、再验证稳定性三步走完才敢在项目里正式使用。希望这份拆解能帮到你尽快把文档里的代码真正变成你自己的工具。本文还有配套的精品资源点击获取