简介:支持向量机(SVM)案例练习资源,面向机器学习初学者、课程设计学生与希望提升分类回归建模能力的研究者。资源以Matlab源码为核心,覆盖SVM在葡萄酒种类识别、上证开盘指数预测、参数优化及信息粒化时序回归等典型场景中的应用,并配有相应数据文件与图文说明,便于对照实验结果理解算法细节。包内含76个文件,主要包括8个m源码、5个mat数据集、7个html辅助说明、55个png运行结果图及1个tif底图,压缩包大小约908KB,png图直观呈现分类边界与预测拟合结果,目录按案例编号组织,方便按需查阅。案例覆盖SVM分类、SVR回归、网格搜索与粒子群参数寻优等内容,源码可直接运行调试,适合通过动手实践掌握核函数选择、C与gamma参数调优等关键技能。目前已有2604人学习下载,借助支持向量分析、结果可视化等环节,可进一步深化对SVM原理与工程化应用的直观认识。
1. SVM 案例拆解:从分类到回归,一套源码包能挖出多少东西
手头这套 SVM 案例练习的 MATLAB 源码包,包含案例 12 到案例 15 四个完整工程:葡萄酒种类识别、SVM 参数优化(GA/PSO/网格搜索)、上证开盘指数回归预测,以及信息粒化时序回归预测。用一句话概括它的价值:它把 SVM 从「调包调参」拉回到了「理解模型」的层面。案例 12 让你看清支持向量怎么构造决策边界,案例 13 逼你思考 C 和 γ 到底在控制什么,案例 14 和 15 则把问题引向回归和时间序列。适合正在做课设、准备算法面试,或者想从分类器跳到回归场景的读者。本文不逐行讲源码,而是把这四个案例串成一条「理论→调优→回归→粒化」的技术链路,每一步都可复现。
2. SVM 的技术骨架:间隔、核函数与 SVR 损失
在动手跑案例之前,建议先把 SVM 的三个核心部件拆开:最大间隔分类器、核技巧、以及从分类到回归的损失函数变化。这三个概念直接决定了你后面读chapter12.m到chapter15.m时,能不能看懂每一行在干什么。
2.1 最大间隔:为什么支持向量比决策面更重要
SVM 分类的核心不是「找一个能把两类分开的线」,而是「找一个离所有样本都尽可能远的线」。这个距离叫间隔,而落在间隔边界上的那少数几个样本,就是支持向量。案例 12 的葡萄酒分类里,三类样本在特征空间中的分布存在交叠,线性超平面无法直接切分,所以代码会先做归一化,再通过核函数把样本映射到高维空间,在高维空间里重新计算最大间隔超平面。
这里有一个新手容易忽略的点:决策面的位置其实只由支持向量决定,其他远离边界的样本即使删掉,模型也不变。这意味着训练完成后,你可以直接查看fitcsvm返回的模型对象里的SupportVectors属性,统计支持向量占训练集的比例。如果这个比例超过 30%,模型大概率处于过拟合状态——它需要大量样本才能撑住边界,泛化能力会打折扣。
2.2 核函数的选型逻辑与参数含义
案例 12 和 13 默认给的是 RBF 核(高斯核),这是一个合理默认值,但不代表永远最优。核函数的本质是定义样本间内积的度量方式,不同的核对应不同的相似度假设:
| 核函数 | 表达式 | 适用场景 | 需要关注的参数 |
|---|---|---|---|
| 线性核 | K(xi, xj) = xi·xj | 文本分类、特征维度极高 | C |
| 多项式核 | K(xi, xj) = (γ·xi·xj + r)^d | 图像、有先验的交互特征 | γ、r、d |
| RBF 核 | K(xi, xj) = exp(-γ‖xi-xj‖²) | 默认首选、非线性边界 | C、γ |
| Sigmoid 核 | K(xi, xj) = tanh(γ·xi·xj + r) | 部分神经网络启发场景 | γ、r |
RBF 核之所以成为默认,是因为它只有一个宽度参数 γ,且对应的特征映射是无穷维的,表达能力足够覆盖大多数非线性边界。但 γ 的设定直接影响间隔的形状:γ 越大,每个样本的影响范围越小,边界越曲折,容易过拟合;γ 越小,决策面越平滑,容易欠拟合。
2.3 从分类到回归:SVR 的 ε 不敏感损失
案例 14 和 15 用的是 SVR(支持向量回归),和分类最大的区别在损失函数。分类 SVM 追求的是「最大间隔」,而 SVR 追求的是「让预测值落在真实值周围一个 ε 管道内」。也就是说,只要误差小于 ε,就不计损失;误差超出 ε 的部分才进入优化目标。这个 ε 相当于你允许模型犯多大的错,案例 14 里通常把它设为训练集标准差的某个比例,比如 0.01 到 0.1 之间。
SVR 的目标函数可以理解为在「拟合精度」和「模型平坦度」之间做权衡,C 参数控制的就是这个权衡的权重。C 越大,模型越倾向于把训练样本都塞进 ε 管道内,代价是决策函数更陡峭;C 越小,模型越倾向于保持平坦,但训练误差会变大。
2.4 一段可直接跑的 MATLAB 训练骨架
无论哪个案例,训练过程的骨架是通用的。以 RBF 核的 SVR 为例:
% 加载数据并划分训练/测试集 load('chapter14_sh.mat'); % 假设包含上证开盘指数序列 X = ...; % 特征矩阵,(N, D) 每行一个样本 Y = ...; % 目标值,(N, 1) % 训练前必须做归一化,SVM 对特征尺度敏感 [X_train, mu, sigma] = zscore(X_train); X_test = (X_test - mu) ./ sigma; % 训练 SVR,核函数为 RBF,设置盒约束 C 和 epsilon mdl = fitrsvm(X_train, Y_train, ... 'KernelFunction', 'rbf', ... 'BoxConstraint', 1, ... 'Epsilon', 0.05, ... 'KernelScale', 'auto', ... 'Standardize', false); % 数据已手动归一化,这里不再标准化 % 预测并计算误差 Y_pred = predict(mdl, X_test); mse = mean((Y_test - Y_pred).^2); mae = mean(abs(Y_test - Y_pred));BoxConstraint就是 C 的别名,控制误分类或超差样本的惩罚力度;KernelScale对应 RBF 核的 γ,'auto'表示用启发式规则自动初始化一个值,但后续还是要靠案例 13 里的调参手段进一步搜索。Standardize这个选项要注意,如果前面已经手动zscore,这里要设为false,否则会重复标准化导致信息丢失。
3. 案例 12 与案例 13:分类预测与参数优化的三种姿势
3.1 案例 12:葡萄酒种类识别中的 SVM 分类流程
chapter12.m使用的数据集是chapter12_wine.mat,这也是 UCI 上经典的三分类葡萄酒数据集,178 个样本、13 个特征、3 个类别。整套流程分四步:加载数据、划分训练集测试集、训练分类器、评估混淆矩阵。
3.1.1 数据划分与预处理细节
load('chapter12_wine.mat'); % 变量通常为 wine_data, wine_labels % 划分训练集与测试集,常见做法是 7:3 划分 rng(42); % 固定随机种子,保证结果可复现 idx = randperm(size(wine_data, 1)); train_idx = idx(1:round(0.7 * length(idx))); test_idx = idx(round(0.7 * length(idx)) + 1:end); X_train = wine_data(train_idx, :); X_test = wine_data(test_idx, :); Y_train = wine_labels(train_idx, :); Y_test = wine_labels(test_idx, :); % 特征缩放:SVM 对量纲敏感,13 个特征的量纲差异很大 [X_train, mu, sigma] = zscore(X_train); X_test = (X_test - mu) ./ sigma;为什么要固定随机种子?因为 SVM 的分类结果依赖训练集的构造,换一组样本结果可能从 98% 掉到 95%,这种波动在调参时容易被误判成参数优劣。zscore先计算训练集的均值和标准差,再用同一组参数去标准化测试集,这是防止数据泄露的标准操作。很多初学版本会直接把整个数据集zscore再划分,这样测试集的信息已经混进了训练过程,评估结果会偏乐观。
3.1.2 训练分类器与评估
% 多分类问题:fitcsvm 默认是二分类,多分类用模板 + fitcecoc template = templateSVM('KernelFunction', 'rbf', ... 'BoxConstraint', 1, 'KernelScale', 'auto'); mdl = fitcecoc(X_train, Y_train, 'Learners', template); % 预测与混淆矩阵 Y_pred = predict(mdl, X_test); C = confusionmat(Y_test, Y_pred); accuracy = sum(diag(C)) / sum(C(:)); fprintf('准确率: %.2f%%\n', accuracy * 100);fitcecoc是 Error-Correcting Output Codes 的缩写,把多分类拆成多个二分类子问题,内部对每个子问题训练一个 SVM 分类器,最后通过投票或纠错码决定最终类别。葡萄酒数据集只有 3 类,一对一策略(one-vs-one)就够了,fitcecoc会自动选择编码设计。运行这段代码后,再把测试集样本逐个可视化到前两个主成分上,对照支持向量的位置,能看到决策边界附近的样本才是真正影响模型的关键点。
3.2 案例 13:网格搜索、PSO 与 GA 调参对比
案例 13 的标题点明了核心矛盾:同样的数据集,C 和 γ 不同,分类准确率可以从 82% 波动到 99%。chapter13_GridSearch.m、chapter13_PSO.m、chapter13_GA.m三个脚本分别实现了三种参数搜索策略。理解它们的区别,比会调包更重要。
3.2.1 网格搜索:暴力但可靠
% 网格搜索:在 C 和 gamma 的对数网格上穷举 C_range = 2.^(-5:15); % 32 个取值 gamma_range = 2.^(-15:3); % 19 个取值 best_acc = 0; best_C = C_range(1); best_gamma = gamma_range(1); for i = 1:length(C_range) for j = 1:length(gamma_range) template = templateSVM('KernelFunction', 'rbf', ... 'BoxConstraint', C_range(i), 'KernelScale', 1/sqrt(2*gamma_range(j))); mdl = fitcecoc(X_train, Y_train, 'Learners', template, ... 'CrossVal', 'on', 'KFold', 5); acc = 1 - kfoldLoss(mdl, 'LossFun', 'ClassifError'); if acc > best_acc best_acc = acc; best_C = C_range(i); best_gamma = gamma_range(j); end end end注意代码里KernelScale和 γ 的关系:MATLAB 中'KernelScale'参数是 RBF 核的宽度 σ,而 γ = 1/(2σ²)。直接用KernelScale设置为1/sqrt(2*gamma)就能精确对应到论文公式。5 折交叉验证在 608 个组合上训练,一次实验大约几分钟,对 178 个样本的小数据集完全可接受。
3.2.2 PSO 与 GA:把调参当成优化问题
粒子群和遗传算法的思路一致:把(C, γ)编码成一个二维向量,用交叉验证准确率作为适应度函数,迭代搜索最优解。差别在于 PSO 靠粒子的速度与个体/群体最优位置更新,收敛快但容易陷入局部最优;GA 靠选择、交叉、变异,全局搜索能力更强但收敛慢。
下面是一个 PSO 调参的核心片段:
% 粒子群搜索 C 和 gamma,目标函数是 5 折交叉验证误差 nvars = 2; lb = [0.01, 0.001]; % C 和 gamma 下界 ub = [100, 10]; % 上界 fun = @(x) svm_cv_error(x, X_train, Y_train); [best_x, best_err] = particleswarm(fun, nvars, lb, ub); best_C = best_x(1); best_gamma = best_x(2);function err = svm_cv_error(x, X_train, Y_train) C = x(1); gamma = x(2); template = templateSVM('KernelFunction', 'rbf', ... 'BoxConstraint', C, 'KernelScale', 1/sqrt(2*gamma)); mdl = fitcecoc(X_train, Y_train, 'Learners', template, ... 'CrossVal', 'on', 'KFold', 5); err = kfoldLoss(mdl, 'LossFun', 'ClassifError'); end用 PSO 搜索时有一个坑:粒子群在搜索空间边缘时会随机生成KernelScale的极值,如果 γ 取得过大,RBF 核的核矩阵对角线占优,交叉验证准确率会直接崩到 33%(三分类随机猜测水平)。所以一般把 γ 的上界设为 10,而不是更大的值。
3.2.3 三种调参策略对比
| 调参方法 | 搜索方式 | 速度 | 适用场景 | 风险 |
|---|---|---|---|---|
| 网格搜索 | 穷举对数网格 | 慢但可控 | 参数范围明确、样本量小 | 维度爆炸 |
| 粒子群 PSO | 群体智能逼近 | 快,通常几十次迭代收敛 | 连续参数空间 | 容易陷入局部最优 |
| 遗传算法 GA | 选择交叉变异 | 最慢 | 参数空间复杂、非线性相关强 | 参数编码敏感 |
我的建议是先用网格搜索画一张C-γ热力图,找到准确率峰值所在的区域,再用 PSO 在那片区域精搜。案例 13 的魅力恰恰在于它同时提供了这三种实现,方便你直接对比同一数据集上三种策略的收敛轨迹和最终精度差异。
4. 案例 14 与案例 15:上证指数回归预测与信息粒化
4.1 案例 14:SVR 对开盘指数的滚动预测
chapter14.m用上证开盘指数历史序列做回归预测,注释里写的是「SVM 神经网络的回归预测」,本质上就是 SVR。金融时序预测和分类问题有两个显著差异:一是数据是自相关的,不能随意打乱顺序;二是样本量通常不大,特征构造方式对结果影响巨大。
4.1.1 特征组织:滑动窗口法
一个常见做法是用前lookback天的开盘价预测第lookback+1天的开盘价,窗口长度为 5 或 10:
lookback = 5; X = []; Y = []; for t = lookback+1 : length(price) X = [X; price(t-lookback:t-1)']; Y = [Y; price(t)]; end % 前 80% 做训练,后 20% 做测试,注意保持时间顺序 train_num = round(0.8 * size(X, 1)); X_train = X(1:train_num, :); Y_train = Y(1:train_num, :); X_test = X(train_num+1:end, :); Y_test = Y(train_num+1:end, :);滑动窗口的窗口长度lookback是时序预测里最关键的决策之一。窗口太短,模型看不到趋势;窗口太长,噪声累积反而降低精度。案例 14 里注释提示使用的窗口在 5 到 10 之间,这个范围对日频数据是合理的起点。你也可以做一个快速实验:分别用 3、5、10、20 做窗口,把测试集上的 MSE 画成曲线,找一个拐点。
金融时序的另一个坑是平稳性。上证开盘指数在 3000 点到 5000 点之间波动时,绝对数值对模型影响很大。常见处理是先对序列取对数差分,把价格序列转成收益率序列,再训练 SVR;预测出收益率后还原成价格。案例 14 没有默认做这一步,但你在复现时如果发现fitrsvm的拟合误差很大,优先检查这里。
4.1.2 回归评估:连续值没有准确率
分类问题看准确率,回归问题看误差。SVR 回归常用的三个指标:
Y_pred = predict(mdl, X_test); mse = mean((Y_test - Y_pred).^2); % 均方误差,大误差被放大 mae = mean(abs(Y_test - Y_pred)); % 平均绝对误差,单位与数据一致 r2 = 1 - sum((Y_test - Y_pred).^2) / sum((Y_test - mean(Y_test)).^2); % R²MSE 对大误差敏感,如果你的预测偶尔偏离几百点,MSE 会被这几个离群样本主导;MAE 则更稳健。R² 的解释力最直观——它表示模型解释了测试集方差的百分比,接近 1 说明预测曲线与真实曲线贴合度很高,负值说明模型比直接猜均值还差。时间序列预测的 R² 通常不会太高,尤其在金融数据上,0.6 以上已经是可用的模型。
4.2 案例 15:信息粒化与三通道时序回归
chapter15.m是整套源码包里最有深度的一个案例。它的名字叫「信息粒化时序回归预测」,目标是预测的不是一个数值,而是一段区间。这里的思路来自模糊信息粒化(Fuzzy Information Granulation):把时间序列按窗口划分成若干「粒」,每个粒用三个特征描述——low(下界)、R(中值代表)、up(上界),然后分别对三个特征训练 SVR 模型。
4.2.1 粒化窗口数据结构
% FIG_D.m 实现的信息粒化,窗口宽度为 w w = 10; n = length(series); num_granules = floor(n / w); low = zeros(num_granules, 1); R = zeros(num_granules, 1); up = zeros(num_granules, 1); for i = 1:num_granules segment = series((i-1)*w + 1 : i*w); % 每个窗口内的最小值和最大值 low(i) = min(segment); up(i) = max(segment); % 中值或均值作为代表值 R(i) = median(segment); end这里的关键是理解low、R、up不是独立的三个预测任务,它们共享同一个时间轴。训练时用第 i 个粒的三个值作为目标,用之前若干个粒的特征做输入,预测下一时刻的(low, R, up)。把三个通道的预测结果画在同一张图上,就能得到一条带上下界的预测带,比单点预测多了一个不确定性维度。
4.2.2 为什么用粒化而不是直接预测日收益率
单点回归预测在金融数据上几乎不可能稳定,因为噪声比信号强。粒化相当于做了一个窗口级别的降噪,每个窗口内的极值和中值把随机波动磨平了,SVR 学的是「未来一段时间的大致波动范围」而不是「明天准确到小数点的价格」。这在实操层面更有意义:一个区间包含真实值的概率,远比单点预测命中真实值的概率有参考价值。案例 15 把这个问题落到了代码层面,FIG_D.m负责粒化画图,chapter15.m负责预测流程,两部分拼起来就构成一个完整的区间预测工具。
5. 验证 SVM 模型的三个实用技巧
调参、训练、预测做完,最后一步是确认模型真的可靠。不看测试集精度之外的东西,很容易被漂亮的准确率数字误导。
先看支持向量的比例。训练完成后用mdl.SupportVectors的数量除以训练样本数,如果这个比例高于 30%,说明模型依赖大量边界样本才能维持间隔,本质上是过拟合。案例 12 的葡萄酒分类在默认参数下支持向量比例大约是 15% 左右,如果你调参后看到接近 40%,C 大概率设得太大了。
再看核宽度的合理性。RBF 核的 γ 如果远大于训练样本间距离的倒数,决策面会高频率振荡,表现为训练集上准确率接近 100% 而测试集掉到 70% 左右。快速验证法:把预测置信度排序,画出测试样本到决策面的距离分布,如果大量样本集中在超平面附近,说明边界太窄,模型对噪声敏感。
最后回归任务里养成画残差排序图的习惯。对案例 14 的 SVR 预测结果,把残差按时间顺序画出来,如果看到残差在某个时间段系统性为正或为负,说明模型没捕捉到该时间段内的趋势变化,这通常是因为滑动窗口太短,只学了局部波动而丢了中期趋势。此时应该加长lookback或对序列做差分后再训练。这三招配合交叉验证,才算是把一套 SVM 案例完整跑透了。
本文还有配套的精品资源,点击获取