十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MATLAB SVM分类完整流程:从数据划分到模型评估与保存

MATLAB SVM分类完整流程:从数据划分到模型评估与保存 简介这是一份基于Matlab的SVM支持向量机分类算法资源包适合需要快速实现分类任务的毕设、课题研究、工程应用也适合借助清晰注释理解SVM原理的初学者覆盖常见分类场景。压缩包内共10个文件涵盖核心m脚本、Excel数据集、libsvm编译的mexw64动态库、训练与测试的混淆矩阵及预测对比图、参数说明文档等整体仅323KB轻量精悍目录结构清晰便于检索。目前已有356人学习下载与使用具备一定参考价值。使用上中文注释非常清晰只需按示例数据修改格式并替换数据集即可直接运行同时提供训练集与测试集两份结果图像与评价指标能直观观察分类精度、查准率、查全率等表现便于后续核函数选择与参数调优附带的参数说明文档能帮助理解惩罚因子、核函数等关键设置适合直接用于教学演示或作为二次开发基础。1. MATLAB 里做 SVM 分类最花时间的从来不是 fitcsvm 那一行如果你看过几篇 MATLAB SVM 教程会发现训练代码翻来覆去就是fitcsvm(X,Y)加一个核函数参数。真正让分类结果可用的是它前后那些没人细讲的步骤数据怎么划分出测试集、特征要不要标准化、多分类怎么绕开二分类限制、预测结果怎么画成图、评价指标怎么算才算完整。这篇就把这条链路按我实际调参的顺序拆开从组织数据一直写到最后一行的模型保存。适合已经会用 MATLAB 基本语法、想在项目里正经交付一个分类任务的工程师也适合写作业时不想只交一张混淆矩阵图的学生。读完你能得到一套可以直接改数据路径就复现的 SVM 分类流程包括决策边界可视化代码和一套不注水的评价指标计算。2. 测试数据集划分与特征标准化决定 SVM 上限的前置步骤2.1 用 cvpartition 做分层划分先把测试集锁起来标题里强调“含测试数据集”很多人第一步就做错了拿全部数据训练再拿全部数据算准确率。这个数字虚高因为模型见过所有样本泛化能力完全没被检验。我一般先把数据分成训练集和测试集用cvpartition做分层抽样保证每个类别在训练集、测试集里的比例和原始数据一致。load fisheriris X meas; % 150x4 的特征矩阵 Y species; % 150x1 的类别标签 rng(42); % 固定随机种子保证结果可复现 cv cvpartition(Y, HoldOut, 0.3); % 30% 作为测试集 idxTrain training(cv); % 逻辑索引训练集取 true idxTest test(cv); % 逻辑索引测试集取 true X_train X(idxTrain, :); Y_train Y(idxTrain, :); X_test X(idxTest, :); Y_test Y(idxTest, :); fprintf(训练集样本数: %d, 测试集样本数: %d\n, ... sum(idxTrain), sum(idxTest));HoldOut指定测试集占比0.3 表示保留 30% 数据做测试。rng(42)这条极其关键不复现随机种子的话每次运行划分结果都不同后面对比模型效果时你根本分不清是参数变化还是数据划分变化导致的波动。分层划分还有个隐藏好处当某个类别样本特别少时随机划分可能把这类样本全分到训练集里测试集里直接缺类后面的混淆矩阵和 ROC 都没法画。2.2 标准化用 zscore还是用 fitcsvm 的 Standardize 参数SVM 依赖样本间的距离计算特征量纲不一致时数值范围大的特征会主导间隔最大化过程数值小但区分度高的特征反而被忽略。常见做法是训练前对特征做标准化。mu mean(X_train, 1); sigma std(X_train, 0, 1); sigma(sigma 0) 1; % 防止常数列除零 X_train_std (X_train - mu) ./ sigma; X_test_std (X_test - mu) ./ sigma;注意测试集的标准化必须用训练集算出的mu和sigma不能重新对测试集单独算均值方差。测试集要模拟的是“未来新到的数据”新数据不可能预先知道全量分布的统计量。如果你不想手写这一步fitcsvm里直接传Standardize, true也能做但要注意它只对训练过程标准化预测时predict会自动沿用训练时的参数对用户是透明的。两种方式我都用过手写标准化适合你要做特征工程可视化、想确认每个特征具体被缩放成什么样的情况Standardize参数适合快速跑基线。2.3 二维可视化训练前先用 gscatter 看类分布数据准备完先别急着训练。分类任务里先画一遍数据分布能省掉后面大量盲调时间。figure; gscatter(X_train(:,1), X_train(:,2), Y_train, rgb, o); xlabel(特征 1); ylabel(特征 2); title(训练集样本分布); legend(Location, best); grid on;连画几组特征对重点看类别之间是线性可分的还是需要弯曲边界是否存在某个类完全被另一类包围有没有明显离群点。如果两类重叠很严重后续 SVM 准确率上限就摆在那不是调参能突破的。这一步还能帮你决定要不要用 RBF 核——下面这章就说怎么选。3. fitcsvm 训练参数怎么定核函数、BoxConstraint、KernelScale3.1 线性核还是 RBF 核先看数据量再看边界形态SVM 的核心思想是找一个间隔最大的分类超平面硬间隔搞不定时引入松弛变量允许少量样本越界。MATLAB 里fitcsvm默认用线性核对二维数据就是画一条直线。线性核的优势是参数少、训练快、不易过拟合但数据边界本身是圆弧或者更复杂形状时线性核再调参也救不回来。RBF 核高斯核把样本映射到高维空间能拟合任意复杂边界代价是两个超参数要调而且调不好很容易过拟合。我一般的选择标准特征维度很高但样本量小先用线性核跑一遍基线特征维度低、样本散布明显非线性直接上 RBF 核。Fisher Iris 数据集用 RBF 核能得到漂亮的非线性边界下面的代码就以它为例。3.2 fitcsvm 核心参数表与网格搜索写法fitcsvm需要认真对待的参数就这么几个参数作用调参方向KernelFunction核函数类型linear、rbf、polynomial线性可分用 linear非线性用 rbfKernelScaleRBF 核的尺度参数值越小边界越弯曲网格搜索配合 BoxConstraintBoxConstraint误分类惩罚越大越不容忍错分过拟合时减小欠拟合时增大Standardize是否自动标准化特征特征量纲差异大时设 trueOutlierFraction允许的离群点比例替代硬惩罚数据有明显噪声时从 0.01 起试先固定 RBF 核对BoxConstraint和KernelScale做网格搜索% 二分类只取 setosa 和 versicolor 两类做演示 idx ismember(Y_train, {setosa,versicolor}); X2 X_train_std(idx, :); Y2 Y_train(idx); C_range [0.1, 1, 10, 100]; S_range [0.1, 0.5, 1, 2]; bestAcc 0; for C C_range for S S_range mdl fitcsvm(X2, Y2, ... KernelFunction, rbf, ... KernelScale, S, ... BoxConstraint, C, ... Standardize, false); % 在训练集上做交叉验证估计泛化能力 cvmdl crossval(mdl, KFold, 5); acc 1 - kfoldLoss(cvmdl); fprintf(C%.1f, Scale%.1f, 交叉验证准确率%.2f\n, ... C, S, acc); if acc bestAcc bestAcc acc; bestParams [C, S]; end end end fprintf(最优参数: C%.1f, Scale%.1f, 准确率%.2f\n, ... bestParams(1), bestParams(2), bestAcc);kfoldLoss返回的是误分类损失用 1 减掉就是交叉验证准确率。这里在循环内部做交叉验证是为了避免“拿测试集调参”的经典错误——测试集只能用一次最终模型确定后再测。BoxConstraint增大时模型会更努力地正确分类每一个训练样本边界变得更复杂训练集准确率上升但测试集准确率可能下降这就是过拟合信号。KernelScale变小同样会让决策边界越来越弯曲。网格搜索的结果可以画成热力图方便直观选参数区域。3.3 多分类必须用 fitcecoc 包装别直接调 fitcsvmfitcsvm本身只能做二分类遇到三分类以上任务直接报错。MATLAB 提供的标准做法是用fitcecoc做多分类包装内部用一对一的策略组合多个二分类器。比如三个类别会生成三对分类器每对输出一个预测最后投票决定最终类别。% 三分类完整训练 template templateSVM( ... KernelFunction, rbf, ... KernelScale, auto, ... BoxConstraint, 1, ... Standardize, true); mdl fitcecoc(X_train_std, Y_train, ... Learners, template, ... Coding, onevsone, ... Verbose, 1);templateSVM把 SVM 参数封装成模板传给fitcecoc后它对每个二分类器都用同一套配置。Coding参数决定多分类策略onevsone是默认值每两类之间训练一个分类器类别数很多时改用onevsall会减少分类器数量训练更快但每个二分类器面对的是“这一类 vs 其余所有类”的不平衡问题精度通常略低。Verbose设为 1 可以在命令行看到训练进度帮助确认流程卡在哪一步。4. 预测与图像把决策边界和错分点画出来4.1 单样本预测predict 的 label 与 score 分别代表什么模型训练完后predict是唯一的预测入口。对二分类模型它返回两个输出预测标签和分类得分。得分不是概率而是样本到决策边界的带符号距离正数表示偏向正类绝对值越大信心越强。% 对测试集做预测 [idxPred, score] predict(mdl, X_test_std); % 查看前 5 个样本的预测结果 for i 1:5 fprintf(真实标签: %s, 预测标签: %s, 得分: %.3f\n, ... char(Y_test(i)), char(idxPred(i)), score(i, 1)); endscore矩阵的行对应每个样本列对应类别。二分类时第一列是正类得分第二列是负类得分。许多误分类样本的得分绝对值非常接近 0说明它们本来就骑在决策边界附近这类样本靠调参也很难救回来。如果你想输出 0 到 1 之间的概率值需要对模型做概率校准这在第 5 章最后单独讲。4.2 绘制全空间决策区域图决策边界可视化是 SVM 分类报告里最直观的一张图。做法是在特征空间铺一层密集网格点让模型逐点预测用颜色填充出决策区域。% 生成覆盖特征空间的网格 x1Range linspace(min(X_train_std(:,1)), max(X_train_std(:,1)), 200); x2Range linspace(min(X_train_std(:,2)), max(X_train_std(:,2)), 200); [xx1, xx2] meshgrid(x1Range, x2Range); XGrid [xx1(:), xx2(:)]; % 网格点预测 [gridPred, ~] predict(mdl, XGrid); gridPred categorical(gridPred); % 绘制区域填充图 figure; gscatter(xx1(:), xx2(:), gridPred, [0.8 0.9 1; 1 0.9 0.8; 0.9 1 0.85]); hold on; gscatter(X_train_std(:,1), X_train_std(:,2), Y_train, rgb, o); xlabel(标准化特征 1); ylabel(标准化特征 2); title(SVM 决策边界与训练样本分布); legend(预测区域 1, 预测区域 2, 预测区域 3, ... 真实 setosa, 真实 versicolor, 真实 virginica, ... Location, best);网格密度 200×200 共 4 万个点对单次预测来说压力不大出图也够平滑。特征超过两个时可以选贡献最大的两个特征画图或者先用 PCA 降到二维再画。观察区域边界是直线还是曲线、不同类别的过渡带宽度能直接看出模型是线性核还是 RBF 核也能发现某些区域的边界明显不符合真实分布——那可能是训练数据本身有缺失。4.3 把测试集错分样本单独标出来决策区域图画的是模型全貌但阅读者往往更关心测试集里哪些样本被分错了。单独画一张错分样本分布图比只给一个准确率数字有用得多。misIdx find(idxPred ~ Y_test); figure; gscatter(X_test_std(:,1), X_test_std(:,2), Y_test, rgb, .); hold on; plot(X_test_std(misIdx,1), X_test_std(misIdx,2), kx, ... MarkerSize, 10, LineWidth, 2); legend(真实 setosa, 真实 versicolor, 真实 virginica, ... 错分样本, Location, best); title(sprintf(测试集错分样本分布共 %d 个, length(misIdx)));错分样本集中在类别交界处是最理想的情况说明数据本身有重叠。如果错分样本散落在类别中心区域就要检查特征是否选对、有没有数据标注错误。用黑色叉号叠加在散点图上在论文和报告里都能直接用。5. 评价指标怎么做才算详细混淆矩阵、F1、ROC 与 AUC5.1 confusionchart 与手算 precision、recall、F1单一准确率指标会掩盖很多问题类别不平衡时尤其危险99% 的样本属于 A 类模型全预测成 A 类也能拿到 99% 准确率。所以评价指标要拆开看。第一步永远是画混淆矩阵。figure; cm confusionchart(Y_test, idxPred); cm.Title 测试集混淆矩阵; cm.RowSummary row-normalized; % 每行显示召回率 cm.ColumnSummary column-normalized; % 每列显示精确率confusionchart会把每个类别的真正例、假正例、假负例都摆出来。基于混淆矩阵逐类计算三个核心指标指标公式MATLAB 计算方式Precision精确率TP / (TP FP)diag(C) ./ sum(C, 1)Recall召回率TP / (TP FN)diag(C) ./ sum(C, 2)F1 值2PR / (P R)2 * P .* R ./ (P R)C confusionmat(Y_test, idxPred); P diag(C) ./ sum(C, 1); R diag(C) ./ sum(C, 2); F1 2 * P .* R ./ (P R); fprintf(各类别评价指标:\n); for i 1:length(P) fprintf(类别 %s: 精确率%.3f, 召回率%.3f, F1%.3f\n, ... unique(Y_test)(i), P(i), R(i), F1(i)); end fprintf(宏平均 F1: %.3f\n, mean(F1));宏平均是逐类算指标再取平均给每个类同等权重微平均是把所有类的 TP、FP、FN 加起来再算受大类的支配更强。分类报告中建议两个都算公式里如果出现除零说明某个类在预测结果里完全没出现这本身就是一个值得注意的信号。5.2 二分类用 perfcurve 画 ROC多分类逐类画ROC 曲线衡量的是分类器在不同阈值下的真阳率与假阳率权衡能反映模型排序能力与具体阈值无关。perfcurve是 MATLAB 画 ROC 的标准函数。% 二分类setosa vs 其余两类 Y_bin double(strcmp(Y_test, setosa)); score_bin score(:, 1); % 取第一列得分 [fp, tp, ~, auc] perfcurve(Y_bin, score_bin, 1); figure; plot(fp, tp, b-, LineWidth, 2); xlabel(假阳率); ylabel(真阳率); title(sprintf(SVM 分类 ROC 曲线 (AUC %.3f), auc)); grid on;perfcurve的输入是二值标签、得分和正类标号。AUC 在 0.5 附近说明模型和随机猜差不多0.9 以上才算有实用价值0.99 以上往往意味着特征选得过于简单或者数据存在泄漏。多分类时可以对每个类别单独做一次“该类 vs 其余类”的 ROC画到同一张图里对比各类别的可分性。5.3 别把 score 当概率用概率校准与 posterior 拟合SVM 输出的得分是样本到边界的距离数值范围跟核函数、数据尺度都有关系不能直接当概率解释。如果你需要输出“这个样本有 70% 的把握属于 A 类”必须做概率校准。MATLAB 官方做法是fitPosterior。mdl_calibrated fitPosterior(mdl, X_train_std, Y_train); [~, postProb] predict(mdl_calibrated, X_test_std); fprintf(样本 1 各类别预测概率: %.3f, %.3f\n, ... postProb(1, 1), postProb(1, 2));校准过程在训练集上用交叉验证拟合一个概率映射函数把得分映射到[0,1]区间。注意fitPosterior只支持二分类模型多分类模型要做概率输出需要借助fitcecoc配合专门的得分转换MATLAB 文档里叫FitPosterior, true选项。概率值还有一个附加用途把置信度低于某个阈值的样本标记为“待人工复核”而不是直接交给下游系统。6. 交叉验证、自动调参与模型保存SVM 分类的收尾技巧6.1 用 crossval 替代单次划分让评价指标更稳单次划分的测试集准确率波动很大换一个随机种子结果可能差两个百分点。对最终模型做 5 折交叉验证取平均损失和标准差比单次测试集评估靠谱得多。cvmdl crossval(mdl, KFold, 5); loss kfoldLoss(cvmdl); acc 1 - loss; fprintf(交叉验证准确率: %.3f (标准差参考 kfoldLoss 输出)\n, acc);交叉验证的另一个用途是画学习曲线观察训练集大小与验证准确率的关系判断当前瓶颈是数据量不足还是模型容量不够。6.2 用 OptimizeHyperparameters 自动搜索参数范围手动网格搜索对两个参数还算可行参数多了以后效率太低。fitcsvm内置了贝叶斯优化自动搜索关键参数的最优组合。mdl_auto fitcsvm(X2, Y2, ... OptimizeHyperparameters, auto, ... HyperparameterOptimizationOptions, struct( ... AcquisitionFunctionName, expected-improvement-plus, ... MaxObjectiveEvaluations, 30, ... Kfold, 5, ... ShowPlots, true));自动调参过程会实时画一张目标函数与参数组合的关系图收敛速度快但要注意它默认的搜索范围不一定适配你的数据。复杂场景下我会先跑一次自动优化拿到一个参数基准再在基准附近手动微调把调参速度和质量结合起来。6.3 saveCompactModel 保存与加载预测模型模型训练完、指标验证通过后要把模型保存下来供后续部署使用。saveCompactModel专门用于保存不含训练数据的精简模型文件体积更小适合交付。saveCompactModel(mdl, svm_iris_model.mat); % 新环境加载直接调用 mdl_loaded loadCompactModel(svm_iris_model.mat); [idxNew, scoreNew] predict(mdl_loaded, newData);加载后的模型和训练时的用法完全一致。注意loadCompactModel加载的模型不能再用于fitPosterior或crossval之类的训练操作需要这些功能时用load加载完整模型。部署到生产环境时把标准化参数mu、sigma连同模型一起保存新数据进模型之前先做同样的标准化变换这一步漏掉会让线上预测结果完全失真。本文还有配套的精品资源点击获取
返回列表