ARTICLE DETAIL

资讯详情

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

支持向量机从原理到实战:SVM的Python实现与参数调优全解析

支持向量机从原理到实战:SVM的Python实现与参数调优全解析 简介一份面向机器学习入门者与Python开发者的支持向量机SVMPython实现资源以精简可运行的代码和配套数据直观展示SVM从数据到分类模型的学习过程。压缩包共6个文件其中3个Python源码文件分别承担核心算法、测试入口与文本预处理功能另有1个pyc编译缓存、1个Markdown说明文档和1个txt格式的测试数据集整体仅9KB结构紧凑适合快速下载并运行体验。包内通过完整脚本演示SVM的训练与预测流程并给出可直接观察分类效果的二维测试样例说明文档则交代运行环境与基本用法方便读者对照使用。内容覆盖数据加载、模型训练、分类结果验证等环节可帮助理解支持向量与决策边界等关键概念。目前已有1937人学习下载适用于课程作业、算法对比或入门自学是一份可直接借鉴的轻量级参考实现。1. 支持向量机Python实现的起点一个二分类问题能教会你什么支持向量机SVM是机器学习里少数几个从数学推导到Python落地都值得自己动手做一遍的模型。它解决的是很具体的一类问题给定一组带标签的样本找到一条能把两类数据分得最开的决策边界。对从业者来说SVM不是最时髦的模型却是最可靠的那一类——当样本量只有几百到几千、特征维度几十上百时一个调好参数的RBF核SVM经常能压过随机森林也常常让轻量级深度学习基线感到吃力。这篇笔记从间隔理论讲到numpy手写梯度下降再落到sklearn的SVC调参把支持向量机的实现路径和踩过的坑一次说清。适合三类人刚接触分类模型、想搞懂损失函数和梯度下降的初学者需要给表格型数据找一个稳定baseline的算法工程师以及正在纠结SVM和CNN这类深度模型在什么场景下该选谁的选型者。2. 先把SVM的间隔理论讲透硬间隔、软间隔与核函数的选择依据2.1 什么样的问题适合用支持向量机线性可分与线性不可分SVM最初的设计目标是线性分类。设想二维平面上两类点你可以画出无数条直线把它们分开但哪一条最稳直观上看离两类点都最远的那条线最好因为新样本即使有轻微扰动也不容易被分错。SVM把这条线推广成高维空间里的超平面 w·xb0并用“间隔”来量化“离两类点都远”这件事间隔就是两类支持向量到超平面距离之和数值上等于 2/‖w‖。最大化间隔等价于最小化 ‖w‖²/2这就是SVM目标函数里正则项的来源也是它和普通感知机的本质区别。这里值得展开说一句间隔最大化不只是几何直觉它背后对应的是结构风险最小化。边界越“宽”模型对训练样本位置的细微变化越不敏感泛化能力通常越好。这也是SVM在小样本、高维场景下比深度学习稳定的原因——深度模型要在大数据里把特征慢慢磨出来而SVM靠一个清晰的几何约束就能在小数据上站住脚。这里要澄清一个常见误解很多人把SVM和Lasso放在一起对比因为它们都带正则化。其实Lasso的L1范数是为了让系数稀疏、做特征选择而SVM的L2范数是为了让决策边界的几何间隔最大两个模型解决的是不同问题。理解了这一点后面看梯度下降更新式就不会混淆。线性可分只是一个理想前提。真实数据里两类样本往往互相交叠或者本身就是非线性的。面对这种数据SVM给出两条路一是允许少量样本越过间隔边界这就是软间隔二是通过核函数把样本映射到高维空间在高维里重新线性可分。两条路可以同时用RBF核SVM本质上就是“高维映射加软间隔最大化”。支持向量还有一个特性值得注意最终模型只由落在间隔边界上的那部分样本决定远离边界的样本对模型没有影响。这意味着SVM在样本噪声大、边界模糊的场景下表现稳定也是它长期被用于文本分类、基因表达这类特征维度远高于样本量场景的原因。样本量上去之后这个特性的价值会下降但这不妨碍它成为理解SVM的钥匙。2.2 硬间隔与软间隔C参数到底在管什么硬间隔SVM要求训练集严格线性可分所有样本都必须落在正确一侧且距离间隔边界至少为1。这个条件对离群点非常敏感只要有一个点标错或者位置异常整个超平面都会被剧烈拉动。我在早期项目里就吃过这个亏一份几乎线性可分的数据里混进两个错标样本硬间隔模型的边界直接扭成奇怪的角度换用软间隔之后立刻恢复正常。所以实际工程里基本不用纯硬间隔而是引入松弛变量 ξᵢ 和惩罚系数 C。软间隔的原始问题可以写成最优化目标 min ½‖w‖² C·Σξᵢ约束是 yᵢ(w·xᵢb) ≥ 1−ξᵢ且 ξᵢ ≥ 0。C是“对违反间隔行为的容忍度”的反向指标C越大系统越不允许样本越界决策边界就越接近硬间隔但也更容易过拟合C越小模型允许更多样本落在间隔内甚至被错分边界更平滑泛化通常更好但C过小会欠拟合。工程上C的取值往往跨越几个数量级去搜常见范围是 10⁻³ 到 10³。我见过最多次的翻车是把C默认值1.0当成“万能”在高噪声数据上得到一个剧烈抖动的边界。判断方法很简单如果验证集准确率比训练集低很多先怀疑C设大了而不是急着换模型。反过来如果训练集准确率和验证集都低得离谱再考虑是不是C太小、边界过于宽松。sklearn的LinearSVC还提供两个损失函数变体losshinge 对应标准SVM目标losssquared_hinge 对违反样本做平方惩罚。squared_hinge的梯度是连续的收敛更快但对离群点的惩罚更重把异常值的影响放得更大。没有特殊理由时用默认的hinge就够。对于手写实现的人来说C还有一个更直观的理解方式在梯度更新式里违反间隔的样本贡献的梯度是 −C·yᵢ·xᵢC直接决定了每个违反样本把超平面推多远。C大一次更新就把边界推得很远C小违反样本的影响被稀释。这个视角会在第3章的代码里反复出现。2.3 核函数为什么RBF是默认选项核函数解决的是非线性问题。它不显式计算高维映射 φ(x)而是用一种等价的内积形式 K(x,x′) 表达高维空间里的相似度把计算量控制住。常用核函数有四个需要根据数据特点选。核函数表达式适用场景linearx·x′特征维度高、样本量大、数据近似线性可分poly(γ·x·x′r)^d明确知道特征之间存在多项式关系RBFexp(−γ‖x−x′‖²)无先验时的默认选项非线性边界首选sigmoidtanh(γ·x·x′r)少数神经激活类场景效果不稳定少用RBF之所以是默认选项是因为它对应的特征映射是无穷维的理论上只要 γ 取得合适能把任意复杂的边界“撑开”。但这也带来一个代价γ 控制每个样本的影响半径。γ 很大时每个样本只影响周围很小一片区域决策边界极度弯曲训练集上几乎零误差测试集上却可能很差γ 很小时所有样本的影响叠加在一起边界过于平滑模型退化成近似线性。我一般把 γ 和 C 一起做网格搜索而不是单独调。因为C控制“允许多少违反”γ控制“边界多复杂”两者互相牵制。sklearn的SVC默认 gammascale也就是 1/(n_features·X.var())这个默认值在标准化后的数据上是个不错的起点但不是最优第4章会讲怎么搜。把这一层的选择逻辑收拢一下数据量大、特征多、边界近似线性用LinearSVC样本几千以内、不确定非线性程度用RBF核SVC要解释性和可控性先在linear和RBF之间对比poly留给有明确先验的场景。至于SVM和CNN怎么选我的判断标准是数据和算力图像、音频这类天生高维的原始数据CNN有结构优势表格型、样本量几千的数据SVM更省事也更稳不必为了追新把一个分类问题硬塞给深度模型。3. 手写线性SVM用numpy梯度下降实现最小可运行版本3.1 构造二分类数据集并完成特征标准化很多教程直接跳到sklearn但手写一遍会让两个概念具体化hinge损失的梯度长什么样、为什么特征标准化对SVM是刚需。开始之前确认你的python环境里装好了numpy、matplotlib和scikit-learn这三个库缺哪个用pip装哪个。先造一份简单的二维数据方便后面可视化。用sklearn的make_blobs生成两个簇再加一点噪声。import numpy as np from sklearn.datasets import make_blobs from sklearn.model_selection import train_test_split # 生成两个簇中心分别在 (0,0) 和 (3,3) 附近制造少量交叠 X, y make_blobs(n_samples300, centers2, cluster_std1.2, random_state42) # SVM要求标签为 1 和 -1而不是 0 和 1 y np.where(y 0, -1, 1) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42) print(X_train.shape, X_test.shape)我把标签转成±1是因为hinge损失的表达式 y(w·xb) 依赖标签符号用1/−1写起来最顺手。sklearn里不需要手动做这一步内部会自动处理但手写实现时不转换公式和代码就会全面错位。训练集和测试集划分固定随机种子保证后续对比可复现。特征标准化这一步在划分之后、训练之前做。SVM的间隔计算依赖样本点之间的欧氏距离如果某个特征数值范围是另一个特征的100倍距离计算会被大数值特征主导间隔最大化就失去了几何意义。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) # 测试集必须用训练集拟合好的 scaler不能重新 fit X_test_scaled scaler.transform(X_test)注意测试集的标准化只能用训练集上拟合好的scaler去transform不能对测试集单独fit_transform。否则测试集被自己的均值和方差归一化和训练集不在同一个坐标空间里测试分数会虚高。这里还有个新手常犯的错先对全量数据标准化再划分训练测试集。那样测试集的统计量已经混进了scaler属于数据泄漏。正确顺序是先划分、后fit。嫌麻烦的话直接用sklearn的pipeline把标准化和模型绑在一起第4章就是这种写法。3.2 从hinge损失推导子梯度梯度下降能用在SVM上的原因SVM的完整损失函数是 ½‖w‖² C·Σmax(0, 1 − yᵢ(w·xᵢb))。第一项是正则项让间隔尽可能大第二项是hinge损失惩罚那些没有达到“离边界至少1”的样本。hinge损失在 margin1 处有一个尖点严格说不可导。机器学习里处理这个问题用的是次梯度在不可导点上任取一个方向的梯度即可。分段来看当 yᵢ(w·xᵢb) ≥ 1 时样本没有产生损失梯度只来自正则项当 yᵢ(w·xᵢb) 1 时损失是 1 − yᵢ(w·xᵢb)对 w 的梯度是 w − C·yᵢ·xᵢ对 b 的梯度是 −C·yᵢ。所以参数更新规则是对每个被违反的样本w 被往远离当前超平面的方向推同时被正则项往回拉对已经满足间隔的样本只做微小的权重衰减。这个“推拉”机制就是SVM区别于感知机的地方——感知机只要分对就不更新SVM即使分对了只要没达到间隔边界仍然要更新。这也是为什么SVM的边界总是比感知机更稳健。需要说明的是梯度下降并不是标准SVM库的训练方式libsvm和liblinear用的分别是SMO和坐标下降。手写梯度下降的意义在于看清每个参数如何影响更新方向为后面用sklearn调参做铺垫。对纯线性问题批量梯度下降的收敛速度完全够用。3.3 训练循环、收敛判定与决策边界可视化下面给出完整训练函数。为了可读性我用批量梯度下降而不是随机梯度下降300个样本的规模计算量很小。def train_linear_svm(X, y, C1.0, lr0.01, epochs200): n_samples, n_features X.shape w np.zeros(n_features) b 0.0 loss_history [] for epoch in range(epochs): margins y * (X w b) # 违反间隔的样本margin 1 violations margins 1.0 # 梯度正则项 违反样本的累加贡献 grad_w w - C * (violations * y) X grad_b -C * np.sum(violations * y) w - lr * grad_w b - lr * grad_b # 记录损失用于观察收敛 loss 0.5 * np.dot(w, w) C * np.sum(np.maximum(0, 1 - margins)) loss_history.append(loss) if epoch % 50 0: print(fepoch {epoch}, loss {loss:.4f}) return w, b, loss_history逻辑说明violations是一个布尔数组标记所有没达到间隔边界的样本grad_w由两部分组成第一部分w来自正则项第二部分是所有违反样本的加权累加grad_b只依赖违反样本的标签之和。参数上learning rate设0.01、epoch设200对标准化后的二维数据够用特征维度升到几十时把lr降到0.001更稳妥。训练完成后画决策边界和间隔线顺便标出支持向量。import matplotlib.pyplot as plt w, b, loss_history train_linear_svm(X_train_scaled, y_train, C1.0, lr0.01, epochs200) # 决策边界w·x b 0 xmin, xmax X_train_scaled[:, 0].min()-0.5, X_train_scaled[:, 0].max()0.5 ymin, ymax X_train_scaled[:, 1].min()-0.5, X_train_scaled[:, 1].max()0.5 xx, yy np.meshgrid(np.linspace(xmin, xmax, 200), np.linspace(ymin, ymax, 200)) Z np.sign(w[0]*xx w[1]*yy b) # 找最接近间隔边界的样本作为支持向量近似 dist y_train * (X_train_scaled w b) sv np.abs(dist - 1.0) 0.05 plt.scatter(X_train_scaled[:, 0], X_train_scaled[:, 1], cy_train, cmapbwr, s10) plt.scatter(X_train_scaled[sv, 0], X_train_scaled[sv, 1], facecolorsnone, edgecolorsk, s80, labelsupport vectors) plt.contourf(xx, yy, Z.reshape(xx.shape), alpha0.2, cmapbwr, levels[-1, 0, 1]) plt.legend() plt.show()支持向量的判定用了近似方法标准化后真正的支持向量满足 y(w·xb)1 或 −1我取距离1不超过0.05的点画圈。实际工程里SVC会直接暴露支持向量的索引但手写实现里这样找已经足够。如果特征标签很长导致横坐标挤成一团记得加一句 plt.xticks(rotation45) 旋转标签这是画图最常见的细节坑。最后验证手写模型和sklearn的一致性。LinearSVC用hinge损失、C1.0理论上应该得到几乎相同的决策边界。from sklearn.svm import LinearSVC svm_sk LinearSVC(C1.0, losshinge, max_iter10000) svm_sk.fit(X_train_scaled, y_train) print(sklearn weight:, svm_sk.coef_, bias:, svm_sk.intercept_) print(manual weight:, w, bias:, b)两者的权重方向应该一致数值略有差异。原因在于我从零向量出发做批量梯度下降LinearSVC内部用liblinear的坐标下降法学习率、迭代次数和收敛误差都不同最终位置会在最优解附近小幅漂移。只要权重方向和间隔宽度对得上就说明手写实现没有大问题。如果发现两者决策边界方向都不一致或精度差异巨大优先检查标签符号和标准化是否一致。手写实现里值得手动试的两个参数把C改成10或0.1观察决策边界斜率怎么变把lr改成0.1loss曲线会震荡甚至发散——这就是第5章要重点排查的坑。调参不是玄学每个参数都对应一个具体的几何行为亲手改一次比背十遍公式管用。4. 切换到sklearn实战SVC与LinearSVC的参数调优全流程4.1 用StandardScalerSVC搭建最小可复现pipeline手写版本验证了原理实际项目里直接上sklearn。SVC和LinearSVC的关键区别是SVC支持核函数但默认用基于SMO的算法样本量稍大就慢LinearSVC只做线性边界但用liblinear同样数据量下经常快一个数量级以上。先把预处理和模型绑成一个pipeline这是我在项目里必做的一步。pipeline的好处是交叉验证里的每一个fold都会单独做标准化避免数据泄漏预测阶段也变成一行调用。from sklearn.pipeline import make_pipeline from sklearn.svm import SVC from sklearn.model_selection import cross_val_score pipeline make_pipeline( StandardScaler(), SVC(kernelrbf, C1.0, gammascale, random_state42) ) scores cross_val_score(pipeline, X, y, cv5, scoringaccuracy) print(CV accuracy: %.3f ± %.3f % (scores.mean(), scores.std()))这里的X、y是原始未标准化的数据标准化由pipeline内部完成。不要在外层先手动标准化再喂给pipeline那样交叉验证的每个fold都会看到全局统计量结果会偏乐观。SVC里的random_state对确定性算法影响不大固定下来只是便于复现。gammascale是sklearn推荐默认它根据特征数和方差自动折算在没有先验时比手动填gamma值靠谱。提示pipeline的标准化必须在交叉验证的每一个fold内部重新拟合这也是我坚持用make_pipeline而不是手动预处理的原因。如果后期想查看特征重要性或模型系数给pipeline里的步骤起名字比默认小写类名直观。比如 make_pipeline(StandardScaler(), SVC(...)) 里的SVC步骤名是 svc用 get_params 和 named_steps 都能访问。换用 LinearSVC 时结构相同只是核函数换成线性的这一步的替换成本几乎为零。4.2 C与gamma的网格搜索用GridSearchCV找到最佳组合RBF核SVC的两个超参数是C和gamma。我见过很多人凭感觉调C从0.1试到10gamma随手填0.1然后抱怨模型不稳定。正确做法是网格搜索加对数刻度让参数跨越几个数量级而不是线性取点。from sklearn.model_selection import GridSearchCV param_grid { svc__C: [0.01, 0.1, 1, 10, 100], svc__gamma: [0.001, 0.01, 0.1, 1, 10], svc__kernel: [rbf] } grid GridSearchCV(pipeline, param_grid, cv5, scoringaccuracy, n_jobs-1) grid.fit(X, y) print(best params:, grid.best_params_) print(best score: %.4f % grid.best_score_)param_grid里用 svc__C 这种前缀写法是因为pipeline第二步的名字叫svc。把SVC单独拿出来做网格搜索而不包pipeline标准化只能做一次搜索出来的参数会偏向未标准化的数据换到测试集上就不成立。5个C乘以5个gamma再乘5折就是125次训练这个规模在几百样本上几秒就出结果。网格搜索有个容易被忽视的细节如果best_params_落在了搜索范围的边缘比如C100恰好是最优说明真正的优值可能在100之外当前网格没搜到位。解决方法是把范围往该方向延伸一个数量级再搜一次而不是直接采信边缘结果。样本量到一两万时5×5的网格会明显变慢届时把候选参数缩到3×3或者改用RandomizedSearchCV随机采样效果接近但耗时少一个量级。4.3 评估指标选择准确率不够时看什么二分类问题里accuracy是最直观的指标但在类别不平衡时会骗人。假设100个样本里95个正类5个负类模型全预测正类也有95%准确率这时看accuracy毫无意义。SVM对不平衡数据尤其敏感因为间隔最大化试图照顾所有样本少数类很容易被多数类淹没。处理不平衡有两条路先换评估指标再调模型参数。指标换成F1参数加 class_weightbalanced。下面用分层交叉验证评估F1。from sklearn.metrics import f1_score from sklearn.model_selection import StratifiedKFold skf StratifiedKFold(n_splits5, shuffleTrue, random_state42) f1_list [] for train_idx, test_idx in skf.split(X, y): pipeline.fit(X[train_idx], y[train_idx]) pred pipeline.predict(X[test_idx]) f1_list.append(f1_score(y[test_idx], pred)) print(CV F1: %.3f % np.mean(f1_list))StratifiedKFold保证每个fold里正负类比例和全集一致这是不平衡数据交叉验证的标配。F1是精确率和召回率的调和平均能同时惩罚“错杀”和“漏判”。如果业务更关注“把少数类找出来”比如风控场景里坏样本识别那recall比accuracy重要scoringrecall会引导调参器优先优化召回。评估协议不固定网格搜索结果就没有可比性后面想反悔都缺依据。我在项目里固定一套协议StratifiedKFold F1再辅以混淆矩阵定位错误类型。等指标稳定了再去动C和gamma这时候每一次参数改动带来的变化才有意义。如果再往下走给SVC设置 class_weightbalanced让少数类的惩罚按样本量比例放大这一步往往比单纯调C更有效。5. SVM实现路上的避坑五个高频问题与排查顺序5.1 特征不标准化导致loss不降现象手写SVM训练几十轮loss几乎不动或者收敛到很差的准确率用sklearn时同一个模型在训练集上分数忽高忽低换一组随机种子结果剧烈波动。原因SVM的间隔计算基于样本点之间的欧氏距离。如果某个特征取值范围是0到10000另一个是0到1距离计算被大数值特征主导决策边界会偏向这个大数值特征梯度方向跟着扭曲。更隐蔽的是不同特征的方差差异会让正则项 ½‖w‖² 对梯度的影响不均匀导致收敛极慢。解决训练前做标准化fit_transform训练集、transform测试集手写实现里放在构造训练数据之后sklearn里用pipeline包住避免交叉验证数据泄漏。标准化之后loss曲线仍不平滑再去看学习率不要反过来先调lr。5.2 梯度下降震荡、收敛慢现象loss曲线像锯齿训练后期在某个值附近反复横跳或者loss持续下降但准确率纹丝不动。原因学习率过大是震荡的主因。批量梯度下降每一轮参数的更新量是lr乘以所有样本梯度之和C设得大时违反样本的梯度贡献也大同样的lr会让更新步长成倍放大。另一个原因是数据没标准化不同方向上的曲率差异悬殊固定学习率很难同时适配。解决把lr从0.01调小一个数量级或者加步长衰减每50轮乘以0.5。更稳妥的方案是用sklearn的SGDClassifier(losshinge)做对比它内部有自适应学习率策略。如果手写实现和SGDClassifier的收敛曲线差异很大基本可以断定是lr或数据尺度的问题而不是SVM原理本身有问题。5.3 类别严重不平衡时模型倾向预测多数类现象混淆矩阵里多数类召回率接近99%少数类召回率只有个位数F1远低于准确率业务上少数类基本是废的。原因SVM的间隔最大化对所有样本一视同仁少数类样本数量少对超平面的“投票权”也少。多数类可以把超平面推到自己一侧少数类即使被错分对总损失的贡献也有限。这个问题在RBF核下比线性核更明显因为非线性边界会把更多区域划给多数类。解决先设 class_weightbalanced让损失函数里每个样本的权重与类别频率成反比如果还不够用SMOTE这类过采样方法但只能对训练集做不能动测试集。最后才考虑调C因为C是全局的调大只会同时放大两类的惩罚不解决相对权重问题。5.4 RBF核运算溢出与gamma过大过小现象训练时报数学域错误或者预测结果出现NaN另一种是训练集准确率99%、测试集准确率60%的经典过拟合。原因RBF核计算 exp(−γ‖x−x′‖²)当γ取大值而特征尺度又没控制时指数内部数值可能溢出。gamma过大时每个样本只影响极小邻域模型把训练集每个点都记住泛化自然崩。这类过拟合在网格搜索范围上限处最容易出现因为搜索器会优先追求训练分数。解决先标准化再进网格搜索gamma搜索范围用对数刻度默认从gammascale算出的值往前后各延伸两三个量级。我一般把gamma上限设为10如果grid.best_params_里的gamma恰好落在上限说明搜索范围不够要么扩要么检查数据里是否有离群点干扰了标准化结果。5.5 数据量稍大就训练很久现象样本一两万时SVC的网格搜索从几秒变成几分钟单次fit也要几十秒迭代调参根本跑不动。原因SVC内部用SMO算法训练复杂度约为O(n²)到O(n³)样本量上去之后二次项直接爆炸。LinearSVC用liblinear的坐标下降复杂度接近线性但只能做线性边界。解决数据量大于一万时线性问题直接用LinearSVC或SGDClassifier(losshinge)非线性问题先把特征做核近似比如用sklearn的RBFSampler把RBF核映射成显式特征再喂给LinearSVC速度和精度之间的平衡通常可接受。RBF核SVC留到几千样本以内的场景那才是它发挥优势的地方。6. SVC的多分类与概率输出decision_function的高级用法SVC默认在多分类时用one-vs-one策略也就是每两类之间训练一个SVM最后投票决定类别。LinearSVC默认则是one-vs-rest每个分类器负责“本类vs其余所有类”。选择依据很简单类别数不多、样本量不大两者差异忽略不计类别超过10个时one-vs-rest训练的分类器更少耗时更短。另一个容易被忽略的功能是概率输出。SVC默认只给决策分数decision_function不提供预测概率。想用predict_proba需要在初始化时加 probabilityTruesklearn会用Platt缩放把决策分数映射成概率。这个概率不是严格校准的它只是分数到概率的单调映射在类别不平衡时会明显偏离真实分布。代价是训练时间增加而且概率值不能当作真实概率用于下游精算。from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler model make_pipeline( StandardScaler(), SVC(kernelrbf, C1.0, gammascale, probabilityTrue) ) model.fit(X_train_scaled, y_train) # 决策分数用于手动选阈值 scores model.decision_function(X_test_scaled) # 概率用于业务侧排序 proba model.predict_proba(X_test_scaled)[:, 1]实际调用里我通常两者都用决策分数在验证集上扫阈值找到让F1最高的切分点概率用来做业务上的排序比如“风险从高到低排前100个”。手写SVM实现里没有这套东西只有 w·xb 这个原始分数这也侧面说明sklearn封装的价值。网上常见把SVM当黑匣子直接predict的用法我建议至少理解到decision_function这一层调阈值和做拦截都靠它。调试时我有一个固定习惯先跑LinearSVC做基线再上RBF核。LinearSVC训练快、参数少跑出来的准确率就是“线性边界的天花板”。如果RBF核SVC在这个基线上只提升一两个点而训练时间多了几十倍理性选择是把线性模型上线把省下的时间用在特征工程上。反之如果提升明显说明数据确实有非线性结构再去网格搜索C和gamma也不迟。多分类概率这块我踩过的坑是只在网格搜索里用了accuracy忽略了每个类别的召回率上线后少数类几乎失效。后来固定用StratifiedKFold加F1做评估才把问题暴露出来。SVM从数学到落地都不复杂复杂的是你有没有一套固定的评估和调参流程。希望这篇能帮到你。本文还有配套的精品资源点击获取
返回列表