news 2026/10/29 14:47:42

MATLAB聚类算法源码包:Kmeans、FCM、层次与神经网络聚类实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MATLAB聚类算法源码包:Kmeans、FCM、层次与神经网络聚类实战

简介:面向机器学习初学者与需要快速上手聚类实验的开发者,这份资源以MATLAB为环境,系统整理了FCM模糊C均值、K-means、Kmedia等经典聚类算法的源代码与配套示例,帮助读者在无监督学习场景下完成数据分类与分析,并理解各算法背后的数学原理与适用边界。压缩包共74个文件、约2.05MB,以40个m脚本为核心实现,辅以gif与png图示、txt说明、htm与css页面及mat数据文件,另附FuzzyClusteringToolbox工具箱与PDF文档,涵盖数据标准化、聚类计算、结果可视化与轮廓系数等性能评估环节。已有4888人学习下载,读者可对照示例逐行理解初始化、隶属度更新与收敛判断流程,比较FCM对模糊边界、K-means对凸形均匀类别的差异,并借助工具箱简化调试、提升实验效率。

1. 四种聚类算法源码包:拿到手就能跑的 MATLAB 聚类实验底稿

做聚类实验最耗时间的往往不是想算法,而是把 Kmeans、FCM、层次聚类、神经网络聚类这四套东西凑到一个能对比的框架里。网上下载的单文件脚本,输入格式各不一样,画出来的图风格也不统一,跑完一组参数想换数据重跑,还得改半天路径。这个源码包解决的正是这件事:四种聚类算法各有一份独立实现,配一份示例脚本,数据加载、参数设置、结果可视化都按同一套接口组织,换数据集只需要改一个变量。适合正在做模式识别课程设计、写论文对比实验,或者想拿 MATLAB 快速验证聚类效果再决定要不要上 Python 重写的开发者。MATLAB 的矩阵运算写 FCM 的隶属度更新天然顺手,这也是这类实验代码长期留在 MATLAB 里的原因。

2. 项目结构与技术选型:四套算法怎么组织在一个目录里

2.1 目录布局与文件职责

拿到压缩包先别急着点运行,花两分钟看清目录,后面改参数能省很多来回。这类聚类源码包通常按「算法实现 + 示例脚本 + 数据」三层组织,结构大致如下:

clustering-demo/ ├── algorithms/ │ ├── kmeans_core.m % 标准 Kmeans,含空簇处理 │ ├── fcm_core.m % 模糊 C 均值,输出隶属度矩阵 │ ├── hierarchical_core.m % 层次聚类,返回合并树 │ └── nn_cluster_core.m % 神经网络聚类(竞争学习) ├── demo/ │ ├── run_all.m % 一键跑四种算法并对比 │ ├── demo_kmeans.m % 单算法示例 │ └── demo_fcm.m ├── data/ │ ├── iris.mat % 常用小数据集 │ └── synthetic_2d.mat % 二维合成数据,方便看聚类边界 └── utils/ ├── normalize_data.m % 归一化 └── plot_clusters.m % 统一可视化

algorithms/下每个文件只做一件事:输入数据矩阵和参数,输出标签或隶属度。demo/负责调用和展示,utils/放公共函数。这种拆法的好处是,你想把 Kmeans 换成自己写的版本,只动一个文件,示例脚本不用碰。常见做法是每个 core 函数第一行做输入校验,维度不对直接报错,避免跑到一半才在矩阵乘法那里崩掉。

2.2 四种算法的定位差异

选型上要清楚这四者不是平级替换关系。Kmeans 是硬划分,速度快,对球形簇效果好,但对初始中心敏感;FCM 是软划分,每个样本对每个簇有隶属度,适合边界模糊的数据,代价是要设模糊指数 m 并迭代隶属度;层次聚类不需要预设簇数,输出树状图,适合样本量不大、想观察合并过程的场景;神经网络聚类这里一般指竞争学习类方法,用权重向量逼近簇中心,学习率衰减策略会影响收敛。示例脚本里通常用同一份二维数据跑四种,就是为了让你直观看到硬划分和软划分在边界上的差别。

2.3 运行环境与依赖

MATLAB 版本上没有太苛刻的要求,R2016b 之后基本都能跑,因为代码主要用基础矩阵运算,不依赖深度学习工具箱。唯一要注意的是如果示例里用了pdist、linkage这类统计工具箱函数,需要确认本机装了 Statistics and Machine Learning Toolbox。没有的话层次聚类部分会报未定义函数,解决办法要么装工具箱,要么用自写的距离矩阵计算替代。运行前把当前文件夹切到项目根目录,再执行addpath(genpath(pwd)),把子目录都加进搜索路径,否则 demo 里调algorithms/下的函数会找不到。

