
简介基于Matlab实现常用机器学习算法的源码与数据合集面向有Matlab基础、希望从可运行代码入手掌握经典算法原理的数据学习者和研究者适用于课程设计、论文复现与日常算法验证。压缩包共35个文件以26个m源码文件为主另有7个mat数据集、1个mlx实时脚本和1个txt说明整体约69.28MBm文件可直接运行算法模型mat数据用于实验输入mlx便于交互式逐段调试。已有788人学习下载。内容覆盖线性回归、逻辑回归、决策树、随机森林、SVM、神经网络、KNN、K均值及层次聚类等常用方法并提供银行营销、人体活动识别等真实场景案例可对照数据观察不同算法表现源码注释清晰、模块化组织既适合逐步理解机理也便于替换数据、调整参数后迁移到实际项目是一份扎实的Matlab机器学习自学资料。1. 为什么我会盯上这个标题常用算法在 Matlab 里本该是开箱即用的先讲一个我见过很多次的场景手里一堆课程设计或者小型项目的数据要跑分类、回归、聚类脑子里第一个念头往往是 Python。问题是相当一部分人电脑上早已装了 Matlab或者学校统一派发了 License这时候再绕道去配 Python 环境纯属折腾。而“基于Matlab实现常用机器学习算法源码数据”恰恰是这类人最该找的东西——它把回归、分类、聚类这些高频算法用 Matlab 写好连训练数据都一并备好解压后改改路径就能跑通第一版。对于赶课程设计的学生、做算法快速验证的工程师、以及想把老脚本从其他语言迁到 Matlab 的人来说这个方向比从零写一遍更划算。我的建议是别急着改代码先把整套流程摸透再谈调参。2. 先备好工具链工具箱检查、数据装载与目录规划拿到任何一份“源码数据”的压缩包最忌讳的是解压完立刻双击脚本然后报错开始瞎猜。Matlab 的机器学习能力集中在 Statistics and Machine Learning Toolbox 里很多第三方打包的 Matlab 环境组件不全第一步应该确认这个工具箱在不在。2.1 一个命令确认工具箱是否齐全% 检查 Statistics and Machine Learning Toolbox 是否可用 % 返回空结构体说明当前环境没装需要重新运行安装程序勾选组件 info ver(stats); if isempty(info) error(缺少 Statistics and Machine Learning Toolbox请补装组件); end disp(info.Name);ver(stats)是查工具箱的标准方式返回的info.Name会显示工具箱全名和版本。这里有两个细节值得注意一是就算你用 Matlab 2026b 这种新版本这套函数名也完全一致不存在老代码跑不了的问题二是如果后续要画混淆矩阵或者用自动调参依赖的内置函数也在这个工具箱里不需要额外装别的。确认完工具箱再看压缩包本身——这类包通常包含data和code两个目录前者放 csv、xlsx 或 txt 数据后者放.m脚本。我一般会先把目录整理成下面这个样子防止数据清洗时把原始文件改坏。目录名用途说明data/raw原始数据只读不改动data/clean清洗后的数据每次清洗跑完输出到这里scripts训练与评估脚本按算法或步骤拆分output模型、图片、指标结果保存训练产物目录整理看起来琐碎但实际项目里能省大量返工时间。Matlab 的工作区有记忆性今天改的变量明天还在文件路径一乱脚本里写死的相对路径就会全部失效报错信息还特别迷惑——明明文件在却弹“找不到文件”。所以解压之后第一件事是把当前文件夹切到包根目录再跑脚本。2.2 readtable 装载数据最稳的一条路Matlab 加载数据的姿势很多老代码里经常见到load(data.mat)或者importdata。但如果数据是 CSV 或 Excel我强烈建议统一用readtable它对表格结构的处理远比importdata干净。下面是典型的装载代码% 读取训练数据自动识别表头 raw readtable(data/raw/train_data.csv, ... ReadVariableNames, true, ... TreatAsMissing, {NA, NULL}); % 看一眼字段名与数据类型 disp(raw.Properties.VariableNames); % 快速查看缺失值统计、最大值、最小值 summary(raw);ReadVariableNames设为true表示第一行是表头这是最常见的设置。TreatAsMissing后面跟的是字符串单元数组把文本里表示缺失的NA、NULL统一转成NaN这一步不做后面特征矩阵里会出现字符串直接导致数值运算报错。summary(raw)是快速体检工具一眼能看到每个字段的缺失数量、数据类型和分布比我手动写循环统计快得多。读进来之后下一步是把表格拆成特征矩阵和标签向量% 取所有数值列做特征排除 id 和 label 这种非特征列 feat_cols raw.Properties.VariableNames; feat_cols(ismember(feat_cols, {id, label})) []; X table2array(raw(:, feat_cols)); % 标签列转成 categorical Y categorical(raw.label);table2array要求被转换的列是数值类型如果特征列里有文本这一步会直接报错说明前面TreatAsMissing没处理好。categorical(raw.label)是把标签列从字符串变成分类数组fitcsvm、fitctree这些训练函数只认这种格式直接把原始字符串塞进去很多版本会报“Y must be a vector”之类的错这是新手最常见的第一道坎。2.3 缺失值处理中位数填充与删除的取舍数据装载完了紧接着就是缺值处理。很多人上来就删行但小数据集删一行可能就损失 5% 的样本得不偿失。我的习惯是先统计缺失比例再做填充% 统计每个特征列的缺失数量 miss_count sum(ismissing(X), 1); disp(miss_count); % 缺失比例低于 5% 的列用中位数填充 for i 1:width(X) col X(:, i); if any(isnan(col)) median_val median(col(~isnan(col)), omitnan); X(isnan(col), i) median_val; end endismissing对数值矩阵的NaN和表格里的缺失值都有效。中位数填充比均值填充更稳健因为均值容易被极端值拉偏。如果某个列缺失比例超过 20%我不建议硬填直接删列更省事——这种特征的信息含量本身已经很低了强行填充只会引入噪声。等数据洗干净了才算真正进入模型训练环节。提示缺失值处理建议放在划分训练集之前做避免用测试集信息去填训练集这一步顺序错了后面交叉验证的指标全是虚的。3. 三个高频算法在 Matlab 里的落地写法回归、分类、聚类工具链备好了数据也洗完了现在进入正题。常用机器学习算法无外乎回归、分类、聚类三件事Matlab 对应的是fitlm、fitctree/fitcsvm、kmeans这一组函数。它们的设计思路一致把模型对象作为第一输出后续预测、评估都基于这个对象二次调用而不是像某些老脚本那样手动算权重。3.1 fitlm 做回归看系数不如看残差线性回归是算法中最朴素也最常用的一个fitlm一行就能出模型% 拟合线性模型 mdl fitlm(X, Y); % 查看系数、p 值、显著性五个统计量一把抓 disp(mdl.Coefficients); % 输出决定系数 fprintf(R-squared: %.4f\n, mdl.Rsquared.Ordinary);mdl.Coefficients里每一行对应一个特征的系数、标准误差、t 统计量和 p 值。p 值大于 0.05 的特征我一般会考虑剔除。但这里有个坑p 值是逐步回归的判断依据不是筛选特征的唯一标准——两个强相关特征同时放进模型各自的 p 值可能都被稀释这是共线性的经典表现需要结合corrcoef看特征相关性。模型训练完不是结束残差图才是检验假设的关键% 画残差对拟合值的散点图 plotResiduals(mdl, fitted);如果残差呈现漏斗状或者弯曲说明线性假设不成立需要尝试加交互项或换非线性模型调整。这个判断比盲目追求 R 方有意义得多——R 方再高残差有结构模型就是不成立的。3.2 fitctree 与 fitcsvm决策树入门、SVM 保底分类任务里我先说决策树因为它是唯一一个能直接打印成规则、肉眼审查的模型% 训练决策树限制复杂度防过拟合 tree fitctree(X, Y, ... MaxNumSplits, 20, ... % 最大分裂次数默认100 MinLeafSize, 5); % 叶子节点最小样本数默认1 % 用文本方式打印树结构 view(tree, Mode, text);MaxNumSplits直接控制树的层数上限小数据集上从 20 起步足够了——数值越大树越深训练误差越低但测试误差可能反弹。MinLeafSize是反过来的正则化旋钮设成 5 意味着每个叶子至少要有 5 个样本这在噪声较多的数据上能有效避免“为个别样本单独开枝散叶”。view(tree, Mode, text)会打印从根到叶的分裂规则这是决策树最有价值的地方它让我能直接看到模型学到了什么特征而不是一个黑匣子。SVM 是分类任务里的保底选择。我的默认配置如下% 二分类 SVMRBF 核自动估计核尺度 svm fitcsvm(X, Y, ... KernelFunction, rbf, ... KernelScale, auto, ... BoxConstraint, 1, ... Standardize, true);KernelScale设成auto让工具箱根据数据分布自动估计 RBF 核的宽度比自己手动试0.1、1、10高效得多。BoxConstraint是误分类惩罚系数值越大模型越努力适配每一个训练点也越容易过拟合从 1 开始调是常规做法。Standardize是 SVM 用户最容易漏掉的参数——它会在训练前自动做 z-score 标准化避免量纲大的特征在核函数距离计算里喧宾夺主。我第一次用 SVM 时没开这个开关特征里有“年龄”和“年收入”两个量纲完全不同的字段分类结果惨不忍睹后来才发现核距离被大数特征带偏了这就是典型的“参数没设对模型变玄学”。训练好的模型预测新样本也很直接% 预测新数据 pred_label predict(svm, X_new); % 如果需要概率分数修改 ScoreTransform svm.ScoreTransform logit; [~, score] predict(svm, X_new);ScoreTransform默认是nonepredict只返回硬标签。设成logit后第二输出值才带概率语义这在计算 AUC 或者做风险阈值控制时是必需的。3.3 kmeans 做聚类确定性与随机性的拉锯聚类的问题和分类不一样——它是无监督的没有标签可对所以判断标准从“分类准不准”变成了“簇内紧、簇间松”。kmeans的用法一行能写完但参数设置实际非常讲究% 固定随机种子保证结果可复现 rng(42); % 聚成 3 类欧氏距离10 次随机起始中心取最优 [idx, center, sumd] kmeans(X, 3, ... Distance, sqeuclidean, ... Replicates, 10, ... MaxIter, 300);K-means 的初始中心是随机选的不同的起点大概率收敛到不同的局部最优。Replicates设为 10等于从 10 组随机起点里挑组内误差sumd最小的一次输出这是对抗随机性的标准手段。MaxIter是单次迭代上限默认 100 够用但数据量大时我习惯提到 300 防止提前终止。Distance参数默认sqeuclidean换成cosine则适合文本向量这类方向敏感的数据。聚完类之后K 值选没选对才是真正的考点% 计算轮廓系数越大说明聚类结构越清晰 sil silhouette(X, idx); fprintf(平均轮廓系数: %.4f\n, mean(sil));轮廓系数的范围是[-1, 1]均值在 0.5 以上认为聚类结构合理0.2 以下说明 K 值没选好或者数据本就没有明显的簇状分布。我习惯对 K 从 2 到 8 各跑一遍把mean(sil)画成折线图找拐点——而不是靠“感觉差不多”拍脑袋决定 K。4. 别让验证拖后腿交叉验证、自动调参与评价指标很多脚本的毛病是训练完直接拿训练集准确率当结论这在学术或交付场景里骗不了任何人。模型要经得起推敲必须做样本外验证。4.1 用 cvpartition 守住数据划分的底线cvpartition是划分训练/测试集的标准工具而它最大的优势在于默认做分层划分类别不平衡时不会把某个类全分到一边% 5 折交叉验证分层划分保持各类别比例 cv cvpartition(Y, KFold, 5); accuracy zeros(cv.NumTestSets, 1); for i 1:cv.NumTestSets tr_idx training(cv, i); te_idx test(cv, i); mdl fitcsvm(X(tr_idx, :), Y(tr_idx), ... Standardize, true); pred predict(mdl, X(te_idx, :)); accuracy(i) sum(pred Y(te_idx)) / numel(te_idx); end fprintf(交叉验证平均准确率: %.2f%%\n, mean(accuracy) * 100);training(cv, i)和test(cv, i)分别返回逻辑索引每次循环拿其中一折当验证集。5 折算下来有 5 个准确率查看它们的方差比看均值更重要——如果某折准确率比其它折低 10 个百分点以上说明这折数据分布有异样可能缺失值处理没对齐也可能是特征里混入了与标签强相关的泄漏列。如果懒得写循环官方也提供了一行式的交叉验证% 直接对模型对象做 5 折交叉验证 cvsvm crossval(svm, KFold, 5); loss kfoldLoss(cvsvm); fprintf(交叉验证损失: %.4f\n, loss);注意这里crossval的输入是已经训练好的模型对象它会忽略原模型的训练结果重新做 K 折训练与评估。这个函数适合快速出指标但拿不到每一折的具体预测结果做不了混淆矩阵分析所以我个人更倾向于手写循环。4.2 自动调参贝叶斯优化比网格搜索快一个量级调参是机器学习里最耗时间的环节。Matlab 的OptimizeHyperparameters提供了开箱即用的自动调参底层是贝叶斯优化svm_best fitcsvm(X, Y, ... KernelFunction, rbf, ... Standardize, true, ... OptimizeHyperparameters, {BoxConstraint, KernelScale}, ... HyperparameterOptimizationOptions, struct(... MaxObjectiveEvaluations, 30, ... Kfold, 5, ... AcquisitionFunctionName, expected-improvement-plus));OptimizeHyperparameters后面列出要调的参数名BoxConstraint和KernelScale是 SVM 最核心的两个旋钮。HyperparameterOptimizationOptions里的MaxObjectiveEvaluations控制评估次数30 次是效果与耗时的折中点Kfold指定内部验证的折数AcquisitionFunctionName选expected-improvement-plus是目前调参效率较高的默认选择它在“探索未知区域”和“利用已知好区域”之间取得了不错的平衡。贝叶斯优化比网格搜索强在“智能挑点”——网格搜索每加一个参数维度评估次数呈指数增长而贝叶斯优化用之前所有评估结果构建概率模型下一次收益最大处才是下一个评估点。小数据量上跑 30 次大约几分钟大数据量建议先抽样子集再调参。4.3 评价指标混淆矩阵和 ROC 才是放大器准确率在高类别不平衡数据上是骗人的指标——99% 负类、1% 正类时全猜负类也有 99% 准确率。用混淆矩阵看一眼真实分布状况% 得到交叉验证的逐样本预测 pred_all kfoldPredict(cvsvm); % 画混淆矩阵 confusionchart(Y, pred_all);confusionchart不仅画矩阵还会自动统计每类的精确率和召回率。我习惯先盯对角线以外占比最大的格子——它指向模型最容易混淆的类别对这往往比总准确率更有业务指导意义。分类问题还应该看一眼 ROC 曲线与 AUC% 用 resubPredict 拿训练集预测分数 [~, score] resubPredict(svm); % 计算 ROC 曲线与 AUC正类 要指定是哪个类别 [fpr, tpr, ~, auc] perfcurve(Y, score(:, 2), 类别1); plot(fpr, tpr, LineWidth, 2); xlabel(假阳性率); ylabel(真正率); title(sprintf(ROC 曲线, AUC%.3f, auc));perfcurve的第三个参数是正类标签必须用类别名而不是位置索引。AUC 0.5 等于瞎猜0.7~0.8 可用0.9 以上才算强分类器。这里的score(:, 2)取的是第二列分数因为二分类模型输出两列——分别对应两个类别作为正类的概率。多分类问题则要拆成一对多分别画 ROC代码会复杂不少。5. 避坑跑通不等于跑对5个高频问题的排查手记“脚本能跑出结果”和“结果是对的”之间隔着一条鸿沟。以下 5 个问题是我见过也踩过频率最高的每一条按现象、原因、解决的思路拆开。5.1 报错 “Y must be a vector of responses”现象fitcsvm(X, Y)直接报错提示标签格式不对。原因从readtable里取出来的raw.label是table类型或者Y是字符向量元胞数组cell都不是训练函数认得的格式。解决显示转换类型Y categorical(raw.label);这一步在数据装载时做掉能避免后面所有训练函数报同一类错。如果label列本身是数值编码0/1那直接用table2array(raw(:, label))转成数值向量即可不需要categorical。5.2 训练不报错但评估指标全是 NaN现象模型训练正常但kfoldLoss或predict的结果出现NaN。原因特征矩阵X里存在NaN或者测试集里有训练集中从未出现过的分类水平。交叉验证的分层划分虽然是按类别比例分的但某些小类样本数太少比如只有 3 个某折里可能一个也分不到模型在训练集里就没见过这个类预测时自然输出 NaN。解决先统计类别频数countcats(Y)如果发现某一类样本少于 5 个直接放弃交叉验证改用留一法Leaveout, 1保证每次训练都尽可能包含所有类别。缺值问题则回到第 2 章的填充方案核心原则是训练和测试的特征处理必须一致测试集不能用训练集的均值/中位数以外的方式填充。5.3 SVM 准确率一直上不去数据换了好几份都一样现象用默认参数跑fitcsvm准确率徘徊在及格线附近调BoxConstraint也没改善。原因特征量纲差异过大比如一个特征范围是 0~1另一个是 0~100000。RBF 核函数里的欧氏距离会被大数值特征完全主导小数值特征几乎不参与相似度计算。解决打开Standardizesvm fitcsvm(X, Y, KernelFunction, rbf, ... Standardize, true);这个参数藏在fitcsvm的名称-值对里默认是false。如果脚本里用的是fitcsvm直接在Standardize处设true或者手动zscore标准化特征矩阵后再训练。手动标准化时注意保存均值和标准差预测新数据时要套同一个标准化参数否则预测结果没有意义。5.4 每次运行结果都不一样复现实验像抽奖现象同一份脚本、同一份数据跑两次测试集的预测结果不同准确率抖动明显。原因kmeans的随机初始中心、fitcsvm的优化过程、交叉验证的随机划分都会引入随机性。Matlab 默认每次启动随机种子重置所以“不固定种子等于不复现”。解决在每个脚本开头固定随机种子rng(42);42只是习惯取值换成任意整数都行。固定种子能让kmeans的Replicates机制、cvpartition的划分逻辑完全可复现。如果实验要对比多个算法这个习惯必须养成——否则你都不知道性能差异是算法造成的还是随机种子造成的。5.5 新版本函数报 “Undefined function”工具箱白装了现象代码是从老项目复制来的调用的函数在当前环境里提示 “未定义”。例如拿老的svmtrain用于新版本 Matlab。原因Matlab 在版本迭代里淘汰或重命名了一批机器学习函数。比较典型的是老版的svmtrain在新版里被fitcsvm取代决策树相关 API 也换过一轮。第三方脚本里如果混用了两代 API跑不起来非常正常。解决查文档确认版本% 查看函数所在工具箱与版本信息 which fitcsvm doc fitcsvm不太确定函数是否可用时优先搜官方文档而不是凭记忆写代码。工具箱装了但函数仍不可用检查path是否包含工具箱路径path只要ver(stats)有输出、path里包含toolbox/stats说明环境本身没问题剩下的就是代码里的函数名问题全局替换成新 API 即可。6. 收尾多算法对比、模型打包与部署前的那几步当单模型验证过关下一步是把多个候选算法放在同一套评估流程里横向对比这是选出最终方案的标准动作。我一般把所有算法写进一个元胞数组循环训练与评估models { (Xtr, Ytr) fitctree(Xtr, Ytr, MaxNumSplits, 20), ... (Xtr, Ytr) fitcsvm(Xtr, Ytr, Standardize, true), ... (Xtr, Ytr) fitcknn(Xtr, Ytr, NumNeighbors, 5)}; names {决策树, SVM, KNN}; for i 1:length(models) mdl crossval(models{i}(X, Y), KFold, 5); losses(i) kfoldLoss(mdl); fprintf(%s 损失: %.4f\n, names{i}, losses(i)); endfitcknn的NumNeighbors默认是 1很容易过拟合对比时建议至少设 5。损失值直接对比的意义大于单看准确率因为不同算法对类别分布的敏感度不一样。训练完选定最优模型剩下的是打包% 保存紧凑模型文件小、加载快适合部署 saveCompactModel(svm_best, model_svm.mat); % 部署环境里加载 mdl_loaded loadCompactModel(model_svm.mat);saveCompactModel和save的区别是前者只存训练好的模型结构不附带训练数据、交叉验证过程等冗余信息文件体积小一个量级。如果是做嵌入式或生产级部署还需要考虑两点一是预测函数输入输出尽量用普通数组而不是table方便生成 C 代码二是如果最终要出 C 代码还需要 MATLAB Coder 工具箱SVM 和决策树这类模型是可以生成的但categorical标签在 C 代码里会变成整数编码接口设计要提前定好。我自己早期做项目时就是栽在没固定随机种子上连续两次调参结果相差 5 个百分点还以为是算法问题排查半天最后发现只是没写rng(42)。这种低级错误只要养成习惯就能避免。希望上面这套从环境、数据到验证、部署的流程能帮你在 Matlab 里把机器学习跑得既快又稳。本文还有配套的精品资源点击获取