简介:这份资源面向MATLAB环境下从事数据分析与机器学习的学习者,聚焦K-means聚类在多维矩阵上的实现与可视化。内容围绕算法初始化、数据点分配、质心更新与迭代收敛四个环节展开,并演示如何借助内置kmeans函数完成聚类,再通过散点图、三维散点图及矩阵图像等方式直观呈现类簇分布与质心变化,帮助读者理解高维数据聚类的完整流程与评估思路。压缩包共7个文件,包含5个m脚本与2个mat数据文件,脚本分别承担主流程调度、质心初始化、最近质心查找、质心计算与整体运行等功能,数据文件则提供可直接加载的实验样本,整体约62KB,结构紧凑便于快速上手。目前已有2270人学习下载,适合希望掌握聚类算法落地与结果可视化技巧的读者参考实践。
1. 多维矩阵聚类到底在聚什么:从一堆看不动的数据说起
手里有一张 200 行 × 30 列的特征矩阵,每行是一个样本,每列是一个维度。你想知道这些样本能不能分成几拨、每拨有什么共性,但散点图最多画三维,超过三维人眼就彻底瞎了。这就是多维矩阵聚类的真实起点:不是算法有多玄,而是数据维度一高,人连“看一眼”的资格都没有了。
K-means 聚类解决的就是这件事。它把 N 个样本按特征相似度硬分成 K 个簇,让同一簇内的样本彼此靠近,不同簇之间尽量拉开。配合 MATLAB 的矩阵运算和可视化能力,从原始矩阵到带颜色标签的聚类结果图,整条链路可以在一份脚本里跑完。这套方案适合做特征工程后的样本分群、传感器多通道数据的工况识别、图像像素特征归类这类任务,也是层次聚类、DBSCAN 之外最常被拿来打基线的方法。下面按“原理选型 → 手写实现 → 可视化 → 避坑 → 调优”的顺序,把这条路走通。
2. K-means 在多维矩阵上的运算逻辑与选型理由
2.1 多维矩阵聚类的数学本质:距离、质心、迭代
K-means 的核心只有三件事:定 K 个初始质心、按距离把样本分配给最近质心、用簇内均值更新质心,反复迭代到质心不再移动或达到最大迭代次数。多维矩阵里,“距离”通常用欧氏距离,因为它在各维度量纲一致时几何意义最直观,也是 MATLABkmeans函数的默认距离度量。
设数据矩阵 X 为 N×D,第 i 个样本 x_i 是 D 维行向量,第 k 个质心 c_k 也是 D 维行向量,则样本到质心的欧氏距离为:
d(x_i, c_k) = sqrt( sum_{j=1}^{D} (x_ij - c_kj)^2 )
分配步骤取 argmin_k d(x_i, c_k),更新步骤取该簇所有样本的逐维均值。目标函数是簇内平方误差和 SSE,K-means 每次迭代保证 SSE 不增,但只能收敛到局部最优,所以初始质心的选择直接决定结果好坏。MATLAB 默认用 k-means++ 初始化,比纯随机撒点稳得多,这也是我一般不建议自己写随机初始化的原因。
2.2 为什么多维场景优先选 K-means 而不是层次聚类或 DBSCAN
层次聚类不需要预设 K,还能输出树状图,但计算复杂度在 O(N² log N) 到 O(N³) 之间,样本量上到几千就明显吃力,而且一旦某步合并错了没有后悔药。DBSCAN 能识别任意形状的簇和噪声点,但它的 eps 和 MinPts 两个参数在高维空间里极难调,维度一高距离就趋于均匀,密度定义直接失效。
K-means 的复杂度是 O(N·K·D·迭代次数),在 N 几千、D 几十的场景下秒级出结果,参数只有一个 K 需要定,配合肘部法和轮廓系数就能选得比较靠谱。代价是它假设簇是凸的、各向同性的,遇到长条形或嵌套簇会翻车。所以选型判断很简单:样本量大、维度中等、簇大致是球形团块,就用 K-means;簇形状诡异或噪声多,再考虑 DBSCAN;样本量小又想要层次结构,用层次聚类。
2.3 数据预处理:量纲不统一时聚类结果会彻底跑偏
多维矩阵最容易踩的坑是各列量纲差异巨大。比如一列是温度(0~100),一列是压力(0.001~0.01),欧氏距离会被温度维度完全主导,压力维度等于白给。所以聚类前必须做标准化,常用 z-score:
% X 为 N×D 原始数据矩阵 X = (X - mean(X)) ./ std(X); % 按列标准化,消除量纲影响mean(X)和std(X)默认按列运算,得到 1×D 的均值和标准差向量,广播到每一行完成标准化。如果某列标准差为 0(常量列),除法会产生 NaN,需要提前剔除或加极小值保护。标准化之后各维度方差为 1,欧氏距离才有可比性。这一步不做,后面调 K、换初始化方法都是白费力气。
3. 用 MATLAB 跑通多维矩阵 K-means 的完整脚本
3.1 生成或载入多维矩阵并确定聚类数 K
先构造一份可复现的多维数据,方便验证流程。这里生成 3 个簇、每簇 100 个样本、每个样本 5 维的数据:
rng(42); % 固定随机种子,保证结果可复现 D = 5; % 特征维度 N_per = 100; % 每簇样本数 K_true = 3; % 真实簇数 centers = randn(K_true, D) * 5; % 3 个真实质心,拉开距离 X = []; for k = 1:K_true X = [X; centers(k,:) + randn(N_per, D)]; % 每簇加高斯噪声 end X = (X - mean(X)) ./ std(X); % 标准化rng(42)固定种子,换机器换 MATLAB 版本结果一致。centers乘 5 是为了让簇间距离明显大于簇内噪声,否则聚类结果会糊在一起。标准化放在拼接之后做,保证所有样本用同一套均值和标准差。
确定 K 用肘部法,计算 K 从 1 到 10 的 SSE:
K_range = 1:10; sse = zeros(size(K_range)); for i = 1:length(K_range) [~, ~, sumd] = kmeans(X, K_range(i), 'Replicates', 5); sse(i) = sum(sumd); % sumd 是各簇内距离和,求和得总 SSE end plot(K_range, sse, '-o'); xlabel('聚类数 K'); ylabel('SSE'); title('肘部法确定 K');sumd是 k×1 向量,存每个簇内样本到质心的距离和,求和就是总 SSE。'Replicates', 5表示用 5 次不同初始化取最优,避免单次初始化陷入局部最优。肘部法看曲线拐点,拐点之后 SSE 下降变缓,那个 K 就是候选值。
3.2 调用 kmeans 并解读输出参数
确定 K=3 后正式聚类:
K = 3; [idx, C, sumd, D_mat] = kmeans(X, K, ... 'Distance', 'sqeuclidean', ... % 平方欧氏距离,计算更快 'Replicates', 10, ... % 10 次重启取最优 'MaxIter', 500, ... % 最大迭代次数 'Display', 'final'); % 只显示最终结果四个输出:idx是 N×1 簇标签,C是 K×D 质心矩阵,sumd是簇内距离和,D_mat是每个样本到每个质心的距离矩阵。'Distance'选sqeuclidean是因为开方不影响 argmin 结果,省掉 sqrt 能提速。'Replicates'设 10 比默认 1 稳,代价是时间乘以 10,样本量大时酌情降到 5。'MaxIter'500 对绝大多数数据够用,如果Display提示未收敛,再往上加。
聚类质量用轮廓系数验证:
s = silhouette(X, idx, 'sqeuclidean'); mean_s = mean(s); % 越接近 1 越好,低于 0.3 说明簇重叠严重silhouette对每个样本算“到本簇平均距离”和“到最近他簇平均距离”的相对差,均值反映整体分离度。这个值配合肘部法一起看,比只看 SSE 可靠。
3.3 高维结果降维到二维做可视化
5 维结果没法直接画,用 PCA 降到 2 维:
[coeff, score, ~, ~, explained] = pca(X); X_2d = score(:, 1:2); % 取前两个主成分 C_2d = (C - mean(X)) ./ std(X) * coeff(:, 1:2); % 质心同步投影coeff是 D×D 主成分系数矩阵,score是投影后的坐标,explained是各主成分方差贡献率。质心投影要注意:C是标准化空间里的质心,投影前得用同一套均值和标准差还原再乘coeff,否则质心会飘到图外。画图:
gscatter(X_2d(:,1), X_2d(:,2), idx, 'rgb', 'o', 6); hold on; plot(C_2d(:,1), C_2d(:,2), 'kx', 'MarkerSize', 12, 'LineWidth', 2); xlabel(sprintf('PC1 (%.1f%%)', explained(1))); ylabel(sprintf('PC2 (%.1f%%)', explained(2))); title('K-means 聚类结果(PCA 降维)'); legend('Cluster 1', 'Cluster 2', 'Cluster 3', '质心'); hold off;gscatter按idx分组着色,explained标注主成分解释的方差比例,让读者知道这张二维图保留了多少原始信息。如果前两个主成分加起来不到 60%,这张图只能当参考,不能当结论。
4. 多维 K-means 避坑:5 个血泪教训
4.1 现象:每次运行结果都不一样,簇标签还乱跳
原因:没固定随机种子,且 K-means 本身对初始化敏感。解决:脚本开头加rng(固定值),并把'Replicates'设到 5 以上。簇标签跳是正常的,K-means 的标签编号没有语义,比较两次结果前先用质心匹配对齐标签。
4.2 现象:SSE 很小但轮廓系数接近 0,图上簇全糊在一起
原因:K 选大了,把本来一个簇硬拆成两个,SSE 当然降,但簇间没有真实间隔。解决:别只看肘部法,轮廓系数低于 0.3 就说明 K 过大或数据本身没有聚类结构,这时候强行聚类没有意义。
4.3 现象:某一维数值特别大,聚类结果几乎由这一维决定
原因:没做标准化,量纲大的维度主导了欧氏距离。解决:聚类前按列 z-score 标准化。如果某些维度是类别型编码后的 0/1 值,标准化会破坏其含义,这类维度要么单独处理,要么改用适合混合类型的距离度量。
4.4 现象:kmeans报错“X 包含 NaN 或 Inf”
原因:原始矩阵里有缺失值或标准化时除以了零标准差列。解决:聚类前用sum(isnan(X(:)))和sum(isinf(X(:)))检查,缺失值用列均值填充或直接删行,常量列提前剔除。
4.5 现象:PCA 图上质心位置明显不对,飘到样本群外面
原因:质心投影时忘了用标准化参数还原,或者coeff用错。解决:质心必须和样本走同一套变换。标准化空间里的质心C先乘回标准差加回均值,再乘coeff(:,1:2),顺序不能反。
5. 让聚类结果更稳的三个进阶技巧
5.1 用 evalclusters 自动扫 K 并对比多种准则
手动写肘部法循环容易漏参数,MATLAB 的evalclusters能一次算完 SSE、轮廓系数、Calinski-Harabasz 三种准则:
eva = evalclusters(X, 'kmeans', 'CalinskiHarabasz', 'KList', 2:8); K_best = eva.OptimalK;'CalinskiHarabasz'可换成'silhouette'或'DaviesBouldin'。三个准则给出的最优 K 一致时最可信,不一致时优先信轮廓系数,它对簇重叠最敏感。
5.2 高维先降维再聚类,还是先聚类再降维
维度超过 50 时,欧氏距离会因“维度灾难”趋于均匀,K-means 效果急剧下降。常见做法是先用 PCA 保留 85%~95% 方差降到 10~20 维再聚类,而不是直接对原始高维矩阵跑。注意 PCA 是无监督的,降维后可能丢掉对聚类有用的小方差方向,所以降维前后都跑一次轮廓系数对比,别默认降维一定更好。
5.3 用平行坐标图检查每个簇的维度特征
PCA 散点图只告诉你分开了,不告诉你为什么分开。平行坐标图能展示每个簇在各原始维度上的分布差异:
parallelcoords(X, 'Group', idx, 'Quantile', 0.25);'Quantile', 0.25画四分位带,比画全部样本线清晰。哪个维度上各簇的带明显分离,那个维度就是区分簇的关键特征,这对后续做特征筛选或业务解释非常有用。
我自己的习惯是:任何一次聚类,肘部法、轮廓系数、平行坐标图三样都过一遍才敢下结论。只贴一张 PCA 散点图就宣布“分成了三类”,十有八九会在换一批数据时翻车。希望帮到你。
本文还有配套的精品资源,点击获取