% 在项目根目录执行,把全部子目录加入路径 addpath(genpath(pwd)); % 确认路径生效,能列出算法文件 which kmeans_core

genpath会递归包含所有子文件夹,which返回函数所在路径说明加载成功。这一步看着琐碎,但很多人第一次跑报「未定义函数」就是忘了加路径,血泪经验是把它写进startup.m,以后打开 MATLAB 自动执行。

3. 核心算法拆解:从 Kmeans 到 FCM 的逐段实现

3.1 Kmeans 主循环与空簇处理

Kmeans 的核心就三步:分配样本到最近中心、更新中心、判断收敛。真正容易翻车的是空簇——某个中心一个样本都没分到,直接求均值会得到 NaN。合格实现里都会处理这种情况,常见做法是把空簇中心重置到离现有中心最远的样本点上。

function [labels, centers, iter] = kmeans_core(X, k, maxIter, tol) % X: n x d 样本矩阵;k: 簇数;maxIter: 最大迭代;tol: 中心变化阈值 [n, d] = size(X); % 随机选 k 个不同样本作为初始中心 idx = randperm(n, k); centers = X(idx, :); labels = zeros(n, 1); for iter = 1:maxIter % 计算每个样本到各中心的欧氏距离平方 dist = zeros(n, k); for j = 1:k diff = X - centers(j, :); dist(:, j) = sum(diff.^2, 2); end [~, newLabels] = min(dist, [], 2); % 空簇处理:把空簇中心挪到最远样本 for j = 1:k if ~any(newLabels == j) [~, farIdx] = max(min(dist, [], 2)); centers(j, :) = X(farIdx, :); end end % 更新中心 newCenters = centers; for j = 1:k members = X(newLabels == j, :); if ~isempty(members) newCenters(j, :) = mean(members, 1); end end shift = max(sqrt(sum((newCenters - centers).^2, 2))); centers = newCenters; labels = newLabels; if shift < tol break; end end end

逐段看:randperm保证初始中心不重复,比纯随机取点稳;距离用平方欧氏,省一次开方,比较大小等价;空簇那段先算每个样本到最近中心的距离,取最大者作为新中心,避免 NaN;收敛判据用中心最大位移而不是标签变化,因为标签在边界样本上可能来回跳,中心位移更平滑。参数上tol一般设 1e-4 到 1e-6,maxIter给 100 到 300 足够,k 超过样本数十分之一时初始化和空簇问题会明显变多。

3.2 FCM 隶属度更新与目标函数

FCM 比 Kmeans 多一层隶属度矩阵 U,大小是 n×k,每行和为 1。迭代公式是固定的:先算隶属度,再更新中心,最后算目标函数判断收敛。模糊指数 m 控制划分的模糊程度,m 越接近 1 越像硬划分,常用 1.5 到 2.5。

