MATLAB K-means聚类实战:从7个点到参数调优与避坑指南
简介这份资源是一份面向数据分析初学者与Matlab使用者的K-means聚类算法实现文档聚焦无监督学习中的经典聚类方法帮助读者理解算法原理并快速上手代码实践。包内共1个docx文件压缩包约15KB内容以文字讲解配合Matlab示例代码为主涵盖算法基本步骤、kmeans函数调用方式、结果可视化以及局限性与改进方向等模块。文档通过一个二维数据点的具体案例演示了数据初始化、聚类执行、质心提取与散点图绘制的完整流程并延伸讨论了K-means、Bisecting K-means、Kernel K-means等改进思路便于读者对照代码理解参数含义与调参方向。目前已有441人学习浏览适合需要完成课程作业、实验报告或入门聚类分析的学习者参考也可作为Matlab聚类工具箱使用的入门材料。1. 从一份 kmeans 聚类算法 matlab 代码.docx 说起7 个点、2 个簇能跑通什么很多人第一次接触无监督学习都是从 K-means 开始的。手头这份kmeans聚类算法matlab代码.docx就是一份典型的入门级实现文档核心只有一段不到 20 行的 MATLAB 代码7 个二维数据点、K2、调用内置kmeans函数、用scatter画出聚类结果。数据点少到可以手算代码短到可以背下来但恰恰是这种最小可运行单元最适合拿来把 K-means 的每个参数、每个返回值、每个坑位拆干净。这份资源能解决什么问题如果你正在做数据分群、客户画像、图像颜色量化、异常检测的预处理或者只是想在 MATLAB 里快速验证一个聚类想法这段代码就是你的起点。它适合两类人一是刚上手 MATLAB 聚类工具箱、需要一份能直接跑通的示例代码的新手二是想快速回顾kmeans函数签名和参数含义、懒得翻官方文档的老手。下面我不复述文档里的代码而是把它拆成能复现、能改参数、能排错的实战笔记。2. MATLAB kmeans 函数签名与参数体系从 idx、centroids 到距离度量2.1 函数签名与返回值到底怎么读MATLAB 的kmeans函数最简调用形式是[idx, centroids] kmeans(data, K)但它的完整签名远比这复杂。先看返回值idx是一个 N×1 的向量N 是样本数每个元素是 1 到 K 之间的整数表示该样本被分到哪个簇centroids是 K×D 的矩阵D 是特征维度每一行是一个簇的质心坐标。这两个返回值是后续所有分析的基础——idx用来做分组统计和可视化着色centroids用来做簇间距离分析和业务解释。很多人第一次用的时候会困惑为什么idx是列向量而不是行向量因为 MATLAB 默认按列组织样本data的每一行是一个样本、每一列是一个特征。如果你从 CSV 读进来的数据是转置的kmeans会按错误的维度聚类结果看起来能跑但完全不对。这是最常见的翻车点之一。2.2 关键参数Distance、Replicates、Start、MaxIterkmeans的参数体系里有四个参数直接决定聚类质量和运行行为必须搞清楚。参数默认值作用什么时候要改Distancesqeuclidean距离度量方式特征量纲差异大时改cityblock或标准化后再用欧氏Replicates1重复聚类次数取最优数据量大或担心局部最优时设 510Startplus初始质心选取策略想复现结果时用sample配合rng固定种子MaxIter100最大迭代次数收敛慢的数据集调到 300500Distance默认是平方欧氏距离注意是平方欧氏不是欧氏。这意味着如果你手动算距离来验证结果要用平方而不是开根号否则对不上。Replicates是最容易被忽略的参数——默认只跑一次如果初始质心选得不好可能收敛到局部最优。设成 5 或 10MATLAB 会跑多次取sumd最小的那次代价是运行时间线性增加。Start默认是plus也就是 K-means 初始化这比纯随机选点好很多。但如果你想复现某次实验结果plus内部有随机性需要配合rng(seed)固定随机种子。我一般会在脚本开头写rng(42)这样每次跑出来的idx和centroids完全一致方便调试和对比。2.3 用一份可复现的脚本把参数串起来下面这段代码在原始示例基础上做了扩展固定随机种子、显式指定参数、输出簇内平方和、并做基本的聚类质量检查。% kmeans_demo.m % 固定随机种子保证结果可复现 rng(42); % 原始数据7 个二维点 data [1 1; 1.5 2; 3 4; 5 7; 3.5 5; 4.5 5; 3.5 4.5]; % 聚类簇数 K 2; % 完整参数调用 [idx, centroids, sumd, D] kmeans(data, K, ... Distance, sqeuclidean, ... % 距离度量 Replicates, 5, ... % 重复 5 次取最优 Start, plus, ... % K-means 初始化 MaxIter, 300, ... % 最大迭代次数 Display, final); % 输出最终迭代信息 % 输出结果 fprintf(簇内平方和 sumd: %s\n, mat2str(sumd)); fprintf(质心坐标:\n); disp(centroids); % 检查每个簇的样本数 for k 1:K fprintf(簇 %d 样本数: %d\n, k, sum(idx k)); end这段代码比原始示例多了三个返回值sumd是每个簇内点到质心的距离平方和D是每个点到所有质心的距离矩阵。sumd是评估聚类质量的核心指标——总sumd越小簇内越紧凑。D可以用来做轮廓系数或者找离质心最远的异常点。参数说明Replicates, 5意味着 MATLAB 内部跑 5 次独立聚类每次用不同的初始质心最后返回sumd总和最小的那次结果。Display, final会在命令窗口打印最终迭代次数和总距离方便你判断是否收敛。如果迭代次数接近MaxIter说明可能没收敛需要调大MaxIter或者检查数据是否适合 K-means。2.4 数据标准化什么时候必须做怎么做原始示例的数据点坐标范围在 1 到 7 之间两个维度的量纲差不多所以不标准化也能跑。但实际项目中特征维度经常是年龄 0-100和收入 0-100000这种量级差异如果不标准化收入维度会完全主导距离计算年龄维度等于没用。常见做法是用zscore做 Z-score 标准化% 对每一列做 Z-score 标准化 data_norm zscore(data); % 用标准化后的数据聚类 [idx, centroids_norm] kmeans(data_norm, K, Replicates, 5); % 把质心还原到原始尺度可选 centroids_orig centroids_norm .* std(data) mean(data);zscore按列减均值除标准差标准化后每个维度均值为 0、标准差为 1。注意centroids_norm是标准化空间的质心如果要画在原始数据上需要按上面的公式还原。这一步很多人会忘导致质心画出来位置不对。3. 从 7 个点到真实数据集可视化、K 值选择与结果解读3.1 可视化代码的细节与常见错误原始示例用scatter画散点、用scatter画质心逻辑是对的但有几个细节容易出问题。第一scatter(data(:,1), data(:,2), [], idx, filled)里第四个参数idx是颜色映射依据MATLAB 会自动用lines色图给不同簇着色。但如果idx是列向量而data是矩阵维度要对齐。第二质心用Marker, x标记但scatter的Marker参数在某些版本里要用x而不是Marker, x写法有差异。下面是我常用的可视化模板加了簇边界和样本数标注figure(Position, [100 100 600 500]); hold on; % 画数据点按簇着色 gscatter(data(:,1), data(:,2), idx, br, o, 8); % 画质心 plot(centroids(:,1), centroids(:,2), kx, ... MarkerSize, 12, LineWidth, 2); % 标注每个簇的样本数 for k 1:K text(centroids(k,1)0.1, centroids(k,2)0.1, ... sprintf(C%d (n%d), k, sum(idxk)), ... FontSize, 10, Color, k); end xlabel(Feature 1); ylabel(Feature 2); title(sprintf(K-means 聚类结果 (K%d), K)); legend(Cluster 1, Cluster 2, Centroids, Location, best); grid on; hold off;gscatter比scatter更适合分组散点图它直接接受分组变量idx和颜色字符br自动处理图例。质心用黑色x标记MarkerSize设 12 让它更显眼。标注样本数是为了快速判断簇是否均衡——如果一个簇只有 1 个点另一个簇有 6 个点说明 K 值可能选大了或者数据本身不适合 K-means。3.2 K 值怎么选肘部法、轮廓系数与业务约束原始示例直接指定 K2但实际项目中 K 值往往需要数据驱动地选。最常用的是肘部法对 K1 到 K10 分别跑聚类画总sumd随 K 变化的曲线找拐点。K_range 1:10; sumd_total zeros(length(K_range), 1); for i 1:length(K_range) [~, ~, sumd] kmeans(data, K_range(i), Replicates, 5); sumd_total(i) sum(sumd); end figure; plot(K_range, sumd_total, bo-, LineWidth, 1.5); xlabel(聚类簇数 K); ylabel(总簇内平方和); title(肘部法选择 K 值); grid on;总sumd随 K 增大单调递减因为簇越多、每个簇越紧凑。肘部法找的是下降速率突然变缓的那个 K。但肘部法不是万能的有时候曲线很平滑没有明显拐点。这时候可以配合轮廓系数Silhouette CoefficientMATLAB 的evalclusters函数可以直接算eva evalclusters(data, kmeans, silhouette, KList, 2:6); fprintf(最优 K轮廓系数: %d\n, eva.OptimalK); figure; plot(eva);evalclusters会自动跑不同 K 值并计算评估指标silhouette是轮廓系数越接近 1 越好。除了轮廓系数还支持CalinskiHarabasz和DaviesBouldin。我一般会同时看两三个指标如果它们一致推荐同一个 K那基本可以确定如果分歧大就要回到业务场景判断——比如客户分群业务方可能明确要求分成 3 档那 K3 就是硬约束。3.3 结果解读idx 和 centroids 怎么变成业务结论聚类跑完只是第一步把idx和centroids翻译成业务语言才是价值所在。以客户分群为例假设两个特征是月均消费和到店频次聚类后得到两个簇质心分别是 (200, 2) 和 (800, 8)。你可以这样解读簇 1 是低频低消费客户簇 2 是高频高消费客户。然后统计每个簇的样本占比、其他特征均值形成画像。% 假设 data 有两列月均消费、到店频次 % 统计每个簇的均值和样本占比 for k 1:K mask (idx k); fprintf(簇 %d: 占比 %.1f%%, 月均消费均值 %.1f, 到店频次均值 %.1f\n, ... k, 100*sum(mask)/length(idx), ... mean(data(mask,1)), mean(data(mask,2))); end这段代码输出每个簇的样本占比和特征均值直接可以写进分析报告。注意mean(data(mask,1))里的mask是逻辑索引MATLAB 里用逻辑向量索引矩阵非常高效。如果特征多可以用grpstats函数一次性算所有特征的分组统计。4. 避坑与排查K-means 在 MATLAB 里的五个血泪教训4.1 现象聚类结果每次跑都不一样原因kmeans默认Startplus内部有随机初始化不固定随机种子时每次结果不同。解决在脚本开头加rng(42)或者显式指定Start为一个 K×D 的初始质心矩阵。如果连rng都不管用检查是不是开了并行池parpool并行环境下随机流是独立的需要在每个 worker 上单独设种子。4.2 现象警告Failed to converge in MaxIter iterations原因数据分布复杂或者 K 值过大100 次迭代不够。解决把MaxIter调到 500 甚至 1000同时观察sumd是否还在下降。如果调到 1000 还不收敛大概率是数据不适合 K-means——比如簇是长条形或者密度差异极大这时候要考虑 DBSCAN 或高斯混合模型。4.3 现象某个簇的样本数为 0 或 1原因K 值选大了或者初始质心恰好落在孤立点上。解决先用肘部法或轮廓系数确认 K 值如果 K 值合理但仍有空簇把Replicates调大比如 10让 MATLAB 多试几次初始质心。另外检查数据里有没有重复点或异常值异常值会吸走一个质心。4.4 现象质心画在图上位置明显不对原因对标准化后的数据聚类但质心没还原到原始尺度就画图。解决用centroids_orig centroids_norm .* std(data) mean(data)还原。注意std和mean默认按列计算如果数据是转置的要加std(data, 0, 1)显式指定维度。4.5 现象中文注释在 MATLAB 编辑器里乱码原因MATLAB 2023 及更早版本对 UTF-8 支持不完善.m文件默认编码可能是 GBK。解决在 MATLAB 首选项里把编辑器/调试器的编码改成 UTF-8或者用feature(DefaultCharacterSet, UTF-8)在脚本开头强制设置。如果文件已经乱码用记事本打开另存为 UTF-8 再重新导入。5. 进阶技巧用 evalclusters 自动选 K 与并行加速5.1 evalclusters 的三种评估指标怎么选evalclusters支持silhouette、CalinskiHarabasz、DaviesBouldin三种指标。轮廓系数计算每个点到同簇其他点的平均距离和到最近簇的平均距离适合簇形状凸且均衡的数据Calinski-Harabasz 是簇间方差与簇内方差的比值计算快适合大数据集初筛Davies-Bouldin 是簇内散度与簇间距离的比值越小越好。我一般先用 Calinski-Harabasz 快速扫一遍 K 范围再用轮廓系数精细确认。% 三种指标对比 eva_ch evalclusters(data, kmeans, CalinskiHarabasz, KList, 2:8); eva_sil evalclusters(data, kmeans, silhouette, KList, 2:8); eva_db evalclusters(data, kmeans, DaviesBouldin, KList, 2:8); fprintf(CalinskiHarabasz 推荐 K: %d\n, eva_ch.OptimalK); fprintf(Silhouette 推荐 K: %d\n, eva_sil.OptimalK); fprintf(DaviesBouldin 推荐 K: %d\n, eva_db.OptimalK);如果三个指标推荐一致直接采用如果不一致看业务约束。注意evalclusters内部会多次调用kmeans数据量大时很慢可以配合Replicates, 3减少重复次数。5.2 大数据集下的并行与内存控制当样本数超过 10 万、特征超过 20 维时kmeans的内存和耗时都会明显上升。两个优化方向一是用parfor并行跑不同 K 值的评估二是用kmeans的OnlinePhase参数如果版本支持。更通用的做法是先用datasample随机抽样 10% 的数据选 K 值再用全量数据跑最终聚类。% 随机抽样 10% 选 K 值 rng(42); sample_idx datasample(1:size(data,1), round(0.1*size(data,1)), Replace, false); data_sample data(sample_idx, :); eva evalclusters(data_sample, kmeans, silhouette, KList, 2:8); K_opt eva.OptimalK; % 全量数据最终聚类 [idx, centroids] kmeans(data, K_opt, Replicates, 5, MaxIter, 500);datasample的Replace, false表示无放回抽样保证样本不重复。抽样比例一般 10% 到 20% 足够稳定太小则评估指标波动大太大则失去加速意义。5.3 一个我常犯的错误早期我跑聚类从来不设rng觉得随机就随机结果差不多就行。直到有一次做客户分群同一份数据跑了三次得到三个不同的分群方案业务方拿着三份报告问我到底以哪个为准场面非常尴尬。从那以后我每次跑kmeans都强制在脚本第一行写rng(42)并且在报告里注明随机种子。这个习惯看起来不起眼但能省掉大量为什么结果又变了的沟通成本。希望帮到你。本文还有配套的精品资源点击获取