
如果你手头只有MATLAB却想动手跑一个机器学习的入门项目鸢尾花分类是一个绕不开的经典题。我平时带学生做课程设计时经常有人问机器学习是不是非得用Python其实完全不是MATLAB在处理二维表格数据、画特征图和调试算法上反而更顺手尤其是你只是想快速验证一个分类思路或者做一个能交差的实验原型MATLAB能省掉不少环境折腾的功夫。这篇文章我就用鸢尾花分类这个最经典的数据集完整走一遍用MATLAB做机器学习项目的流程数据加载、可视化探索、切分训练与测试集、训练kNN和决策树分类器、输出混淆矩阵与准确率报告最后聊几个新手容易踩的坑以及项目后续能怎么扩展。强调一下这不是“调包侠”教程。虽然因为MATLAB封装程度高代码看起来很短但每一步背后的逻辑我都会拆开讲——比如为什么测试集不能参与标准化、为什么单次划分的结果不能完全代表模型水平、为什么精确率和召回率不能只看其中一个。把这些想明白你才算真正会做机器学习项目而不是只会点运行。1. 项目思路与方案选型为什么是MATLAB为什么是鸢尾花1.1 用MATLAB做机器学习到底合不合适先说结论合适尤其在小数据集、算法验证、课程作业、科研Demo这类场景下MATLAB的优势非常明显。第一MATLAB的矩阵操作是底层设计没有Python里那种“列表推导、numpy广播”的额外学习成本你加载一个150×4的矩阵可以直接参与运算、切片、统计思维负担小很多。第二MATLAB自带一整套分类器接口fitcknn、fitctree、fitcecoc、fitcdiscr这些函数调用风格非常统一training、predict、crossval三步走适合快速搭建对比实验。第三绘图能力是默认配置gscatter一类的函数能让你在几秒钟内把特征分布画出来这对理解数据非常有帮助。当然这不是说让你抛弃Python。Python生态里的大规模深度学习框架、更灵活的预处理管道确实更占优势。但很多人面临的实际场景是老板或导师要求先出个快速结果或者你实验室的服务器上只有MATLAB授权或者你整个课程都在用MATLAB。这时候从MATLAB入手不是“将就”而是一种性价比很高的选择。等理解清楚机器学习的基本流程后再迁移到Python也不会有什么障碍因为核心概念是通用的。1.2 鸢尾花数据集为什么适合新手入门鸢尾花数据集是机器学习里最经典的“Hello World”数据之一英文名通常是Fisher’s Iris在MATLAB里对应变量名fisheriris。它有150个样本分成三个品种Setosa、Versicolor、Virginica每个品种50个样本。每条记录包括四个特征花萼长度、花萼宽度、花瓣长度、花瓣宽度单位都是厘米。这个数据好在哪首先是数据结构干净不存在大量缺失值、异常值非常适合用来学通流程。其次三个类别里Setosa和另外两个品种的特征区分度极高几乎用肉眼看一下花瓣长度就能分开而Versicolor和Virginica之间存在部分重叠需要综合多个特征才能较好区分——这就让任务有了“难度梯度”既能让你体会“特征可视化”的价值又能让你观察不同算法在边界样本上的表现差异。再次这是一个真实数据不是人为构造的玩具它有测量噪声但噪声不大所以各种经典分类算法的对照结果都很稳定。1.3 从数据到准确率的完整技术路线我在这个项目里按下面这条路线走你也可以直接照抄加载数据load fisheriris→ 标签格式转换cell转categorical→ 随机划分训练集与测试集 → 用训练集计算标准化参数并变换 → 特征可视化、观察大概可分性 → 训练多个分类器 → 在测试集上预测 → 计算准确率和混淆矩阵 → 对分类器做交叉验证 → 结论与对比。这套流程本质上是所有机器学习项目的通用框架只是数据集和算法变了。流程顺序上有一个关键点一定要先划分训练集和测试集再做标准化。道理后面会详细解释但顺序错了你的评估结果就会偏乐观写论文或做实验报告时这类操作是会被挑毛病的。2. 环境准备与数据获取没有工具箱一切都白搭2.1 需要哪些MATLAB工具箱我在项目里用到的核心工具箱是Statistics and Machine Learning Toolbox也就是统计与机器学习工具箱。fitcknn、fitctree、fitcdiscr、cvpartition、confusionchart这些函数都在这里面。另一个在扩展环节可能用到的是Deep Learning Toolbox但本篇文章不依赖它。如果只是想跑通鸢尾花分类一个统计与机器学习工具箱就够了。怎么确认自己有没有这个工具箱在MATLAB命令窗口输入ver回车后会列出所有已安装的工具箱名称和版本号也可以输入license(test,Statistics_Toolbox)返回1代表可用返回0代表没装或没授权。如果提示Undefined function fitcknn基本可以断定缺工具箱。2.2 关于安装和授权的几点建议这部分我说几句掏心窝的因为真的太多人一上来就在网上搜各种版本密钥、破解包浪费时间不说还可能踩到病毒。MATLAB官方对学生是有完整免费版的叫MATLAB Online或校园版很多高校都有校园授权输入学校邮箱就能用。即使暂时没有校园授权官方也提供30天试用版足够你把这个项目跑通好几遍。版本方面只要是R2018b以后的版本基本都支持我下面写的这些函数。confusionchart这个可视化函数出现在R2018b之后如果你还在用很老的版本可以用plotconfusion神经网络工具箱或直接手工绘图但强烈建议升级一下。安装的时候勾选Statistics and Machine Learning Toolbox其他工具箱按需勾选。2.3 两种获取鸢尾花数据的常用方式第一种最省事直接在命令行输入load fisheriris。这个数据内置于Statistics and Machine Learning Toolbox中加载之后工作区会出现两个变量meas是一个150×4的double矩阵species是一个150×1的cell数组里面是setosa、versicolor、virginica字符串。第二种方式是自己拿CSV数据练手这更贴近真实工作因为实际项目里你拿到的数据很少是MATLAB内置的。你可以把鸢尾花数据导出成iris.csv然后T readtable(iris.csv); X T{:, 1:4}; Y categorical(T.species);readtable返回一个table类型T{:,1:4}是提取前四列数值T.species是品种列。表格数据的好处是有列名之后如果要跟别人协作或者换工具分析csv仍然是通用率最高的格式。3. 数据加载、划分与标准化训练开始前最关键的几步3.1 数据加载与标签转换不管用内置数据还是CSV我都建议先把标签转成categorical类型。原因是categorical数据在做比较、混淆矩阵、画图时都非常方便直接能用判断相等不会像cell数组那样要先取元素再strcmp。load fisheriris; X meas; % 150x4 double Y categorical(species); % 150x1 categorical转换后你可以在命令窗口输入class(Y)确认类型。这一步虽然简单但能避免后面一卡就是半天的类型不匹配报错。3.2 训练集和测试集怎么划分才科学机器学习里有个铁律评估模型用的数据必须是模型训练时没见过的数据。如果你用全部数据又训练又评估模型相当于“考试前翻过答案”会给人不切实际的自信。所以要把数据拆成训练集和测试集在训练集上拟合并调参在测试集上做最终评估。最稳妥的划分方式是用cvpartition而不是自己随手randperm一次性打乱。原因有两个第一cvpartition做的是分层抽样保证每个类别的比例在训练集和测试集里保持一致避免出现测试集里恰好抽不到某个类别的情况第二它生成的partition对象可以复用于后续的交叉验证代码风格更统一。rng(2024); % 设置随机数种子保证结果可复现 cv cvpartition(Y, HoldOut, 0.3); idxTrain training(cv); idxTest test(cv); XTrain X(idxTrain, :); YTrain Y(idxTrain); XTest X(idxTest, :); YTest Y(idxTest);这里HoldOut为0.3表示把约30%的数据留作测试集。为什么选0.3这是最常见的选择之一样本量不大时留20%~40%都合理留太少评估不稳留太多训练数据不够。对于只有150个样本的数据集留30%大约45条测试样本已经能看出趋势了。3.3 标准化kNN这类距离算法的隐形门槛标准化这步很多新手会忽略但它对kNN这类基于距离的算法影响巨大。kNN判断一条测试样本属于哪一类时看的是距离它最近的K个训练样本的欧氏距离如果某个特征的数值范围远大于其他特征那么距离计算会几乎被这个特征主导。鸢尾花数据里花瓣长度大约在1.0~6.9之间花萼宽度大约在2.0~4.4之间看似接近但实际不同特征的方差差异足以让距离度量失衡。更普遍的情况是特征可能有“年龄0-100”和“收入0-1000000”这种天壤之别不标准化就是灾难。标准化的常见方式之一是z-score标准化把特征变成均值0、标准差1[XTrain, mu, sigma] zscore(XTrain); XTest (XTest - mu) ./ sigma;注意看我只在训练集上调用zscore并记录返回的mu和sigma测试集是用训练集的mu和sigma做变换。这是整个流程里最容易出错的地方。为什么不能直接在测试集上再算一次zscore因为测试集被视为“未来数据”你不能用它的统计量来调整自己否则就引入了测试集信息评估结果不可信。注意如果你不手动做标准化也可以直接在fitcknn里设置Standardize, true让函数在内部处理。但当你手动标准化之后就不要再设置Standardize为true了否则等于做了两遍变换。另外还要提醒一点决策树和SVM里的线性核对标准化的敏感度跟kNN不完全一样。决策树基于阈值分裂不受单调变换影响所以不标准化也能跑但为了流程统一和后续模型对比公平我在这个项目里还是统一做了标准化。4. 特征可视化先看清数据再谈建模4.1 散点图矩阵快速观察类别的可分性拿到数据别急着丢给分类器。先把数据可视化往往能给你很多直观判断。最简单的做法是两两画散点图看不同类别在特征空间里的分布。figure; gscatter(meas(:,1), meas(:,2), species); xlabel(花萼长度 (cm)); ylabel(花萼宽度 (cm)); title(花萼长度 vs 花萼宽度);再画一张花瓣特征figure; gscatter(meas(:,3), meas(:,4), species); xlabel(花瓣长度 (cm)); ylabel(花瓣宽度 (cm)); title(花瓣长度 vs 花瓣宽度);第一次跑这个项目的人看到第二张图往往会很兴奋三个类别的点基本分在三个区域里尤其是setosa跟另外两类离得很远只在左下角一小团。这说明只用花瓣长度和花瓣宽度两个特征就能达到很不错的分类效果。而第一张图里三类的点混在一起比较多说明花萼长宽这个特征组合区分度有限。如果想看所有特征两两之间的关系可以用gplotmatrix或plotmatrix后者能直接把4×4的子图画出来对角线是单特征直方图非对角线是两两散点图。这一步不是必做但能帮助你决定后续要不要做特征选择或特征降维。4.2 箱线图和直方图怎么看特征区分度散点图适合看两两关系箱线图适合看单个特征在不同类别上的分布幅度。比如figure; boxplot(meas(:,3), species); ylabel(花瓣长度 (cm)); title(不同类别下的花瓣长度分布);你会看到setosa的箱体远低于另外两类基本不重叠versicolor和virginica的箱体有部分重叠但中位数差异明显。这个观察和后面决策树在花瓣长度阈值附近第一次分裂是呼应的。直方图也有类似作用但直接用histogram按类别画需要逐类绘制不够方便所以我一般以boxplot为主。看图的目的是建立直觉如果一个特征能把某个类别单独切出来模型学起来会很容易如果所有特征都重叠严重那就要考虑要不要换特征、增加样本或使用非线性模型。5. 分类器训练与评估跑通核心流程5.1 用fitcknn训练kNN分类器kNNK近邻是机器学习中最直观的分类算法。对新样本找出训练集中离它最近的K个样本让这K个邻居投票票数最多的类别就是预测结果。它不做显式“训练”本质上是把训练数据存下来预测时做距离计算。在MATLAB里训练一个kNN分类器一条命令就够了mdlKNN fitcknn(XTrain, YTrain, NumNeighbors, 5, Distance, euclidean);这里NumNeighbors是K值我选了5。为什么是5不是1或50K太小比如1分类边界会很碎一点点噪声就会改变预测结果K太大决策面变得平滑但可能把小区域的真实结构也抹掉了。5是鸢尾花这类小数据集上比较稳妥的默认值。Distance用欧氏距离是kNN最常用的距离定义。如果想试试其他距离度量也可以改成cosine或mahalanobis但需要结合特征含义判断。预测和评估predKNN predict(mdlKNN, XTest); accKNN sum(predKNN YTest) / numel(YTest); fprintf(kNN 测试集准确率%.2f%%\n, accKNN * 100);这一步你会得到一个百分数比如97%左右。这个数字会因为随机划分不同而有波动我后面会讲怎么用交叉验证把波动看清楚。5.2 用fitctree训练决策树并可视化决策规则决策树和kNN的思路完全不同。它是一连串的if-else规则比如第一层判断“花瓣长度是否小于2.6”小于则判为setosa大于则继续判断下一个特征一层层往下直到某个叶子节点给出最终类别。这种可解释性是它最大的优点尤其适合需要向别人解释判据的场景。mdlTree fitctree(XTrain, YTrain, MaxNumSplits, 10); view(mdlTree, Mode, graph);MaxNumSplits指最大分裂次数限制树不要长得太深。限制树深度的原因一是防止过拟合二是让树更容易看。如果不限制决策树很容易在训练集上长到完美分类但在新数据上表现下降。view函数打开的是图形窗口能看到一棵倒着的树每个节点显示分裂特征和阈值比如“PetalLength 2.45”。我强烈建议你跑一遍并仔细看看因为这是最能直观体会“机器学习模型在学什么”的瞬间之一。你会发现树的第一刀往往就切在花瓣长度或花瓣宽度上这和我们第4节可视化观察到的规律完全一致——模型自动找到了区分度最高的特征。5.3 模型评估准确率、混淆矩阵与F1准确率是最直观的指标但在类别不平衡或错误代价不同的场景下只有准确率远远不够。比如癌症筛查99%都是健康人一个“全都判健康”的模型准确率能到99%但它漏掉了全部癌症患者这显然不可接受。所以要看混淆矩阵。figure; cm confusionchart(YTest, predKNN);confusionchart会画一个对角线很亮的热力图对角线是预测正确的数量非对角线是具体错分成了哪些类。对鸢尾花数据常见的错误是versicolor和virginica之间互相分错setosa几乎不会错。这再一次印证了可视化阶段看到的setosa特征差异极大另外两类在边界区域有重叠。如果你需要数值可以这样提取C confusionmat(YTest, predKNN);C是3×3矩阵C(i,j)表示第i类真实样本被预测成第j类的数量。由C可以计算每个类别的精确率Precision和召回率Recall精确率 该类预测正确的数量 / 预测为该类的总数量 召回率 该类预测正确的数量 / 该类的真实总数量。F1分数是两者的调和平均F1 2 * Precision * Recall / (Precision Recall)。在多分类任务里最简单的做法是分别算每个类别的F1再取宏平均。我自己的习惯是报告里放准确率混淆矩阵偶尔补一下宏平均F1这样能同时看出整体水平和错误分布。5.4 换用SVM和线性判别分析做对照一个项目只跑一个模型参考价值有限。最好多跑几个放在一起对比。这也是训练阶段最费时间但最有收获的一步。mdlSVM fitcecoc(XTrain, YTrain, Learners, svm); predSVM predict(mdlSVM, XTest); accSVM sum(predSVM YTest) / numel(YTest); mdlLDA fitcdiscr(XTrain, YTrain); predLDA predict(mdlLDA, XTest); accLDA sum(predLDA YTest) / numel(YTest);fitcecoc是多分类SVM的封装它把3分类拆成多个二分类子问题再投票综合。fitcdiscr是线性判别分析假设每个类特征服从高斯分布且协方差相同用贝叶斯准则决策。在鸢尾花数据上三者通常都能达到93%以上。如果某一次划分里kNN出现较多边界样本误判SVM和LDA的准确率往往会更稳一点。这背后的原因是当训练样本只有105条时kNN对局部密度很敏感而SVM和LDA会学“分类面”对少量噪声样本的容忍度更高。多模型对比的意义就在这里不同算法有不同的归纳偏置换一个算法可能在同一份数据上表现不同你的任务就是找到合适的那一个。6. 扩展方向从一个小Demo到更完整的项目6.1 用交叉验证替代单次划分我前面说过单次划分的准确率有波动。如果你想更客观地评估模型可以用K折交叉验证。思路是把训练数据均分成K份轮流拿K-1份训练、1份验证重复K次把K次的错误率平均得到更稳定的估计。MATLAB里这样写mdlKNN fitcknn(XTrain, YTrain, NumNeighbors, 5); cvmdl crossval(mdlKNN, KFold, 5); loss kfoldLoss(cvmdl); cvAccuracy 1 - loss;K一般取5或10样本量不大时取5比较常见。交叉验证的缺点是计算量增加K倍但鸢尾花这种小数据集完全没压力。把单次准确率和K折交叉验证结果一起列进报告会更有说服力也更接近真实研究里“报告泛化性能”的做法。6.2 用Classification Learner App零代码试错MATLAB里还有一个非常适合新手的东西Classification Learner在App标签页里能找到。你只需要把特征和标签导入左边勾选kNN、决策树、SVM等算法点训练它一次性能跑很多模型还自动给出准确率排行和混淆矩阵。用App做“快速试错”把有潜力的模型导出到工作区再用命令行精调是我个人很喜欢的组合方式。要注意的是App导出的模型变量可以直接用predict去预测新数据但特征顺序必须和训练时完全一致。这是新手最容易踩的另一个坑训练时列顺序是[花萼长花萼宽花瓣长花瓣宽]预测新数据时却把顺序搞乱了模型的分类结果会非常离谱。6.3 模型导出与部署思路项目不能只停留在工作区里。MATLAB里可以把训练好的模型保存成文件之后直接用saveCompactModel(mdlSVM, irisSVMModel.mat); loadedMdl loadCompactModel(irisSVMModel.mat); predNew predict(loadedMdl, newData);在R2021a之后的版本推荐用saveCompactModel如果还想部署到Excel、Python、Java或MATLAB Production Server可以用exportCompactModel。如果你的应用场景是嵌入式实时预测还可以用MATLAB Coder把predict代码生成成C/C。这些都属于工程化内容刚入门不必全搞懂但知道“模型能导出去用”思路就打开了。6.4 从鸢尾花到实际业务数据拆掉“鸢尾花专属”的心理这个项目的流程可以直接迁移到很多实际任务。比如在工业场景里用传感器采集的振动、温度、电流特征做设备故障分类在生物医学场景里根据血液指标判断患病类型在营销场景里根据消费行为做客户分群。替换掉数据、特征、标签然后沿用“加载数据→划分→标准化→训练→评估→交叉验证”这套骨架即可。如果数据规模变大比如几十万行要考虑的特征工程和算法选型会复杂很多但机器学习的基本流程和评估逻辑是不变的。这也是我强调“先把这一条最小闭环跑通”的原因——跑通一次你就有了一套可复用的思维模板。7. 常见问题与排查技巧实录7.1 高频报错与解决方案我在带这个项目的时候见过下面这些高频问题按发生频率列个速查表。症状可能原因解决办法Undefined function or variable fitcknn未安装Statistics and Machine Learning Toolbox安装工具箱或用ver确认授权species是cell用于报错数据类型不一致先执行categorical(species)predict后比较pred YTest维度报错标签是字符向量cell统一转categorical标准化后kNN效果反而更差可能没保留mu/sigma测试集被独立标准化用(XTest - mu) ./ sigma每次运行准确率都不一样随机划分种子不同运行前先rng(2024)固定随机种子confusionchart报错版本太老R2018b以下用plotconfusion或手动画图决策树图形窗口中文标签乱码字体不支持中文用英文标签或修改Figure字体为宋体/雅黑新数据predict报错“列数不一致”特征顺序/数量与训练时不匹配确保新数据按训练时的列顺序输入在App里训练好的模型不知道怎么保存未点击导出按钮在Classification Learner里用Export Model导出单独强调一个容易被忽略的设置随机种子。rng(2024)不是可有可无它是复现结果的前提。没有随机种子你每次划分的训练测试集不同得到的准确率可能在93%和100%之间晃动写报告时自己都没法解释“为什么昨天跑出来97%今天变成93%”。这不是代码错了是随机性在起作用。固定种子后结果可复现实验才谈得上可比较。7.2 让实验可复现的操作习惯建议养成两个习惯。第一个是写脚本而不是一路在命令行敲。把上面所有代码按顺序整理到同一个.m文件里每次运行都能得到一致结果也方便保存和复盘。第二个是遇到不认识的函数直接在命令行输入doc 函数名MATLAB会弹出官方文档里面有完整的输入输出说明和示例。比如doc fitcknn比在网上搜半天答案靠谱得多尤其适合新手建立函数使用的准确认知。另外调试时优先检查数据形状。在训练前用size(XTrain)、size(YTrain)确认维度在预测后用size(predKNN)确认输出长度是否等于测试样本数。很多时候报错信息很长但根源就是某个变量维度没对上。把数据形状打出来看一眼问题往往立刻清楚。8. 一点个人心得8.1 跑通流程比记住API更重要当初我第一次跑鸢尾花分类时也经历了“代码全对但逻辑不熟”的阶段看别人的脚本觉得简单自己动手却总卡在数据类型转换和划分逻辑上。这个项目的价值不在于“跑通”本身而在于帮你建立起机器学习的完整操作心智数据是原料特征是燃料模型是引擎评估是仪表盘。这四个环节缺一不可。如果你已经把这个项目从头到尾跑完我建议你做一个“变式练习”把鸢尾花数据换成自己的CSV或者把分类器换成朴素贝叶斯、神经网络或者把三分类问题改成二分类问题。每换一次你对流程的理解都会深一层。毕竟真正的工作里几乎不会有现成的fisheriris等你load。8.2 下一步可以从哪里扩展跑完这个项目之后我比较推荐按这个顺序继续深入先学交叉验证和超参数调优搞清楚kNN里的K值、决策树里的MaxNumSplits怎么选再做特征工程比如用PCA对鸢尾花特征降维观察降维后再分类的效果最后可以接触一点深度学习用MATLAB的Deep Learning Toolbox搭一个简单的全连接网络试试同样的数据集。每一步都建立在最小闭环之上就不会觉得机器学习是个黑盒。就这样动手跑一遍比看十遍教程都管用。等你跑完欢迎回来交流你踩到的坑。