恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

k-means聚类算法MATLAB实现:初始化、迭代与避坑指南

  • 首页
  • 资讯中心
  • /
  • k-means聚类算法MATLAB实现:初始化、迭代与避坑指南

相关资讯

PyCharm本地历史:代码后悔药,无Git也能恢复文件快照 2026/10/11 15:43:00
Excel动态引用终极武器:INDIRECT函数原理、实战与避坑指南 2026/10/11 15:43:00
PyTorch反向传播全解析:从计算图到自动求导实战 2026/10/11 15:38:00

最新资讯

2026拉萨景区古建牌坊检测排名 TOP5 CMA 资质机构提供牌坊裂缝检测、牌坊倾斜检测、老化检测 联系方式推荐
2026年江西省职业院校技能大赛 信息技术应用创新赛项竞赛方案(高职组)
用Imatest量化镜头分辨率:SFR与MTF50测试从入门到避坑
蓝桥云课Lv.1刷题全记录:从基础语法到边界条件提升代码能力
WebView原理与JSBridge通信机制:混合开发性能优化实战指南
Linux字符编码实战:从乱码“锟斤拷”到UTF-8、GBK与locale排查

今日推荐

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

k-means聚类算法MATLAB实现:初始化、迭代与避坑指南

发布时间:2026/10/11 15:43:00
k-means聚类算法MATLAB实现:初始化、迭代与避坑指南 简介《k-means数据聚类实验报告MATLAB详细代码》是一份面向机器学习初学者、高校学生及数据分析人员的实验型资料系统演示k-means这一无监督聚类算法在MATLAB中的落地过程。文档从算法原理和迭代流程入手讲到距离度量、误差平方和准则、初始类中心与k值选取的影响并结合Iris鸢尾花数据集给出k3、k4两组聚类实验及效果分析帮助读者理解聚类结果的差异和收敛条件。下载包仅含1个doc文档压缩后约126KB内容除实验目的、步骤、流程图、结果分析外还附有可直接运行的MATLAB源代码便于对照调试。目前已有2737人学习下载适合作为课程实验报告、期末复习或入门聚类分析的参考资料。1. 从实验报告到可运行代码这份k-means聚类资源解决什么问题在课程设计和数据挖掘入门里“k-means 聚类分析”几乎是必练项目但很多人卡在同一个地方原理背得出来一打开 MATLAB 却不知道矩阵怎么组织、迭代怎么写、图画出来为什么和课件不一样。这份实验报告的核心价值在于它不只有文字推导还附了完整可跑的 MATLAB 主程序和 kmeans 自定义函数以及用ch7 iris.txt内置在代码矩阵中跑出的 k3、k4 两套聚类效果。适合三类人正在做机器学习课设、需要凑齐实验报告和源码的学生想快速在 MATLAB 里验证 k-means 收敛行为、又不想从零写距离计算的从业者以及想搞明白“初始类中心”和“迭代次数”之间关系的好奇者。可以把它当成一份带注释的参考实现而不是只能抄的作业。2. 平方误差准则与欧氏距离先把 k-means 的收敛逻辑立住2.1 聚类问题的数学语言距离度量与准则函数k-means 的本质是“按距离划簇再按均值更新”。给定 n 个对象想分成 k 个簇使得簇内紧凑、簇间疏离。这里“紧凑”的度量是平方误差准则E Σ(p − mi)²其中 p 是数据点mi 是第 i 个簇的平均值也叫质心累加范围覆盖所有点和它们所属的簇。E 越小说明每个点离自己的簇中心越近簇内相似度越高。算法交替做两件事固定中心分配点固定归属更新中心。数学上可以证明这个过程保证 E 单调不增所以一定会收敛到某个局部极小值——这也解释了为什么报告里强调“初始类中心只影响迭代次数对结果影响不大”的说法不完全严谨不同初始值会收敛到不同的局部极小只是在这个数据集上差异不明显。距离度量用的是欧几里得距离。在 MATLAB 实现里为了省去开方运算直接用距离平方判断大小因为开方是单调变换不影响“哪个中心更近”的结论。这个细节在源码里反复出现是理解代码的关键——看到sum((...).^2,2)就应该知道这是在算平方距离而不是标准差或别的统计量。2.2 程序结构主脚本与 kmeans 自定义函数的分工这份实验报告的程序分两层。主脚本负责数据准备、参数设定和可视化自定义函数kmeans(x,k,nc)负责聚类计算。这种拆分是课设里最常见也最合理的组织方式业务逻辑和算法逻辑分离调试时可以单独在命令行测函数不用反复跑绘图代码。主脚本里第一个值得注意的变量是nc初始聚类中心。代码用bn round(n/k*rand)生成一个随机行号然后用x(bn,:)、x(2*bn,:)、x(3*bn,:)拼出三个初始中心。这个做法的用意我在第三章细讲这里先记住一点通过rand引入随机性意味着每次运行结果可能不同这是后面所有“图形对不上”问题的根源。函数返回三个值cid是 1×n 的类别编号向量每个点属于第几类、nr是 1×k 的每类样本数、centers是 k×d 的最终类中心矩阵。主脚本拿到cid后逐个点画散点图用红、蓝、绿三色区分三个簇。函数内部不是一个 while 循环而是两段式迭代第一段用maxgn100的上限做标准 k-means第二段用maxiter2和move标志做“调整式再分配”。很多初学者只看到while iter maxgn就以为算法结束了实际上后半段才是这个实现里最有意思的部分——它尝试在簇大小不平衡时把点重新分配给别的簇代价是牺牲一点距离最优性。理解这两段才算真正读懂了这份代码。function [cid,nr,centers] kmeans(x,k,nc) [n,d] size(x); cid zeros(1,n); oldcid ones(1,n); nr zeros(1,k); maxgn 100; iter 1;oldcid在代码里声明了但没有参与收敛判断实际收敛是靠固定迭代次数实现的。这是源码里一个可以优化的小瑕疵也提醒我们并不是所有“看起来像收敛条件”的变量都在起作用。3. 主程序与 kmeans 函数拆解初始中心、两阶段迭代和绘图分支3.1 初始中心怎么选bn round(n/k*rand)的用意很多人第一次看到bn round(n/k*rand)会困惑为什么随机数要乘n/k再四舍五入直接randi(n)不更简单k 3; [n,d] size(x); bn round(n/k*rand); nc [x(bn,:); x(2*bn,:); x(3*bn,:)];这段代码的意图是“让初始中心尽量分散”。rand生成 (0,1) 均匀随机数乘以n/k这里 n150、k3所以是 50bn的取值基本落在 1 到 50 之间。于是三个初始中心分别取数据矩阵的第 bn 行、第 2bn 行、第 3bn 行——对应整个数据集的前段、中段、后段位置。就算bn随机波动三个中心也不会挤在一起。这是一种“准均匀随机初始化”比纯随机更稳定也比人工指定更省事。参数说明round是四舍五入结果是整数行号bn是单个数而不是向量所以x(bn,:)是取某一行的全部 4 个特征。如果需要 k4代码注释里给了扩展写法nc [x(bn,:);x(2*bn,:);x(3*bn,:);x(4*bn,:)]但要注意此时bn应该用round(n/k*rand)重新计算k 变了随机范围也得跟着变。这是源码里最容易被忽略的联动参数。3.2 第一阶段最多 100 次的均值重算主程序调用kmeans函数后第一阶段循环负责最核心的“分配—更新”while iter maxgn for i 1:n dist sum((repmat(x(i,:),k,1)-nc).^2,2); [m,ind] min(dist); cid(i) ind; end for i 1:k ind find(cidi); nc(i,:) mean(x(ind,:)); nr(i) length(ind); end iter iter 1; end逻辑说明外层for i 1:n遍历每个样本repmat(x(i,:),k,1)把当前点复制成 k 行与 k 个中心逐行相减、平方、按行求和得到该点到 k 个中心的距离平方向量。min找出最小值索引ind当前点就归为第ind类。内层for i 1:k统计每个类里有哪些点用mean重新计算质心nr记录每类点数。maxgn100是硬性的迭代上限——不管是否收敛100 轮后必然退出。这里的dist没有开方因为比较距离平方和比较距离的大小顺序一致。min(dist)返回两个值m是最小值代码里没用ind是索引真正需要的。如果某个类在某一轮变成空类find(cidi)返回空向量mean会得到 NaN后续计算全部崩掉——这是 k-means 实现的经典边界问题Iris 数据上不常见但换成小数据集很容易翻车。3.3 第二阶段move标志与调整后的距离第一阶段结束后代码没有直接返回而是进入一段“微调”maxiter 2; iter 1; move 1; while iter maxiter move ~ 0 move 0; for i 1:n dist sum((repmat(x(i,:),k,1)-nc).^2,2); r cid(i); dadj nr./(nr1).*dist; [m,ind] min(dadj); if ind ~ r cid(i) ind; ic find(cid ind); nc(ind,:) mean(x(ic,:)); move 1; end end iter iter 1; end这段的差异在dadj nr./(nr1).*dist。它把原本的距离乘了一个与簇大小nr相关的系数簇越大nr/(nr1)越接近 1簇越小这个系数越小于是小簇对点的“吸引力”被放大。这是一种带倾向性的再分配——不是纯粹找最近中心而是同时惩罚大簇继续膨胀。move标志记录这一轮是否有任何点被重新分配如果一轮下来所有点都满意循环提前结束。但注意maxiter2且iter从 1 开始while iter maxiter实际最多执行一轮调整。也就是说这个“微调”只做一次尝试不管是否还有可移动的点第二轮就退出。源码最后用move判断并打印No points were moved这行输出只能说明“最后一次尝试没动”不能说明整个聚类已达到稳定。了解这个边界才不会误读实验报告里的收敛性结论。3.4 绘图分支把 cid 映射到颜色for i 1:150 if cid(i) 1 plot(x(i,1),x(i,2),r*) hold on else if cid(i) 2 plot(x(i,1),x(i,2),b*) hold on else if cid(i) 3 plot(x(i,1),x(i,2),g*) hold on end end end end这段是典型的三层嵌套if-else取每个样本的前两列x(:,1) 和 x(:,2)画散点红、蓝、绿分别代表第 1、2、3 类。绘图只用了前两个特征第三、四列特征没有进入可视化——这是 Iris 数据常见的展示方式取花萼长宽二维投影直观但会丢失花瓣维度的信息。嵌套结构在功能上没问题但可读性一般自己改造时可以写成switch或直接把颜色数组映射成colors(cid(i),:)还能省掉三层缩进。有个小坑藏在绘图末尾text(-4,-3.6,strt)把图例文字写在了坐标 (-4,-3.6) 的位置而 Iris 数据特征值范围全在 0~1 之间文字会落在图形区域外打开图看不到说明。需要手动把坐标改成text(0,1.1,strt)这类图内位置。这种“坐标越界”问题在这份报告里不是笔误而是早期 MATLAB 绘图习惯——坐标范围不确定时先放远处最后用axis统一约束。实际跑代码时建议直接删掉这行或调整坐标。4. 在 Iris 数据上换 k 值k3 与 k4 的聚类边界和实验结论4.1 ch7 iris.txt 的数据组织方式实验用的ch7 iris.txt是经典 Iris 鸢尾花数据集150 个样本、4 个特征花萼长宽、花瓣长宽分属 3 个真实类别。在代码里数据没有走load(ch7 iris.txt)的文件读取流程而是以矩阵字面量直接嵌进了主脚本的x变量里保存成 150×4 的 MATLAB 矩阵。每一行是一个样本前两列是花萼特征、后两列是花瓣特征顺序和原始 txt 文件一致。x [ 0.224 0.624 0.067 0.043 0.749 0.502 0.627 0.541 ... ];真实复现时如果想把数据从代码里拆出来单独维护用load(ch7 iris.txt)或readmatrix(ch7 iris.txt)就能读成同样尺寸的矩阵。注意数据已经做过归一化所有值都在 0 到 1 之间所以距离计算不会出现某个特征数值过大主导欧氏距离的情况——这是实验能稳定跑出漂亮图形的隐藏前提后面避坑章会展开说。4.2 k3 与 k4 的聚类结果对比报告里给了两个 k 值的结论k3 时三个簇与鸢尾花的三个真实类别大体对应分类效果较好k4 时出现“过度分割”某个真实类别被拆散部分簇样本过于稀疏。在二维投影图上这种差异非常直观k3 的三个色块边界清晰山鸢尾和维吉尼亚鸢尾的样本区分离k4 时其中一个簇会被拦腰切两段而原本应当合并的两个小簇反而因为中心距离近而融在一起。实验观察到k 值继续增大到 5、6 时平方误差 E 会继续下降但新的簇不再对应任何有意义的类别边界。簇越切越碎每个中心都被“拽”向局部密度中心这在无监督场景里是典型的 k 值过拟合。判断 k 是否合适不能只看误差是否变小要看聚类结果能否对应业务上的可解释分组——这也是实验报告里“对类别数选择有较高要求”这句话的实际含义。4.3 从平方误差看收敛性与迭代次数报告总结出两个经验初始类中心不同对聚类结果影响不大但对迭代次数影响明显数据输入顺序同样只影响迭代次数。结合代码验证这个结论是对的但有个前提——bn round(n/k*rand)这种初始化方式天然保证了初始中心分散所以起点都在合理的“盆地”里最终收敛到的局部极小值差别不大。如果改成完全随机选点比如randi(n,1,k)两个初始中心可能落在同一个真实簇里迭代次数和最终划分都会明显波动。sqerr 0; for i 1:k idx find(cid i); if ~isempty(idx) sqerr sqerr sum(sum((x(idx,:) - repmat(nc(i,:), length(idx), 1)).^2, 2)); end end disp(sqerr)这段代码可以放在聚类结束后手动计算平方误差用来比较不同初始中心、不同迭代次数下的误差值。实际跑实验时会发现同一份数据、同一个 k跑十次会有十套不同的cid平方误差却不尽相同——差异可能只有 5% 以内但足以说明“结果稳定”不是由距离决定的而是由初始化分布决定的。理解这一点就能明白为什么后面第 6 章的“固定随机种子”是复现实验报告图形的必要手段。5. 复现 k-means 避坑随机初值、乱序标签、未归一化与 maxiter 陷阱5.1 每次运行图形都不一样现象照着实验报告敲完代码连跑三次三次的散点图颜色分布不同有时红色块大有时蓝色块大。原因bn round(n/k*rand)里的rand每次生成随机数初始中心每次都变。k-means 是局部收敛算法不同起点可能走向不同局部极小值。Iris 数据因为类间重叠小、归一化做得好最终“分类质量”差别不大但具体哪个样本被分到哪一类会有细微位移反映在图上就是颜色边界抖动。解决复现报告结果时在脚本开头固定随机种子rng(42); % 固定随机数生成器 bn round(n/k*rand);rng(42)是 MATLAB 推荐的固定方式替换原来的rand默认流。加这一行后bn稳定下来每次跑出的cid完全一致图形可复现。如果环境是旧版 MATLAB2013 之前改用rand(seed, 42)。5.2 把 k 改成 4报错或者分不出第四类现象主程序k3改成k4运行时nc矩阵维度不匹配或者图画出来只有三种颜色。原因主程序里nc [x(bn,:);x(2bn,:);x(3bn,:)]写死了三行。k4 时kmeans函数要求第二个参数是 4但nc只给了 3 个中心函数内部repmat(x(i,:),k,1)拿 4 个中心去减而nc只有 3 行维度对不上直接报错。就算手动把nc补成四行绘图里的if cid(i)3 ... else嵌套也只画到三类第四类不显示。解决改 k 时同步改初始中心和绘图分支k 4; bn round(n/k*rand); nc [x(bn,:); x(2*bn,:); x(3*bn,:); x(4*bn,:)];绘图分支加一层elseif cid(i)4并指定第四种颜色比如k*黑色。或者把绘图段改成循环加颜色数组colors rgbk; for i 1:n plot(x(i,1), x(i,2), [colors(cid(i)), *]); hold on; end参数说明colors(cid(i))按类别索引取颜色字符类别是 1 时取r、2 时取g、3 时取b、4 时取k。这样 k 无论取几只要颜色数组够长绘图代码不用改。5.3 聚类完成后类别标签是“乱序”的现象跑完聚类cid的第 1 类不是真实数据的第 1 类红色点对应的是原始数据里的中间类别序号对不上数据表。原因k-means 是划分聚类输出的是“第几个簇”的编号这个编号完全由初始中心决定。真实标签比如三种鸢尾花的品种顺序和聚类编号没有任何对应关系每次运行结果都可能把真实类别映射到不同编号上。解决对比聚类效果前先做标签匹配。最常用的是“多数投票”或“匈牙利算法”对 Iris 这个场景直接用混淆矩阵目测即可confusionmat(true_labels, cid)confusionmat是 MATLAB 统计工具箱的函数横轴真实标签、纵轴聚类编号。观察哪个真实类最多被分到哪个编号再把cid重编号对齐。不做这一步单看散点图容易误判“聚类错了很多”其实只是颜色和类别序号没对上。5.4 特征没归一化距离被最大数值列主导现象换了另一个数据集比如原始未归一化的 Iris花瓣长宽单位是厘米且最大值差 10 倍聚类出来的图形明显偏向某个特征另一个特征几乎不起作用。原因欧氏距离对各特征数值大小敏感。Iris 原始数据中花瓣长宽数值远大于花萼宽度距离计算几乎由花瓣特征主导花萼宽度信息被淹没。这份实验报告里x数据已经归一化到 0~1所以跑出来效果好这个隐藏前提很容易被忽略。解决换数据前先标准归一化x zscore(x);zscore把每个特征变成均值为 0、标准差为 1 的分布所有特征在距离计算中权重一致。如果是其他自定义缩放需求用(x - min(x)) ./ (max(x)-min(x))做 0~1 归一化也可以。系数差异和标准化后的聚类结构差异可以用第 4.3 节的sqerr量化比较。5.5 第二阶段的 maxiter2 和 iter 重置微调只执行一轮现象代码最后打印Some points were moved after the initial clustering procedure.但实际上只有一次调整如果把maxiter改成 10图形也几乎没有变化。原因iter在第一阶段结束时是 100进入第二段前代码用iter 1重置了计数器while iter maxiter move ~ 0中的maxiter2意味着最多执行 1 次调整循环体iter1 时进入iter2 时条件为假退出。所以这段“优化”在实际运行中只有一次机会移动点。如果这次调整后move仍为 1算法也不会继续直接返回。解决想要真正迭代到移动次数为 0需要去掉iter maxiter限制加一个更大的迭代上限和空簇保护maxiter 100; iter 1; move 1; while iter maxiter move ~ 0 move 0; % ... 原调整逻辑 ... iter iter 1; end参数说明是短路逻辑iter maxiter先判断move ~ 0再判断避免move在迭代到上限后被多算一次。实际测试时可以从 maxiter10 开始调如果连续几轮move都为 1 但图形基本稳定说明已经接近局部最优强行跑满上限只会浪费时间。6. 固定种子并留一份量化判据一个验证聚类效果的实操习惯复现实验报告时我习惯做三件事固定随机种子、多次初始化取最优、用轮廓系数兜底。固定种子前面说了加一行rng(42)就能保证图形和报告一致。但聚类不是回归只看一张图很容易被颜色分布误导——同一个点集k3 和 k4 的图都挺好看可哪个更合适我的做法是在主程序末尾加一个量化判据rng(42); best_sse inf; for trial 1:20 bn round(n/k*rand); nc [x(bn,:); x(2*bn,:); x(3*bn,:)]; [cid,nr,centers] kmeans(x,k,nc); sse sum(sum((x - centers(cid,:)).^2, 2)); if sse best_sse best_sse sse; best_cid cid; end end这段循环把随机初始化跑 20 次保留误差平方和最小的一组。centers(cid,:)的含义是取出每个点所属中心的坐标与原样本逐行相减再平方求和得到当前划分的总误差。多次初始化的意义在于k-means 每次只保证收敛到局部最优20 次里大概率能找到更靠近全局最优的解比单次运行的结果更稳定。对比 k3 和 k4 时分别跑这个循环比较两者的best_sse——如果 k4 只比 k3 降低了很小的幅度那多出来的簇就是过拟合不值得。轮廓系数也是一个易用的辅助工具[s,h] silhouette(x, best_cid); mean(s)silhouette返回每个样本的轮廓值范围在 -1 到 1 之间越接近 1 说明样本离自己簇中心近、离最近邻簇远。mean(s)超过 0.5 表示聚类结构较强低于 0.25 说明簇之间重叠严重。我第一次跑 k4 时mean(s)从 k3 的 0.56 直接掉到 0.38图形上看着有四个色块实际结构已经被破坏了。从那以后我每次跑完 k-means 都会强制走一遍“固定种子、多次初始化、轮廓系数”三连图形再好看也得先过量化这一关。希望帮到你。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号