简介面向机器学习入门者与需要快速上手聚类实验的MATLAB用户这套代码合集围绕无监督分类任务整理了FCM模糊C均值、K-means、KmedoidKmedia以及GK、GG等聚类方法的实现并配有可直接运行的演示脚本与FuzzyClusteringToolbox工具箱说明。压缩包共74个文件、约2.05MB主体为40个.m函数脚本涵盖聚类主程序、归一化与反归一化、聚类有效性评价validity.m、clusteval.m以及PCA降维、Sammon投影等辅助模块此外还有1份PDF工具箱文档、1个.mat数据文件、6个txt说明和若干gif、png图示方便对照理解算法流程与结果展示。资源已有4888人学习下载读者可据此完成数据标准化、参数调试、结果可视化以及轮廓系数等指标对比在FCM与K-means的适用边界、非球形分布数据处理上形成直观认识也便于二次修改为自己的课程实验或项目代码。1. 聚类没有标准答案为什么同一份数据四种算法会给出四张图同一份二维数据交给 K-means 会切出几块边界笔直的凸区域交给 FCM 会得到一片隶属度渐变的“雾”交给 SOM 这类神经网络会得到一条自己蜷起来的拓扑线交给层次聚类会长出一棵可以任意剪枝的树。四张图放在一起没有哪张是错的。这不是玄学而是聚类这件事本身就没有唯一解——没有标签没有可以对标真值的损失所谓“簇”完全取决于你用哪种相似性去切割空间。搞懂这套 MATLAB 聚类代码的价值不在于多记几个函数名而在于能从结果的形状反推出算法在优化什么从而判断什么时候该信它、什么时候它已经在胡说。神经网络那一支最容易被当成黑匣子其实它的每一步权值更新都能写成显式公式。2. K-means质心交替为什么必然收敛又为什么必然停在局部最优2.1 目标函数把“簇内紧密”写成一个能求闭式解的量K-means 的全部行为都由一个目标函数决定J Σ(k1..K) Σ(x∈S_k) || x - c_k ||²其中 S_k 是第 k 个簇的样本集合c_k 是它的质心。这个式子只关心一件事每个点到它所属质心的欧氏距离平方和。它同时隐含了两个很强的假设簇是凸的、各向同性的球状且各簇规模大致相当。后面所有“K-means 效果不好”的抱怨几乎都能追溯到这两条假设被违反而不是代码写错了。写成指示变量形式更便于推导。令 r_nk ∈ {0,1} 表示第 n 个样本是否属于第 k 个簇且满足约束 Σ(k) r_nk 1则J Σ(n) Σ(k) r_nk || x_n - c_k ||²这就是所谓的硬划分每个样本只能归一个簇隶属度非 0 即 1。FCM 后面要做的事本质就是把这个 0/1 松成 [0,1] 区间上的连续值其余结构几乎不动。2.2 两步交替固定一边另一边就有闭式解J 同时依赖 r 和 c联合优化属于组合优化也被称为 k-means 问题精确求解是 NP-hard 难。但固定其中一个变量时另一个的最优解有解析形式这正是交替优化的切入点。固定 c 求 r每个样本相互独立最小化 ||x_n - c_k||² 显然是选最近的质心即 r_nk 1 当且仅当 k argmin_j ||x_n - c_j||²。这一步习惯上叫分配步。固定 r 求 c对 c_k 求偏导并令其为零∂J/∂c_k -2 Σ(n) r_nk (x_n - c_k) 0解得 c_k Σ(n) r_nk x_n / Σ(n) r_nk也就是簇内均值。这一步叫更新步。注意“均值”不是随便挑的统计量它是平方误差准则下的精确最优解。如果把准则换成绝对误差最优解会变成中位数那就是 K-medians 而不是 K-means 了。目标函数的形式直接决定了更新公式长什么样这条因果链在后面 FCM 里会以同样的方式再出现一次。2.3 单调性为什么每次迭代 J 都不会上升两次更新都是对 J 的精确最小化各自在固定另一个变量的前提下完成所以整轮迭代下来 J 单调不增。再补一句J 有下界 0而划分方案总数虽然是个天文数字K^N 量级但终究有限因此不可能无限次严格下降必然在有限步内停在某个 J 不再变化的点上。这段推理是 K-means 收敛性的全部骨架没有任何额外假设。但有一个极其容易被误读的地方收敛不等于收敛到全局最优。坐标下降只会落到某个局部极小落在哪个坑里完全由初始质心决定。教科书里最经典的构造是三个真实簇而两个初始质心偏偏落在同一个真簇内部那么第三个真簇就永远分不到质心——它只能从已有的两个质心里抢一部分样本过来最后的结果无论迭代多久都回不去。实际运行时这个现象表现为“J 一直在某个值附近不动但肉眼看图明显分错了”。这不是实现 bug是算法性质。2.4 初始化K-means 到底在改什么最朴素的初始化是从样本里随机抽 K 个点当初始质心。期望上这会把多个质心浪费在同一个密集区域直接加剧上面说的局部极小问题。K-means 的思路是让初始质心彼此尽量远第一个质心随机选之后每个新质心按“与已选质心最近距离的平方”成正比的概率采样。那个平方不是随手写的它让远处点被选中的概率显著高于均匀采样同时保留随机性避免退化概率上能给出 O(log K) 的近似保证。工程上还有一层更实用的保险跑多次独立初始化取 J 最小的那一次。MATLAB 的 kmeans 里对应 Replicates 参数默认值很小实际用的时候按数据规模调到 10 到 50 之间往往能明显压低结果方差。这个参数的作用是缓解初始化敏感性不是提高精度——它让“这次运气不好”的概率指数级下降但依然不保证全局最优。2.5 一个可以逐行对照的实现下面这段实现把分配步、更新步、空簇处理和终止条件都摊开写方便对照上面的推导。function [idx, C, Jhist] myKmeans(X, K, maxIter, tol) % X: N x D每行一个样本K: 簇数 % idx: N x 1 簇标号C: K x D 质心Jhist: 每轮目标函数值 [N, D] size(X); % ---- K-means 初始化 ---- C zeros(K, D); C(1, :) X(randi(N), :); d2 sum((X - C(1, :)).^2, 2); % 每个点到最近已选质心的平方距离 for k 2:K if sum(d2) 0 % 所有点重合的极端情况 C(k, :) X(randi(N), :); else p d2 / sum(d2); % 按 D^2 采样 C(k, :) X(find(rand cumsum(p), 1), :); end d2 min(d2, sum((X - C(k, :)).^2, 2)); % 只保留到最近质心的距离 end Jhist zeros(maxIter, 1); for it 1:maxIter % 分配步E 步 Dist pdist2(X, C).^2; % N x K 的平方距离矩阵 [dmin, idx] min(Dist, [], 2); % 每个样本取最近质心 % 更新步M 步 Cnew C; for k 1:K members (idx k); if any(members) % 空簇时保留旧质心避免 NaN Cnew(k, :) mean(X(members, :), 1); end end Jhist(it) sum(dmin); % 当前划分的平方误差和 if max(abs(Cnew(:) - C(:))) tol % 质心位移小于阈值即停 C Cnew; break; end C Cnew; end几个参数和细节值得单独说。pdist2(X, C) 返回 N×K 矩阵第 n 行第 k 列是样本 n 到质心 k 的欧氏距离平方之后正好是目标函数里的那一项如果换成手写双重循环思路一样但慢一个数量级。tol 判的是质心最大坐标位移取 1e-4 到 1e-6 都常见它只影响停机时机不影响最终落点。空簇那段 if any(members) 是必须的否则 mean 返回 NaNNaN 会顺着质心传播到距离矩阵最后整个结果全变 NaN 且不报错——这是 MATLAB 里很典型的一类静默失败。Jhist 除了用来看收敛曲线还有一个实用价值多次重启时直接比较 Jhist 末尾的值选最小的那组不用再跑一遍评估。2.6 什么时候不该用它把前面几条串起来K-means 的边界其实很清楚。第一K 必须事先给它不会自己判断有几个簇。第二它只擅长球状、规模相近、方差接近的簇遇到月牙形、环形或者密度差异大的数据必然翻车因为目标函数里的欧氏距离只能表达“到中心有多远”。第三它对特征尺度极其敏感一个量纲是万元、一个是百分比的字段混在一起距离几乎完全由前者决定所以标准化几乎是必做的前置步骤而不是可选项。这三点不是缺憾是目标函数的直接推论。理解到这一层就不会再试图用调参去救一个本来就不适配的数据形状。3. FCM隶属度为什么要取 m 次幂一次拉格朗日乘子推导3.1 从硬到软把 0/1 松成区间上的连续值K-means 的硬划分有个现实问题处于两个簇边界上的样本被强行判给其中一个误差没有任何缓冲。FCM模糊 C 均值的做法是把 r_nk 换成 u_nk ∈ [0,1]表示样本 n 对第 k 个簇的隶属度约束从“恰好属于一个”放宽为“各簇隶属度之和为 1”。目标函数相应变成J_m Σ(n) Σ(k) u_nk^m · || x_n - c_k ||², m 1 约束Σ(k) u_nk 1对所有 n那个 m 叫模糊因子或加权指数。它的存在不是为了好看而是为了让 u 的更新有唯一解——如果写成 u_nk 的一次方对 u 求导得到的是线性方程在约束下最优解会退化成硬划分也就是回到 K-means。指数 m 是让目标函数对 u 严格凸、从而保证内点解的关键。3.2 拉格朗日函数带约束的最小化怎么落地每个样本的隶属度要满足和为 1这是 N 个独立等式约束。用拉格朗日乘子法为每个样本引入一个 λ_nL Σ(n) Σ(k) u_nk^m d_nk² - Σ(n) λ_n ( Σ(k) u_nk - 1 )其中 d_nk² ||x_n - c_k||² 是简写。注意这里只对 u 加了约束对 c 没有约束所以 c 仍然可以直接求偏导。这种“一半带约束、一半不带”的结构是 FCM 推导里最容易被写乱的地方。3.3 求偏导隶属度和质心的更新公式怎么来的先对 u_nk 求偏导并令其为零∂L/∂u_nk m · u_nk^(m-1) · d_nk² - λ_n 0 u_nk ( λ_n / (m · d_nk²) )^(1/(m-1))把这个表达式代回约束 Σ(k) u_nk 1可以解出乘子( λ_n / m )^(1/(m-1)) · Σ(k) d_nk^(-2/(m-1)) 1于是u_nk 1 / Σ(j) ( d_nk / d_jk )^(2/(m-1))这个形式很有信息量。它说明隶属度只取决于样本到各质心距离的相对比值而不是绝对距离——把所有距离同比例放大隶属度不变。这也解释了为什么 FCM 对特征的整体缩放不敏感但对各维度的相对尺度依然敏感。再对 c_k 求偏导∂L/∂c_k -2 Σ(n) u_nk^m (x_n - c_k) 0解得c_k Σ(n) u_nk^m · x_n / Σ(n) u_nk^m这就是加权均值权重是隶属度的 m 次幂。和 K-means 的均值公式相比只多了这个权重——K-means 相当于每个样本权重非 0 即 1。3.4 m 的取值它到底在调节什么从 u_nk 的公式能直接读出 m 的两个极限行为。当 m → 1 时指数 2/(m-1) → ∞距离稍大的那一项比值被放大到无穷隶属度退化为 1 给最近质心、0 给其余完全回到 K-means。当 m → ∞ 时指数趋于 0所有比值的 0 次幂都是 1u_nk → 1/K隶属度完全均匀聚类信息全部丢失。所以 m 是在“硬”和“糊”之间滑动的一根轴。工程上常用 1.5 到 2.5m 2 是最常见的默认值因为此时指数 2/(m-1) 2公式最简洁。m 偏小的结果是边界清晰但接近硬划分m 偏大的结果是隶属度普遍接近 1/K看起来“每个簇都沾一点”实际已经失去判别力。判断 m 是否取大了有个很直接的办法看隶属度矩阵的行最大值分布。如果大量样本的行最大值都落在 0.3 到 0.4 区间说明模糊得过头了正常情况应该呈现明显的双峰——大部分样本行最大值接近 1少部分边界样本在 0.5 附近。3.5 实现矩阵化写法和除零保护function [U, C, Jhist] myFCM(X, K, m, maxIter, tol) % X: N x DK: 簇数m: 模糊因子常用 2 [N, ~] size(X); % 初始化隶属度随机后按行归一化保证 Σ_k u_nk 1 U rand(N, K); U U ./ sum(U, 2); Jhist zeros(maxIter, 1); for it 1:maxIter Um U .^ m; % 质心以 u^m 为权重的加权均值K x D C (Um * X) ./ sum(Um, 1); % 平方距离矩阵 N x K D2 pdist2(X, C).^2; D2(D2 1e-12) 1e-12; % 防止样本与质心重合导致除零 % 隶属度更新比值形式的直接实现 ratio D2 .^ (-1 / (m - 1)); Unew ratio ./ sum(ratio, 2); % 按行归一化 Jhist(it) sum(sum((Unew .^ m) .* D2)); if max(abs(Unew(:) - U(:))) tol % 用隶属度变化判收敛 U Unew; break; end U Unew; end代码里有几处和公式的对应关系需要点明。sum(Um, 1) 得到 K×1 的权重和MATLAB 会自动沿列方向广播到 K×D 的分子上这一步省掉了显式循环。D2 .^ (-1/(m-1)) 就是 (1/d²)^(1/(m-1))与推导里的比值形式等价因为分子上的常数在按行归一化时会被约掉。收敛判据用的是隶属度矩阵的最大变化量而不是质心位移原因是 FCM 的质心可能变化很小但隶属度仍在缓慢重分布用 U 判更贴近实际收敛状态。那句 D2(D2 1e-12) 1e-12 不是防御性编程的客套话。当某个样本恰好等于某个质心时d 0取负幂直接得 Inf接着按行归一化会出现 Inf/Inf NaNNaN 再进入下一轮质心计算整个矩阵就废了。而这种情况在真实数据里有重复样本时非常常见。3.6 FCM 给出的到底是什么很多使用场景把 FCM 的输出直接 argmax 成硬标签然后拿去和 K-means 比准确率这其实浪费了它最大的价值。FCM 真正有用的输出是隶属度矩阵本身它可以告诉你某个样本有多“犹豫”这在医学影像分割、遥感地物边界、软测量这类需要量化不确定性的场景里才是关键。把它当 K-means 用除了多花几倍计算量换不来任何东西。另外值得一提的是FCM 的目标函数依然基于欧氏距离所以 K-means 的球形假设它一个都没逃掉只是边界变软了。数据形状本身不适合球状簇时换 FCM 并不会救回来。4. 不靠质心的两条路SOM 的邻域保序与层次聚类的合并准则4.1 SOM为什么胜者周围的神经元也会被拽过去K-means 和 FCM 都在优化“样本到质心”的距离质心之间没有任何关系簇的编号可以任意置换。神经网络这一支的代表 SOM自组织映射引入了完全不同的机制神经元被固定排列在一个低维网格上网格上的相邻关系本身就是一种约束。每个神经元持有一个和样本同维的权值向量 w_i。训练时对每个输入样本 x 做两件事。第一是竞争找出权值离 x 最近的神经元称为获胜神经元 c这一步和 K-means 的分配步一模一样。第二是合作不只是更新获胜者还要按距离衰减的方式更新它网格上的邻居。更新式是w_i(t1) w_i(t) η(t) · h_ci(t) · ( x - w_i(t) ) h_ci(t) exp( - || r_c - r_i ||² / ( 2σ(t)² ) )其中 r_i 是神经元在网格上的坐标η 是学习率σ 是邻域半径。h_ci 是高斯型邻域函数获胜者自己 h 1隔得越远衰减越快。这个“邻居一起拽”的动作就是 SOM 和 K-means 的全部差别也是它拓扑保序能力的来源。因为相邻神经元被同一批样本反复拉近训练完成后网格上相邻的神经元在数据空间里也倾向于相近高维数据被铺到了一张可以肉眼看的二维图上。这是 K-means 做不到的——它的簇之间没有邻接概念。4.2 衰减时间表η 和 σ 为什么必须随迭代缩小如果 η 和 σ 固定不变网络会一直处于剧烈调整状态永远停不下来最后权值在几个解之间来回振荡。所以两者都必须随迭代衰减。σ 的衰减尤其关键。训练初期 σ 接近网格尺寸获胜者的更新会波及大半个网格这相当于全局粗调让整个映射先大致铺开。随着 σ 缩小更新范围收缩到局部进入精调阶段。这个从粗到细的过程如果顺序反了——一开始就用小 σ——结果是各个神经元各自为政网格上的邻接关系完全丢失输出的拓扑图就是一张没有结构的散点。η 的衰减相对宽松但同样不能省。常见做法是指数衰减 η(t) η_0 · exp(-t/τ)τ 取总迭代数的四分之一左右让学习率在前四分之一就降到初值的 37%之后缓慢收尾。4.3 实现把竞争与合作分开写function [W, label] mySOM(X, gridSize, maxEpoch) % X: N x DgridSize: 网格边长神经元总数 gridSize^2 [N, D] size(X); % 神经元网格坐标用于计算邻域距离 [gx, gy] meshgrid(1:gridSize, 1:gridSize); gridPos [gx(:), gy(:)]; nNeuron size(gridPos, 1); % 权值初始化在数据均值附近的小邻域内比纯随机收敛快得多 W randn(nNeuron, D) * 0.01 mean(X, 1); sigma0 gridSize / 2; % 初始邻域半径约覆盖半个网格 eta0 0.5; tau maxEpoch / 4; % 衰减时间常数 for ep 1:maxEpoch eta eta0 * exp(-ep / tau); sigma sigma0 * exp(-ep / tau); for n randperm(N) % 每个 epoch 内打乱样本顺序 x X(n, :); d2 sum((W - x).^2, 2); [~, c] min(d2); % 竞争找获胜神经元 dgrid2 sum((gridPos - gridPos(c, :)).^2, 2); h exp(-dgrid2 / (2 * sigma^2));% 合作高斯邻域权重 W W (eta * h) .* (x - W); % 广播到所有神经元 end end % 训练结束后按最近权值给样本打标 [~, label] min(pdist2(X, W), [], 2);代码里的广播是理解重点。(x - W) 是 nNeuron×D 的矩阵etah 是 nNeuron×1 的列向量MATLAB 自动沿列方向扩展等价于对每个神经元 i 执行 w_i etah_i*(x - w_i)。写成一个矩阵表达式而不是循环除了快之外也让“所有神经元同时被更新”这件事在代码层面显式可见。randperm(N) 那层循环容易被忽略但它有实际作用。如果不打乱、按固定顺序遍历样本权值会带有样本顺序的偏置尤其在数据本身按类别排列时最后一类的样本会把映射拉偏。打乱相当于给随机梯度下降加噪声是标准做法。训练完的 label 是把每个样本分配给最近的神经元再按需要把神经元映射到簇。SOM 本身给出的是降维映射聚类是在映射之上再做的一步这一步很多人会混淆——以为 SOM 的输出天然就是簇标号。4.4 层次聚类距离矩阵怎么一步步缩小层次聚类走的是完全不同的路线不预设 K不迭代优化而是先算所有样本两两之间的距离然后反复把最近的两个簇合并直到只剩一个。整个过程记录在一棵二叉树树状图里最后按需要的高度剪枝得到任意簇数。关键在于合并之后新簇到其他簇的距离怎么算。暴力做法是重新遍历两个簇的所有样本对复杂度立刻上去。Lance-Williams 公式给出了统一递推d(i∪j, k) α_i·d(i,k) α_j·d(j,k) β·d(i,j) γ·| d(i,k) - d(j,k) |不同连接准则只是系数不同。single linkage 取 α 0.5、β 0、γ -0.5等价于两簇间最近点距离complete linkage 把 γ 取 0.5变成最远点距离average linkage 取 γ 0是平均距离Ward 准则的系数稍复杂目标是最小化合并后簇内平方和的增量倾向产生大小均衡的球状簇。这个公式的意义在于无论用哪种准则每合并一次只需要更新一行一列距离总复杂度 O(N² log N)暴力版是 O(N³)而且四种准则可以共用一套骨架代码只换系数。4.5 实现pdist 和 linkage 的输入形式% 1. 样本间距离输出是长度为 N(N-1)/2 的向量不是方阵 Y pdist(X, euclidean); % 2. 凝聚层次聚类Z 是 (N-1) x 3 的合并记录 % 每行 [被合并簇A, 被合并簇B, 合并时的高度] Z linkage(Y, ward); % 3. 按目标簇数剪枝 K 3; idx cluster(Z, maxclust, K); % 4. 共表型相关系数树对原始距离的保真度越接近 1 越好 c cophenet(Z, Y);pdist 返回向量而不是方阵这是 MATLAB 里最容易出问题的地方后面避坑一节会专门说。cophenet 那个值值得养成看的习惯它衡量树状图里的高度和原始距离之间的一致性低于 0.7 通常意味着连接准则选得和数据形状不匹配换一种准则再试比继续调 K 更有意义。层次聚类最大的优势是那个树状图本身就是结论不需要事先决定 K可以看完整棵树再决定在哪剪。代价是它没有“重新分配”的机会——一旦两个簇合并后面就不可能再拆开早期的一次错误合并没有任何后悔药可吃。5. 四种算法的踩坑排查清单空簇、方向、模糊因子与归一化5.1 现象每次运行结果都不一样有时差别还很大原因基本可以锁定在初始化。K-means 和 FCM 的初始质心或初始隶属度都是随机的而目标函数又只有局部最优保证不同的起点会落到不同的坑里。这不是代码不稳定是算法性质。解决方式是显式控制随机性并多次重启。MATLAB 里给 kmeans 传 Replicates, 20 和 Start, plus取多次里 J 最小的那组需要复现实验时在脚本开头固定随机种子。但要注意多次重启降低的是方差不是偏差——如果数据形状本身不适合球状簇跑一百次也还是错的。5.2 现象结果矩阵里出现 NaN或者整个簇标号全是 1先查空簇。当 K 给得比实际结构还大或者某次迭代中一个簇没能抢到任何样本更新步里对空集合求均值就得到 NaNNaN 顺着质心传播下一轮距离全变 NaNmin 的行为随之异常。上面 K-means 实现里那句 if any(members) 就是拦这个的。再查除零。FCM 里样本和质心完全重合时 d 0负幂运算得 Inf按行归一化出 NaN。真实数据有重复行时这几乎是必然触发的必须加下限保护。5.3 现象FCM 的隶属度矩阵看起来“每个簇都差不多”打印一下每行的最大值分布。如果大量样本的行最大值集中在 1/K 附近说明 m 取太大了模糊得过头隶属度已经不含判别信息。把 m 往 1.5 降重新看分布是否呈现双峰。反过来如果几乎所有样本的行最大值都接近 1说明 m 太接近 1已经退化成硬划分这时候用 FCM 就没有意义了不如直接用 K-means 省算力。m 的合理区间要靠这个分布来定而不是照抄别人论文里的值。5.4 现象SOM 训练完的映射图一团糊看不出任何结构第一个要查的是归一化。SOM 用的是欧氏距离某一维量纲远大于其他维时竞争结果几乎完全由那一维决定网格根本铺不开。所有特征先做 z-score 或归一到 [0,1]这一步不是可选项。第二个要查的是衰减时间表。如果 σ 一开始就设得很小比如直接等于 1各神经元独立更新邻域合作机制形同虚设输出的拓扑结构必然是散的。正确顺序是大 σ 起步、随迭代指数收缩。第三个要查迭代轮数几百轮对于稍有规模的数据通常不够权值还没稳定就停了。5.5 现象linkage 跑完不报错但树状图明显不合理先确认传给 linkage 的是 pdist 返回的距离向量而不是 pdist2 返回的 N×N 方阵。这两个函数名只差一个字符但输入形式完全不同传错了 MATLAB 不会报错它会把这个方阵当成一个长度 N² 的距离向量去解释结果是一棵毫无意义的树。这个坑非常隐蔽因为代码能跑通、有输出、只是结论荒谬。再确认有没有把样本矩阵转置错。MATLAB 的聚类函数默认按行是样本、列是特征而从某些语言或文件读进来的数据习惯是列存样本转置一旦漏掉聚类就变成在对特征聚类而不是对样本聚类规模稍大时表现为内存爆掉或者结果完全没有可解释性。提示这五类问题的共同点是都不抛异常。聚类算法的失败几乎全是静默的所以每次跑完都该看一眼目标函数曲线、隶属度分布或者树状图高度而不是只看最终标号。6. 选型时的真实权衡没有哪个准则能通吃把四种算法放在一起看会发现它们的分歧不在实现细节而在对“相似”这件事的定义上。K-means 定义相似为“到同一个中心的距离小”FCM 定义为“到各中心的距离比值决定归属概率”SOM 在距离之外还要求“相邻的神经元映射到相近的位置”层次聚类则完全依赖两两距离和一条合并规则。定义不同能表达的结构就不同这是选型的唯一依据。一个实用的判断顺序是这样。先问 K 知不知道如果业务上簇数固定且样本量大K-means 是性价比最高的起点Replicates 开大一点基本能压住初始化风险。再问边界要不要软如果下游需要的是“这个样本有多大概率属于某类”而不只是一个标号FCM 的隶属度矩阵比硬标签信息量大得多。再问结构要不要可视化如果目标是把高维数据摊到一张二维图上让人看懂SOM 的拓扑保序是划分类算法给不了的。最后问要不要事后改主意如果 K 本身是探索出来的、需要看着树状图决定切在哪层次聚类那个不可回退的合并过程反而是优点因为整棵树完整保留了所有中间状态。代价也要一并算清。K-means 和 FCM 都是 O(N·K·D·迭代数)能撑到百万级样本层次聚类是 O(N²) 的内存和 O(N² log N) 的时间样本上到几万就必须考虑先抽样或者换用其他策略SOM 的训练成本取决于网格大小和迭代轮数通常是四者里最慢的。这些数量级差异不是常数因子规模一变就决定了方案可不可行。验证这一层我一般会做两件事。一是在同一份数据上跑多种算法用轮廓系数或者已知标签下的调整兰德指数横向比看分歧出在哪里——分歧大的样本往往就是真正的边界样本值得单独拿出来看。二是固定随机种子重跑十次看结果的稳定性方差大的配置直接淘汰不管它单次跑出来的指标多好看。血泪经验是曾经为了省事跳过标准化直接跑 K-means结果所有样本被一个量纲特别大的字段主导聚出来的簇和业务完全对不上排查了大半天才定位到一行没写的 normalization。从那以后我每次跑聚类之前都强制走一遍“检查维度方向、检查量纲、检查缺失值、固定种子”这四步一个都不跳。希望帮到你。本文还有配套的精品资源点击获取