function [U, centers, obj] = fcm_core(X, k, m, maxIter, tol) % m: 模糊指数,通常取 2;U: n x k 隶属度矩阵 [n, ~] = size(X); U = rand(n, k); U = U ./ sum(U, 2); % 行归一化 objHist = zeros(maxIter, 1); for iter = 1:maxIter % 更新中心:按隶属度的 m 次方加权 Um = U .^ m; centers = (Um' * X) ./ sum(Um, 1)'; % 更新隶属度 dist = zeros(n, k); for j = 1:k diff = X - centers(j, :); dist(:, j) = sum(diff.^2, 2); end dist = max(dist, 1e-10); % 防止除零 ratio = dist .^ (-1/(m-1)); U = ratio ./ sum(ratio, 2); % 目标函数 objHist(iter) = sum(sum(Um .* dist)); if iter > 1 && abs(objHist(iter) - objHist(iter-1)) < tol break; end end obj = objHist(1:iter); end

中心更新那行(Um' * X) ./ sum(Um, 1)'是矩阵化写法,比循环快很多,注意转置方向别搞反。隶属度更新里dist加了下限 1e-10,否则样本恰好落在中心上会除零得到 Inf。目标函数是隶属度加权距离和,单调不增,用它判断收敛比看 U 的变化更可靠。m 取太小会退化成 Kmeans 且数值不稳,取太大所有样本隶属度趋近 1/k,划分失去意义,这是 FCM 最常见的参数坑。

3.3 层次聚类与神经网络聚类的接口统一

层次聚类一般直接调linkage得到合并树,再用cluster按簇数切分;神经网络聚类用竞争学习,权重向量按学习率向胜出样本靠拢。为了让四种算法在 demo 里能统一调用,建议每个 core 函数都返回labels作为第一输出,FCM 额外返回 U。这样run_all.m里可以写一个循环,把四个函数句柄放进 cell 数组依次执行,结果存进结构体再统一画图。接口统一这件事看着不起眼,但做对比实验时能省掉大量手工对齐标签的功夫,尤其是写论文要出对比表格的时候。

4. 跑通示例:数据准备、参数设置与结果对比

4.1 数据加载与归一化

示例数据一般是.mat文件,用load读进来后先看变量名和维度。聚类对量纲敏感,欧氏距离下数值大的特征会主导结果,所以跑之前统一做归一化。utils/normalize_data.m通常做 z-score 或 min-max,二选一即可,关键是四种算法用同一份归一化后的数据,否则对比没有意义。

load('data/synthetic_2d.mat'); % 假设变量名为 X X = normalize_data(X); % z-score 归一化 k = 3; % 预设簇数

normalize_data内部按列减均值除标准差,返回同尺寸矩阵。如果数据里有常数特征,标准差为 0 会出 NaN,稳妥写法是加一个极小值保护。二维合成数据适合入门,因为能直接画散点看边界;换成 iris 这类四维数据时,可视化要降维或者只画前两维,别指望一张图看全。

4.2 一键对比脚本的写法

run_all.m的价值在于把参数集中在一处,改一次全部生效。典型写法是用结构体存参数,四个算法各跑一遍,记录耗时和聚类指标。

params.k = 3; params.maxIter = 200; params.tol = 1e-5; params.m = 2; % FCM 模糊指数 results = struct(); tic; [results.kmeans.labels, results.kmeans.centers] = ... kmeans_core(X, params.k, params.maxIter, params.tol); results.kmeans.time = toc; tic; [results.fcm.U, results.fcm.centers] = ... fcm_core(X, params.k, params.m, params.maxIter, params.tol); results.fcm.time = toc; % 层次聚类与神经网络聚类同理,统一存进 results

tic/toc记录耗时方便对比效率,结构体分层存结果便于后面取用。注意 FCM 返回的是隶属度,画硬边界时要先[~, labels] = max(U, [], 2)转成标签,这一步漏了会导致画图函数报维度不匹配。参数集中管理还有个好处:做敏感性分析时只改params.m或params.k,外面套一层循环就能批量跑。

4.3 结果可视化与指标计算

plot_clusters.m一般接收数据、标签、中心三个参数,散点按标签着色,中心用大标记标出。二维数据直接画,高维数据要么选两维要么先做 PCA。除了看图,建议顺手算轮廓系数或簇内平方和,光看图容易自我说服,指标能兜底。

算法主要输出关键参数适用场景
Kmeans标签、中心k、maxIter、tol球形簇、大数据量
FCM隶属度、中心k、m、maxIter边界模糊、软划分需求
层次聚类合并树、标签距离度量、链接方式小样本、想看合并过程
神经网络聚类标签、权重学习率、迭代次数在线学习、原型逼近

表格里参数只是常用起点,实际调参要看数据分布。轮廓系数用silhouette函数算,输入数据和标签即可,值越接近 1 聚类越紧凑。注意层次聚类的标签编号和 Kmeans 不一定对应,对比时别直接按编号匹配,要用指标而不是标签相等来判断。

5. 避坑与排查:跑聚类代码时最容易翻车的几处

5.1 报「未定义函数或变量」

现象是运行 demo 立刻报错,提示某个 core 函数找不到。原因基本是当前路径不在项目根目录,或者没执行addpath。解决方法是切到根目录跑addpath(genpath(pwd)),再用which确认。如果which能找到但运行仍报错,检查函数文件名和函数名是否一致,MATLAB 以文件名为准,名字对不上会调不到。

5.2 聚类结果每次都不一样

Kmeans 和 FCM 都有随机初始化,不设随机种子每次结果不同,做对比实验时会被误认为算法不稳定。解决是在脚本开头固定种子rng(42),让初始化可复现。但要注意固定种子只是让本次可复现,不代表这组初始化就是最优的,正式实验建议多次运行取指标平均,而不是拿一次结果下结论。

5.3 FCM 隶属度出现 NaN 或全相等

现象是 U 矩阵里有 NaN,或者每行隶属度都接近 1/k。前者多半是某样本到中心距离为 0 导致除零,检查dist有没有加下限保护;后者是模糊指数 m 设得过大,划分被过度平滑。解决是把 m 调回 1.5 到 2.5 区间,并确认距离计算没有溢出。数据量纲差异大时也会加剧这个问题,归一化别省。

5.4 空簇导致中心为 NaN

Kmeans 里某个簇没分到样本,直接mean空矩阵返回 NaN,后续距离计算全崩。现象是中心矩阵出现 NaN 且迭代不收敛。解决是按 3.1 里的空簇处理逻辑,把该中心重置到最远样本,或者干脆减少 k 值。k 设得接近样本数时这个问题高发,先确认簇数是否合理再调代码。

5.5 层次聚类内存或耗时爆炸

样本量上千时,pdist生成的距离矩阵是 n×n,内存占用按平方增长,几千个样本就可能卡死。现象是运行很久没反应或者报内存不足。解决是控制样本量,或者改用不预先算全距离矩阵的实现;做演示时用几百个样本足够看出树状图结构,没必要拿全量数据硬跑。

6. 二次开发:把示例代码改成自己的实验框架

拿到这套代码后,最值得做的改造不是重写算法,而是把「参数—运行—评估」这条链路固化下来,让它能批量出结果。我一般会新建一个experiment.m,外层用循环遍历簇数 k 和模糊指数 m,内层对每个参数组合多次运行取平均,结果写进表格。这样一次跑完就能得到 k 值选择曲线,比手工改参数点运行高效得多。

kList = 2:6; mList = [1.5, 2.0, 2.5]; records = []; for k = kList for m = mList scores = zeros(5, 1); for rep = 1:5 rng(rep); % 每次重复固定不同种子 [U, ~] = fcm_core(X, k, m, 200, 1e-5); [~, lab] = max(U, [], 2); scores(rep) = mean(silhouette(X, lab)); end records = [records; k, m, mean(scores)]; end end

这段的关键点是重复运行取均值,单次结果受初始化影响太大,直接拿来选参数容易过拟合到某次随机。rng(rep)让每次重复的种子不同但可复现,silhouette作为评估指标和算法本身解耦,换算法也能用。改造时还有两个地方值得注意:一是把数据加载和归一化抽成独立函数,换数据集只改一处;二是给每个 core 函数加输入维度校验,维度不对立刻报错,别等到矩阵乘法才崩。验证改造是否成功,最简单的办法是拿同一份数据跑改造前后,确认指标在随机波动范围内一致,再去看批量结果。

从那以后我每次改聚类实验代码,都强制先用小样本跑一遍全流程再上全量数据,这个习惯帮我省掉过好几次跑到一半才发现标签维度对不上的返工。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/29 14:47:21

Ponytail:轻量级AI Agent开发范式(JS+FastAPI+CLI)

1. 项目概述&#xff1a;Ponytail 是什么&#xff0c;它解决的不是“CLI 工具”这个表象问题Ponytail 这个名字乍一听像发型、像马尾辫&#xff0c;但在当前 AI 工具链快速演进的语境下&#xff0c;它已悄然成为一批开发者私下交流时频繁提及的轻量级 agent 开发支撑层代号。它…

作者头像 李华
网站建设 2026/10/29 14:46:23

Zola多语言站点未翻译内容:文件名约定、语言分组与构建自检

多语言站点最耗神的环节&#xff0c;往往不是写内容&#xff0c;而是盯住“哪一份还没写”。Zola 多语言站点里的未翻译内容&#xff0c;说到底是两件事叠在一起的结果&#xff1a;一是文件名约定决定了内容归属哪种语言&#xff0c;二是语言分组决定了同一篇内容的不同语言版本…

作者头像 李华
网站建设 2026/10/29 14:45:20

ponytail:轻量级CLI Agent框架,支持JS插件与FastAPI服务

1. 项目概述&#xff1a;一个轻量级、可插拔的 CLI Agent 框架设计初衷“ponytail”这个名字乍一听像某种发型&#xff0c;但在开发者社区里&#xff0c;它正悄然成为一类新型工具链的代号——不是图形界面&#xff0c;不是 Web 应用&#xff0c;而是一个以命令行为核心入口、支…

作者头像 李华
网站建设 2026/10/29 14:45:18

浏览器引擎横向对比:架构差异、选型与多内核测试

浏览器引擎这个词&#xff0c;大部分前端同学可能只在面试八股里见过&#xff0c;真正动手碰过的人不多。但只要你做过自动化测试、爬虫渲染、桌面端内嵌网页、跨端应用&#xff0c;或者被某个“只有某个浏览器上才复现”的诡异 Bug 折磨过&#xff0c;你就一定会绕回到这个问题…

作者头像 李华
网站建设 2026/10/29 14:44:22

Hyperframes:轻量级CLI动效工具链,解决网页帧级控制难题

1. 项目概述&#xff1a;Hyperframes 不是“超帧”&#xff0c;而是一套面向现代网页动效交付的轻量级 CLI 工具链 你有没有遇到过这样的场景&#xff1a;设计师甩来一个 3 秒的 Lottie 动画&#xff0c;但你发现它在低端安卓机上卡顿严重&#xff1b;或者用 CSS keyframes …

作者头像 李华