简介:这份代码基于支持向量机(SVM)算法,用于数据回归预测,采用Scikit-learn库实现支持向量回归模型,并借助Matplotlib完成结果可视化。资源面向机器学习初学者、数据科学从业者,以及需要快速搭建回归预测流程的开发者,可帮助理解SVM回归原理与sklearn标准建模调用方式。压缩包共6个文件,其中包含1个可直接运行的svm.py脚本、2个Excel数据文件(训练集与测试集)、2张预测结果对比图以及1份README说明文档,整体仅188KB,结构清晰、轻量易用。项目基于波士顿房屋数据集(涵盖犯罪率、房间数、师生比例等典型特征),完整演示数据加载、模型训练、预测及训练/测试均方误差(MSE)评估流程,并输出实际值与预测值的散点对比和双线对比图,便于直观评估模型拟合效果。目前已有3467人学习下载,适合作为SVM入门练习、课程实验或毕业设计参考。
1. 支持向量机做数据回归预测:一份能直接跑通的 sklearn 实战代码
支持向量机(SVM)在大多数教程里都被贴上「分类器」的标签,但真正把它用在数据回归预测上的人,多半是被线性回归在小样本数据集上的表现逼过去的。这份资源的核心是一个 svm.py 脚本:基于 sklearn 的 SVR 实现在波士顿房屋数据集上做回归预测,用 Matplotlib 画出预测值与实际值的对比图,并输出训练集和测试集的 MSE。
项目包里除了主脚本,还有两份已划分好的 Excel 数据、两张运行生成的对比图以及 README。适合谁?手里数据量不大、特征维度不算低、又不想一上来就堆深度模型的从业者——你需要一个能快速出基线的回归方案,同时还想看清预测值和真实值到底差在哪。这篇笔记就把脚本逐段拆开,把参数和踩过的坑一次说清。
2. 从分类到回归:SVR 的原理与 C、gamma、epsilon 选型
2.1 SVR 的本质:误差带内不计损失
分类 SVM 的目标是找一条最大间隔超平面,让两类样本分得越开越好。回归任务没有「类别」这个概念,SVR(Support Vector Regression)的思路换成了另一套:在目标值附近画一条「管道」,管道半径是 epsilon,落在管道内的样本点不计损失,只有跑出管道的点才按超出距离计算误差。换句话说,SVR 不追求每个点的预测值都踩在真实值上,而是追求「绝大多数点都落在误差带里」。
这个设计对回归预测非常实用。真实业务数据里总有个别异常值,普通回归会被这些点拉偏,SVR 因为对管道内误差不敏感,天然具备一定的抗噪能力。小样本场景下这个优势尤其明显——样本本来就不多,如果模型把所有噪声都记住,测试集上大概率崩盘。管道机制相当于给模型加了一层「容差保护」,让它把精力花在拟合整体趋势上。
分类 SVM 有硬间隔和软间隔之分,回归 SVR 里对应的是 C 参数。硬间隔要求所有点严格在管道内,软间隔允许部分点突破管道,突破得越过分的点受到的惩罚越大。C 就是那个惩罚强度:C 大,模型更偏向把训练点全部包住,容易过拟合;C 小,模型更平滑,但也可能欠拟合。调参的时候 C 永远和 epsilon、gamma 联动着看,不能孤立的调某一个。
2.2 三个核心超参数各自管什么
| 参数 | 默认值 | 作用 | 调大的影响 | 调小的影响 |
|---|---|---|---|---|
| C | 1.0 | 对超出误差带样本的惩罚强度 | 训练集拟合更好,容易过拟合 | 模型更平滑,可能欠拟合 |
| epsilon | 0.1 | 误差带宽度 | 容差大,曲线更平缓 | 容差小,曲线更曲折 |
| gamma | scale | RBF 核的单个样本影响半径 | 影响范围缩小,容易记住噪声 | 影响范围扩大,曲线更光滑 |
gamma 是 RBF 核特有的参数,控制的是「一个训练样本能影响多远」。gamma 越大,每个样本的影响范围越小,决策边界越复杂;gamma 越小,影响半径越大,边界越平滑。sklearn 里的默认值 scale 会自动取 1 / 特征数,这个默认值在多数场景下是个不错的起点,但换数据集后最好还是手动搜一遍。
epsilon 是最容易被忽略的参数。很多时候只调 C 和 gamma,epsilon 留在 0.1,但如果你发现训练集 MSE 很低、测试集 MSE 很难看,先检查 epsilon 是不是被设成了 0。epsilon 设 0 等于要求每个点都进误差带,模型会拼命弯曲去追个别点,过拟合几乎是必然的。
提示:sklearn 的 SVR 接口里 epsilon 的单位和预测目标的量纲一致。波士顿房价是千美元量级,epsilon=0.1 表示允许 0.1 千美元(即 100 美元)的误差。换粗量纲的数据时,这个值要跟着放大。
2.3 为什么直接调 sklearn 而不是手写梯度下降
网上很多讲 SVM 原理的文章都会手推梯度下降,硬间隔 SVM 的梯度下降推导、软间隔的拉格朗日求解,这些都是理解模型的必经之路。但工程上做数据回归预测,我一般直接调 sklearn 的 SVR 类,原因很现实:sklearn 底层用的是 libsvm 的成熟求解器,数值稳定性和收敛速度远好于自己写的梯度下降。手写实现适合跑通原理,不适合拿来出业务结果。
另外一个常被忽略的点:SVR 训练完成后,模型只保留支持向量,其余样本不参与预测。这意味着推理时模型体积受支持向量数量控制,而不是全体训练样本。小样本场景下这个特性很友好,模型不会因为训练数据稍多就膨胀。
选择 sklearn 的 SVR 还有一层原因:Pipeline 和 GridSearchCV 是 sklearn 的生态标配,标准化、调参、交叉验证可以串成一条流水线。这套东西在回归预测里几乎是标配工作流,手写实现很难对接上。
2.4 回归评价指标:MSE 够用,但要懂量纲
这份资源里用 mean_squared_error 作为评价指标,训练集和测试集各输出一个 MSE。对起步阶段的回归预测来说,MSE 够用,但读它的时候要时刻记住量纲:MSE 是误差平方的平均值,波士顿房价以千美元为单位,MSE 是几十的话,换算成房价误差要看 RMSE(均方根误差)。比如 MSE=25,开根号后约等于 5,也就是平均误差大约 5 千美元,比直接看 MSE 直观得多。
如果有余力,建议同时看 R2 分数和 MAE。R2 告诉你模型解释了目标变量多少比例的方差,MAE 告诉你平均绝对偏差。MSE 对离群点敏感,MAE 更稳健,两者结合才能判断模型是被少数大误差点带崩了,还是普遍性地预测偏差。这份 svm.py 只输出 MSE,复现之后自己加两行打印 R2 和 MAE,成本很低,收益很大。
3. 拆解 svm.py:数据加载、标准化、训练与可视化一条龙
3.1 资源包里的文件结构
拿到压缩包解压后,目录里有六个文件,每个文件干一件事,结构很清爽:
| 文件 | 作用 |
|---|---|
| svm.py | 主脚本,完成数据加载、标准化、训练、评估和绘图 |
| boston_housing_train_data.xlsx | 训练集,包含 13 个特征和 1 个目标列 |
| boston_housing_test_data.xlsx | 测试集,结构和训练集一致 |
| Figure_1.png | 运行后生成的散点对比图 |
| Figure_2.png | 运行后生成的双折线对比图 |
| README.md | 使用说明和运行方式 |
数据已经被拆成 train 和 test 两份 Excel,不用自己再做随机划分,省了一步。脚本运行方式是python svm.py,前提是环境里装好 pandas、scikit-learn、matplotlib 三个库。README 里应该有详细的运行说明,照做就行。
3.2 从 Excel 读入数据并核对形状
第一步是把两份 Excel 读进来,拆出特征矩阵和目标向量。波士顿数据集的惯例是前 13 列是特征,最后一列 MEDV 是房价中位数。
import pandas as pd train_df = pd.read_excel("boston_housing_train_data.xlsx") test_df = pd.read_excel("boston_housing_test_data.xlsx") # 前 13 列是特征,最后一列是目标 MEDV X_train = train_df.iloc[:, :-1].values y_train = train_df.iloc[:, -1].values X_test = test_df.iloc[:, :-1].values y_test = test_df.iloc[:, -1].values print("训练集特征形状:", X_train.shape) print("测试集特征形状:", X_test.shape)iloc[:, :-1]取所有行、除最后一列外的所有列,iloc[:, -1]取最后一列。用.values转成 numpy 数组是因为 sklearn 的 SVR 接口直接吃数组,传 DataFrame 虽然也能跑,但转成数组后后续操作更干净。
打印 shape 这一步别省。我见过不止一次因为 Excel 里多了一列索引序号,导致特征维度变成 14 列,模型训练完测试集预测结果全偏的情况。先确认训练集和测试集列数一致,再往下走。
3.3 标准化:SVR 不标准化等于白调参
SVR 的核函数计算依赖样本间的距离,如果特征量纲不统一——犯罪率是零点几、房间数是几、师生比例是十几——距离计算会被大数值特征主导,小数值特征的信息基本被淹没。所以标准化是必须步骤。
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() scaler.fit(X_train) # 只用训练集统计量 X_train_scaled = scaler.transform(X_train) X_test_scaled = scaler.transform(X_test)fit计算训练集每列的均值和标准差,transform对数据做 (x - mean) / std 变换。关键点在于fit只能用训练集,不能把测试集一起塞进去。如果让 scaler 提前看到了测试集的均值和标准差,测试结果会偏乐观,这属于数据泄露。
标准化做完,训练集均值为 0、方差为 1,测试集按同一套统计量变换,这样训练和预测时的特征空间才是一致的。
3.4 训练 SVR 并输出训练集和测试集 MSE
模型训练这段代码是整个脚本的核心,注意参数先按默认值跑一遍,拿到基线再调。
from sklearn.svm import SVR from sklearn.metrics import mean_squared_error regressor = SVR(kernel="rbf", C=1.0, epsilon=0.1, gamma="scale") regressor.fit(X_train_scaled, y_train) y_train_pred = regressor.predict(X_train_scaled) y_test_pred = regressor.predict(X_test_scaled) train_mse = mean_squared_error(y_train, y_train_pred) test_mse = mean_squared_error(y_test, y_test_pred) print("Train MSE:", train_mse) print("Test MSE:", test_mse)kernel="rbf"是默认核,适合特征和目标关系非线性的场景。gamma="scale"表示 sklearn 自动取 1 / 特征数,这个值在特征较多时比较保守,能避免一开始就过拟合。C 和 epsilon 先用默认值,目的是先确认整个流程能跑通,再谈调优。
fit执行的是 libsvm 的求解过程,训练完成后模型内部保存了支持向量和对应系数。predict直接基于支持向量计算输出,所以 SVR 的推理速度只和支持向量数量有关,和支持向量机的训练数据总量关系不大。MSE 输出在终端里,两个值会同时打印,训练集 MSE 远小于测试集 MSE 是默认参数下的常见状态,别急着怀疑代码写错。
3.5 绘制散点对比图:Figure_1.png
第一张图把测试集的真实房价和预测房价画成散点,同时画一条 y=x 的参考线。
import matplotlib.pyplot as plt plt.figure(figsize=(6, 6)) plt.scatter(y_test, y_test_pred, alpha=0.6, edgecolors="k") plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], "r--", linewidth=2) plt.xlabel("Actual MEDV") plt.ylabel("Predicted MEDV") plt.title("Test Set: Actual vs Predicted") plt.savefig("Figure_1.png", dpi=150) plt.show()alpha=0.6是散点透明度,样本重叠多的时候能看出来密度分布。红色虚线是参考线,理想状态下所有散点都落在虚线上。savefig会把图存到当前目录,dpi=150保证清晰度够用。
3.6 绘制双折线图:Figure_2.png
第二张图把测试集所有样本按序号排列,真实值和预测值画成两条折线,方便逐点对比。
plt.figure(figsize=(10, 5)) plt.plot(y_test, label="Actual", marker="o", linestyle="-") plt.plot(y_test_pred, label="Predicted", marker="x", linestyle="--") plt.xlabel("Sample Index") plt.ylabel("MEDV") plt.legend() plt.title("Test Set: Actual and Predicted Lines") plt.savefig("Figure_2.png", dpi=150) plt.show()折线图的好处是能看出模型在哪些区段跟得紧、哪些区段跟不上。marker="o"和marker="x"给每条线加了不同标记,打印成黑白文档时也能区分两条线。如果想保存图片而不弹窗,可以把plt.show()注释掉,服务器环境跑脚本时不会报错。
注意:
savefig要在show之前执行。如果先show,在某些环境下会阻塞脚本直到窗口关闭,后续代码不会继续跑。
4. 在波士顿房价数据上复现:MSE 数值与两张对比图的读法
4.1 波士顿数据集的特征含义
波士顿房屋数据集来自美国 1970 年代的人口普查数据,预测目标是以千美元计的业主自住房屋中位价 MEDV。13 个特征里常见的几个:CRIM 是城镇人均犯罪率,RM 是每套住宅的平均房间数,PTRATIO 是师生比例,还有一氧化氮浓度、到就业中心距离等。这个数据集特征之间量纲差异很大,正好是演示标准化的好例子。
训练集和测试集已经被切成两个 Excel 文件,训练集用于拟合模型,测试集用于评估泛化能力。直接跑 svm.py 会从这两个文件分别读数据,不需要自己写 train_test_split。
4.2 运行结果预期判断
脚本跑完后终端会输出两行 MSE。第一次跑默认参数,训练集 MSE 会比较低,测试集 MSE 明显高于训练集——这是正常现象,说明模型有一定过拟合,但也说明模型确实学到了训练数据里的模式。
重点不是盯训练集 MSE,而是看训练集和测试集的差距。两者接近,说明模型泛化还行;差距很大,说明模型把训练数据背下来了。如果测试集 MSE 比乱猜还差,先检查是不是标准化步骤出了问题,或者两份 Excel 的列顺序不一致。代码本身能跑通并不代表结果是对的,结果合理性得靠对业务的判断。
4.3 Figure_1.png 散点图怎么读
散点图的读法很简单:点越贴红虚线,预测越准。看三个地方。第一,散点整体是否沿着对角线分布,如果整体偏向一侧,说明预测有系统性偏差;第二,散点云的宽度,越窄越好,宽意味着预测误差大;第三,有没有离群点,特别是右上角的高价房区域。
波士顿房价有个特点:MEDV 大于 50 的样本会被截断,也就是当年的数据收集上限是 50。高价房的实际值集中在 50 附近,模型预测这些样本时往往会偏低,散点图右上角会出现一段水平分布。这是数据集本身的特性,不是模型 bug。
4.4 Figure_2.png 双折线怎么读
双折线图按样本序号把真实值和预测值画在一起。重叠度高说明模型跟得紧,错位大说明误差大。重点看两个位置:一是房价低谷区,模型是否也预测到低谷;二是房价突变的样本,模型预测是否滞后或超前。
如果预测线在某些样本上剧烈抖动,多半是 gamma 调大了或者 epsilon 设小了。如果预测线整体比实际线「平」很多,说明模型把房价拉向均值,这在高价区和低价区表现得最明显。这两张图配合着看,散点图看整体分布,折线图看逐样本趋势,基本就能定位模型的问题出在哪。
5. 参数调优与避坑:C、epsilon、gamma 选不对模型就翻车
5.1 五个高频踩坑记录
坑一:不标准化直接训练,MSE 大得离谱
现象:同样的特征和数据,不缩放直接训练,测试集预测结果几乎是一条水平线,MSE 比使用标准化后高出几个数量级。
原因:SVR 的 RBF 核计算依赖样本欧氏距离。犯罪率 0.01 和房间数 6.5 在距离计算里贡献完全不平等,大数值特征完全主导了核函数值,小数值特征的信息被淹没。
解决:任何 SVM/SVR 场景都先做标准化。用 StandardScaler 在训练集上 fit,再 transform 训练集和测试集。记住一个习惯:scaling 是 SVR 流水线里不可省略的环节,和数据集规模无关。
坑二:标准化时把测试集一起 fit 了,结果虚高
现象:测试 MSE 比预期好很多,但换一份新数据预测立刻打回原形。
原因:scaler.fit(X_train + X_test)让 scaler 提前统计了测试集的均值和方差,测试集的信息在训练阶段就被模型间接看到了。这是一种隐蔽的数据泄露,结果好看但不可信。
解决:标准化严格要求只在训练集上 fit,测试集只做 transform。代码里写scaler.fit(X_train)而不是scaler.fit(X)。交叉验证时用 Pipeline 把 scaler 和 SVR 串起来,防止手动顺序搞错。
坑三:epsilon 设成 0,训练集漂亮测试集崩
现象:训练集 MSE 接近 0,测试集 MSE 比默认参数下差好几倍,预测曲线在折线图里呈锯齿状。
原因:epsilon=0 意味着误差带没有宽度,每个训练样本都必须被模型精确拟合,模型被迫在很多局部区域急剧弯曲,把噪声当成了信号。
解决:epsilon 不要设 0。从 0.1 起步,如果数据目标值范围大,按目标标准差的 5%~10% 设定。预测结果太粗糙就调小 epsilon,太曲折就调大 epsilon。
坑四:gamma 盲目调到 1 以上,过拟合到怀疑人生
现象:训练集 MSE 很低,测试集 MSE 很高,折线图预测值像心电图。
原因:gamma 太大时,RBF 核的影响半径收缩到单样本附近,模型记住了每一个训练点的具体位置,对新样本的泛化能力几乎为零。
解决:gamma 从 0.01 到 1 之间按量级搜索,不要一上来就大于 1。sklearn 默认的scale在很多场景已经够用,只有默认结果偏差明显时再手动调。
坑五:Excel 列顺序和预期不一致,模型训练了但结果全偏
现象:脚本不报错,但测试集预测值和真实值完全对不上,散点图散布没有任何规律。
原因:Excel 文件里可能多了一列序号,或者列的顺序不是「前 13 列特征、最后 1 列目标」,iloc[:, :-1]取到的特征矩阵和目标向量错位。
解决:读入数据后立刻打印train_df.columns和X_train.shape,确认列名和维度。批量处理多个数据集时,建议直接用列名取特征,而不是靠位置硬切。
5.2 用 GridSearchCV 代替手拨参数
手拨 C、gamma、epsilon 三个参数组合太多,靠感觉调低效。常见做法是网格搜索,把所有候选组合都跑一遍交叉验证,选测试分数最高的那组。
from sklearn.model_selection import GridSearchCV param_grid = { "C": [0.1, 1, 10, 100], "gamma": [0.01, 0.1, "scale"], "epsilon": [0.01, 0.1, 0.5] } search = GridSearchCV( SVR(kernel="rbf"), param_grid, scoring="neg_mean_squared_error", cv=5 ) search.fit(X_train_scaled, y_train) print("Best params:", search.best_params_) print("Best CV MSE:", -search.best_score_)scoring="neg_mean_squared_error"是因为 sklearn 的搜索机制认为分数越大越好,MSE 是越小越好,所以取负号。cv=5做五折交叉验证,样本量很小时改成 3,否则训练集被切得太薄。
网格大小是 4×3×3=36 组参数,每组做 5 折交叉验证,也就是要训练 180 个模型。波士顿数据集规模小,几秒就能跑完;数据量过万时建议缩小网格或者改用 RandomizedSearchCV。
5.3 调参后的闭环验证
网格搜索出的最优参数不要直接当最终答案,拿它重新训练一次,对比调参前后的测试集 MSE,确认提升是真实的。
best_reg = search.best_estimator_ y_test_pred_best = best_reg.predict(X_test_scaled) print("调参前 Test MSE:", test_mse) print("调参后 Test MSE:", mean_squared_error(y_test, y_test_pred_best))调参后的测试集 MSE 通常会下降不少。如果没下降甚至上升,说明原先的默认参数在本数据集上已经很合适,或者最优参数组合过拟合到了验证集。这时候最好的做法是接受基线结果,而不是继续加参数网格。
提示:GridSearchCV 内部已经做了交叉验证,
search.best_estimator_是在最优参数下重新用全量训练集拟合的模型。用它做最终预测前,不需要再手动调用一次 fit。
6. 进阶:用学习曲线判断样本量,再决定要不要上高斯过程回归
6.1 学习曲线:判断加数据有没有用
SVR 在小样本上表现不错,但小到一定程度也会力不从心。与其凭感觉判断样本量够不够,不如跑一张学习曲线,看训练集和验证集分数随样本量变化的趋势。
from sklearn.model_selection import learning_curve import numpy as np train_sizes, train_scores, valid_scores = learning_curve( best_reg, X_train_scaled, y_train, train_sizes=np.linspace(0.2, 1.0, 5), cv=5, scoring="neg_mean_squared_error" ) train_curve = -train_scores.mean(axis=1) valid_curve = -valid_scores.mean(axis=1) print("训练集 MSE 曲线:", train_curve) print("验证集 MSE 曲线:", valid_curve)如果验证集曲线后期还在明显下降,说明加数据还有收益;如果已经趋平,说明模型容量到头了,加再多数据也难提升,这时该换模型或者加特征。
6.2 SVR 的边界:什么时候换高斯过程回归
SVR 不是万能的。特征维度上百、样本量几万时,SVR 训练时间会明显拉长;数据带强异方差噪声时,固定 epsilon 管道可能不合适。同类小样本仿真数据的回归预测里,还有一个常被拿来做对比的模型是高斯过程回归(GPR),它天然给出预测置信区间,并且在小样本下表现惊艳。SVR 管点估计,GPR 管点估计加不确定性,两者在小样本回归场景里互为备选。
从那以后我每次拿到新数据集,都会先跑一遍这份 svm.py 拿基线,再画一张学习曲线判断瓶颈在数据量还是模型容量,最后才决定要不要上高斯过程回归。这套流程已经成了我做回归预测的固定动作,省掉了很多盲目调参的时间。希望帮到你。
本文还有配套的精品资源,点击获取