支持向量机这个算法,我前前后后在不同项目里用过不下十次,每次重新翻出来用都会有一些新的体会。它不像决策树那么直观,也不像神经网络那样容易堆叠,但它在中小规模数据集上的表现,尤其是特征维度高、样本量又不算特别大的场景下,往往能给出让人意外的稳定结果。Scikit-learn把SVM封装得相当友好,几行代码就能跑起来,但真正要把它用好,核函数怎么选、C值怎么调、gamma设多少,这些细节才是决定模型能不能落地的关键。这篇文章我打算把自己在实际项目中积累的SVM使用经验完整梳理一遍,从原理理解到代码实操,再到调参避坑,尽量把每个环节讲透。不管你是刚接触机器学习的新手,还是已经用过SVM但总觉得调不到最优的老手,应该都能从中找到一些可以直接拿来用的东西。
1. 支持向量机到底在解决什么问题
1.1 从直观理解到数学表达
SVM最核心的想法其实一句话就能说清楚:在两类数据之间找到一条分界线,让这条线离两边最近的点都尽可能远。这个“尽可能远”就是SVM的灵魂所在。普通的线性分类器只要把两类点分开就行,但SVM追求的是最大间隔,也就是让决策边界到最近样本点的距离最大化。为什么这样做?因为间隔越大,模型对未知数据的容错能力就越强,泛化性能就越好。
用个生活化的类比:假设你在一条走廊里要把两拨人分开,一拨站左边,一拨站右边。你可以贴着左边的人画一条线,也可以贴着右边的人画一条线,但最合理的方式是在两拨人正中间画线,这样两边的人都不会轻易越界。SVM做的就是这件事,而且它只关心那些最靠近分界线的人——这些人就是“支持向量”。
数学上,SVM要解决的是一个凸二次规划问题。对于线性可分的情况,目标是最大化间隔,等价于最小化权重向量的范数。约束条件是所有样本点都要被正确分类且位于间隔边界之外。这个优化问题有唯一全局最优解,不会陷入局部极小,这是SVM相比神经网络的一个显著优势。
但现实数据很少是线性可分的,所以SVM引入了两个关键机制:软间隔和核技巧。软间隔允许少量样本点越界,通过惩罚系数C来平衡间隔最大化和分类错误之间的关系。核技巧则是把数据映射到高维空间,在高维空间里寻找线性分界面,从而实现对原始空间中非线性边界的划分。
1.2 硬间隔、软间隔与损失函数
硬间隔SVM要求所有样本都必须被正确分类且位于间隔之外,这在现实数据中几乎不可能满足。哪怕只有一个噪声点,整个优化问题就可能无解。所以实际使用的都是软间隔SVM,它允许某些样本点违反间隔约束,但要对这些违反行为施加惩罚。
这个惩罚力度由参数C控制。C越大,对分类错误的容忍度越低,模型倾向于把所有训练样本都分对,但间隔会变窄,容易过拟合。C越小,对错误的容忍度越高,间隔更宽,模型更偏向于简单化,但可能欠拟合。这个权衡关系是SVM调参的核心之一。
从损失函数的角度看,软间隔SVM使用的是合页损失(Hinge Loss)。对于正确分类且位于间隔之外的样本,损失为零;对于位于间隔之内但分类正确的样本,有小的损失;对于分类错误的样本,损失与偏离程度成正比。这种损失函数的设计使得只有支持向量对决策边界有影响,其他样本点怎么移动都不会改变模型,这也是SVM计算效率较高的原因之一。
1.3 核函数:从线性到非线性的桥梁
核函数是SVM最精妙的设计。它的核心思想是:与其显式地把数据映射到高维空间再做内积计算,不如直接定义一个核函数,它等价于高维空间中的内积,但计算量仍然停留在原始维度。这就是所谓的“核技巧”。
常用的核函数有几种。线性核最简单,就是原始空间的内积,适合特征维度已经很高或者数据本身线性可分的情况。多项式核可以把数据映射到多项式组合的高维空间,但参数较多,调起来麻烦。径向基函数核(RBF核)是最常用的,它把数据映射到无穷维空间,理论上可以拟合任何非线性边界,而且只有一个gamma参数需要调。Sigmoid核在某些场景下类似神经网络的效果,但实际使用较少。
RBF核的数学形式是exp(-gamma * ||x - x'||^2)。gamma参数控制单个样本影响的范围。gamma越大,影响范围越小,决策边界越曲折,容易过拟合;gamma越小,影响范围越大,决策边界越平滑,可能欠拟合。这个参数和C的配合使用是SVM调参的重头戏。
2. Scikit-learn中SVM的核心API与参数体系
2.1 分类器与回归器的选择
Scikit-learn提供了多个SVM相关的类。最常用的是SVC和SVR,分别用于分类和回归。SVC基于libsvm实现,支持多种核函数,适合中小规模数据集。如果数据量特别大,可以考虑LinearSVC,它基于liblinear实现,只支持线性核,但速度更快,适合高维稀疏数据。
NuSVC是另一个分类器,它用参数nu代替C来控制支持向量的比例和错误率的上界。nu的取值范围是0到1,表示支持向量占总样本比例的下界和错误率的上界。这个参数在某些场景下比C更直观,但实际使用频率不如SVC高。
对于回归任务,SVR的目标是找到一个函数,使得所有训练样本的预测误差都在一个指定的范围内,超出范围的误差才计入损失。这个范围由epsilon参数控制。epsilon越大,容忍的误差范围越宽,支持向量越少,模型越简单。
2.2 关键参数逐一拆解
C参数是惩罚系数,控制对分类错误的惩罚力度。前面已经讲过,C越大越容易过拟合,C越小越容易欠拟合。实际调参时通常在对数尺度上搜索,比如从0.001到1000。
kernel参数选择核函数类型,可选linear、poly、rbf、sigmoid和precomputed。大多数情况下从rbf开始尝试,如果特征维度很高且样本量不大,也可以先试linear。
gamma参数只对rbf、poly和sigmoid核有效,控制核函数的宽度。gamma的常用搜索范围也是对数尺度,比如从0.001到10。gamma和C需要联合调参,因为它们之间存在耦合关系。
degree参数只在多项式核中使用,控制多项式的阶数,默认是3。阶数越高,模型越复杂,但也越容易过拟合。
coef0参数在多项式和sigmoid核中使用,控制常数项的影响。
class_weight参数用于处理类别不平衡问题。可以设为balanced,让算法自动根据类别频率调整权重,也可以传入自定义的字典。
probability参数决定是否启用概率估计。启用后可以使用predict_proba方法,但会显著增加训练时间,因为内部需要进行交叉验证来拟合概率校准。
2.3 核函数选择的经验法则
在实际项目中,我通常按照以下顺序尝试核函数。首先用线性核跑一个基线,看看数据是否近似线性可分。如果线性核的效果已经不错,就没必要上非线性核。如果线性核明显欠拟合,再换RBF核。RBF核在大多数场景下都能给出不错的结果,而且只有一个gamma参数需要调,相对省心。
多项式核我很少用,因为参数多、调参成本高,而且RBF核通常能覆盖它的能力范围。Sigmoid核更少用,它在某些参数下等价于两层神经网络,但稳定性不如RBF。
有一个经验性的判断方法:如果特征维度远大于样本量,比如文本分类中的词袋特征,线性核往往就够了,甚至更好,因为高维空间本身就已经很容易线性可分。如果特征维度不高但样本量较大,RBF核通常更有优势。
3. 完整实操流程:从数据准备到模型评估
3.1 数据预处理与特征缩放
SVM对特征的尺度非常敏感。因为它的决策边界依赖于距离计算,如果一个特征的数值范围是0到1,另一个是0到10000,那么后者会主导距离计算,导致模型失效。所以使用SVM之前,特征缩放是必须的。
最常用的缩放方法是标准化,即减去均值再除以标准差,让每个特征都服从标准正态分布。Scikit-learn的StandardScaler可以完成这个操作。另一种方法是归一化到0到1区间,使用MinMaxScaler。两种方法都可以,但标准化更常用,因为它对异常值不那么敏感。
需要注意的是,缩放参数必须只在训练集上拟合,然后应用到测试集。如果对整个数据集做缩放,测试集的信息会泄露到训练过程中,导致评估结果偏乐观。正确的做法是在Pipeline中完成缩放和模型训练,或者手动先fit训练集,再transform测试集。
from sklearn.preprocessing import StandardScaler from sklearn.svm import SVC from sklearn.pipeline import Pipeline from sklearn.model_selection import train_test_split from sklearn.datasets import load_breast_cancer # 加载示例数据 data = load_breast_cancer() X_train, X_test, y_train, y_test = train_test_split( data.data, data.target, test_size=0.2, random_state=42, stratify=data.target ) # 构建Pipeline pipe = Pipeline([ ('scaler', StandardScaler()), ('svm', SVC(kernel='rbf', C=1.0, gamma='scale')) ]) pipe.fit(X_train, y_train) print(f"测试集准确率: {pipe.score(X_test, y_test):.4f}")这段代码展示了最基本的SVM分类流程。Pipeline确保缩放只在训练数据上拟合,避免了数据泄露。gamma设为scale是Scikit-learn的默认值,它等于1除以特征数乘以X的方差,是一个比较合理的起点。
3.2 网格搜索与交叉验证调参
SVM的参数调优离不开网格搜索和交叉验证。GridSearchCV可以穷举指定的参数组合,用交叉验证评估每组参数的性能,最后返回最优组合。虽然计算量大,但对于中小规模数据集来说完全可行。
from sklearn.model_selection import GridSearchCV import numpy as np param_grid = { 'svm__C': [0.1, 1, 10, 100], 'svm__gamma': [0.001, 0.01, 0.1, 1], 'svm__kernel': ['rbf', 'linear'] } grid = GridSearchCV( pipe, param_grid, cv=5, scoring='accuracy', n_jobs=-1, verbose=1 ) grid.fit(X_train, y_train) print(f"最优参数: {grid.best_params_}") print(f"最优交叉验证得分: {grid.best_score_:.4f}") print(f"测试集得分: {grid.score(X_test, y_test):.4f}")这里我把C和gamma都放在对数尺度上搜索,这是实践中总结出来的有效策略。因为这两个参数对模型性能的影响是指数级的,线性尺度搜索效率太低。n_jobs设为-1表示使用所有CPU核心并行计算,能显著缩短搜索时间。
交叉验证的折数通常选5或10。5折在计算成本和评估稳定性之间取得了较好的平衡。如果数据量很小,可以用留一法交叉验证,但计算成本会大幅增加。
3.3 概率输出与阈值调整
标准的SVC输出的是类别标签,不直接给出概率。如果需要概率输出,必须设置probability=True。但要注意,这会触发内部的Platt缩放,通过交叉验证来拟合概率校准参数,训练时间会明显增加。
svm_prob = SVC(kernel='rbf', C=10, gamma=0.01, probability=True, random_state=42) svm_prob.fit(X_train_scaled, y_train) proba = svm_prob.predict_proba(X_test_scaled) print(f"前5个样本的预测概率:\n{proba[:5]}")有了概率输出之后,就可以根据业务需求调整分类阈值。默认阈值是0.5,即概率大于0.5判为正类。但在某些场景下,比如疾病筛查,我们可能更关心召回率,宁可误报也不能漏报,这时可以把阈值调低,比如0.3。如果更关心精确率,可以把阈值调高。
阈值调整的本质是在精确率和召回率之间做权衡。可以通过绘制精确率-召回率曲线来辅助决策,找到最符合业务需求的平衡点。
3.4 模型持久化与部署
训练好的模型需要保存下来供后续使用。Scikit-learn推荐使用joblib而不是pickle,因为joblib对包含大量NumPy数组的对象序列化效率更高。
import joblib joblib.dump(grid.best_estimator_, 'svm_model.joblib') loaded_model = joblib.load('svm_model.joblib') print(f"加载模型的测试集得分: {loaded_model.score(X_test, y_test):.4f}")部署时需要注意,推理阶段的数据预处理必须和训练阶段完全一致。如果训练时用了StandardScaler,推理时也要用同一个scaler对象做变换。把scaler和模型一起保存到Pipeline中是最稳妥的做法,这样加载后直接调用predict即可,不会遗漏任何预处理步骤。
4. 调参实战:C与gamma的联合优化策略
4.1 理解C与gamma的耦合关系
C和gamma是RBF核SVM最重要的两个参数,它们之间存在明显的耦合关系。C控制对分类错误的惩罚力度,gamma控制决策边界的弯曲程度。两者都增大时,模型复杂度急剧上升,过拟合风险很高。两者都减小时,模型过于简单,欠拟合。
有一个经典的可视化方法:在二维网格上画出不同(C, gamma)组合下的交叉验证准确率热力图。通常可以看到一条对角线方向的高准确率带,说明C和gamma需要同向变化才能保持模型性能稳定。偏离这条带的区域,要么过拟合要么欠拟合。
我在实际调参时通常采用由粗到细的策略。先在大范围上做粗略搜索,比如C取[0.01, 0.1, 1, 10, 100],gamma取[0.001, 0.01, 0.1, 1, 10],找到大致的最优区域。然后在这个区域附近做更细粒度的搜索,比如步长缩小到原来的十分之一。
4.2 不同数据规模下的调参经验
数据规模对SVM调参有很大影响。样本量较小时,比如几百个样本,模型容易过拟合,应该选择较小的C和适中的gamma。样本量较大时,比如几万个样本,模型有足够的统计支撑,可以适当增大C,让模型更精确地拟合训练数据。
特征维度也很关键。高维稀疏数据,比如文本的TF-IDF特征,通常线性核就够用了,调参重点在C上。低维稠密数据,比如鸢尾花数据集,RBF核更合适,需要同时调C和gamma。
类别不平衡的场景下,除了调C和gamma,还要考虑class_weight参数。把class_weight设为balanced可以让算法自动调整权重,相当于对少数类样本施加更大的惩罚。也可以手动指定权重字典,比如{0: 1, 1: 10}表示少数类的权重是多数类的10倍。
4.3 学习曲线与验证曲线诊断
调参不能只看最终得分,还要看学习曲线和验证曲线来诊断模型状态。学习曲线展示训练集和验证集得分随训练样本量增加的变化趋势。如果两条曲线都偏低且趋于平缓,说明模型欠拟合,需要增大C或gamma。如果训练集得分高但验证集得分低且差距大,说明过拟合,需要减小C或gamma。
验证曲线展示得分随某个参数变化的情况。固定其他参数,画出得分随C变化的曲线,可以直观看到C在哪个区间表现最好。类似地可以画出gamma的验证曲线。两条曲线结合看,就能找到比较合理的参数范围。
from sklearn.model_selection import validation_curve import matplotlib.pyplot as plt param_range = np.logspace(-3, 3, 7) train_scores, test_scores = validation_curve( pipe, X_train, y_train, param_name='svm__C', param_range=param_range, cv=5, scoring='accuracy', n_jobs=-1 ) train_mean = np.mean(train_scores, axis=1) test_mean = np.mean(test_scores, axis=1) plt.figure(figsize=(8, 5)) plt.semilogx(param_range, train_mean, label='训练集得分', marker='o') plt.semilogx(param_range, test_mean, label='验证集得分', marker='s') plt.xlabel('C') plt.ylabel('准确率') plt.legend() plt.grid(True) plt.show()这段代码画出的曲线能帮你快速判断C的合理范围。如果验证集得分在某个C值之后开始下降,说明再增大C就会过拟合。如果还在上升,说明可以继续增大C试试。
5. 常见问题与排查技巧实录
5.1 训练速度慢怎么办
SVM的训练时间复杂度在O(n^2)到O(n^3)之间,n是样本量。样本量上万之后,训练时间会明显增加。如果遇到训练速度慢的问题,可以从几个方面优化。
首先考虑换用LinearSVC。如果数据本身近似线性可分,或者特征维度很高,线性核的效果可能不比RBF差,但速度快很多。LinearSVC使用坐标下降法,在大规模数据上效率更高。
其次可以减小训练集规模。如果数据量太大,可以随机采样一个子集来训练,或者使用增量学习的方式分批训练。不过SVC不支持增量学习,需要用SGDClassifier来替代。
还可以降低参数搜索的粒度。网格搜索很耗时,可以先用随机搜索快速缩小范围,再在最优区域附近做精细搜索。RandomizedSearchCV比GridSearchCV快很多,尤其是在参数空间大的时候。
5.2 预测结果全是一类怎么回事
这个问题通常出现在类别严重不平衡的场景。如果正类样本只占1%,模型把所有样本都预测为负类就能达到99%的准确率,但这显然没有意义。解决方法有几种。
设置class_weight='balanced'是最简单的,让算法自动调整权重。也可以手动指定更大的正类权重。另外可以调整分类阈值,把正类的判定门槛降低。如果用了probability=True,可以输出概率后自己设定阈值。
还有一种可能是特征缩放没做好。如果某个特征的数值范围远大于其他特征,SVM可能会忽略其他特征,导致模型退化。检查一下是否所有特征都做了标准化。
5.3 核函数选择困难症的破解方法
很多人纠结到底选哪个核函数。我的经验是:先跑线性核,如果交叉验证得分已经满足需求,就用线性核,简单可解释。如果线性核明显不够,换RBF核,用网格搜索调C和gamma。多项式核和Sigmoid核在绝大多数场景下不会比RBF核更好,除非你有明确的领域知识表明数据适合多项式映射。
还有一个实用技巧:比较线性核和RBF核的交叉验证得分。如果差距在1%以内,选线性核,因为它的模型更简单,推理速度更快,可解释性也更好。如果RBF核明显更优,比如高出3%以上,那就用RBF核。
5.4 内存不足的应对策略
SVM训练时需要计算核矩阵,内存消耗与样本量的平方成正比。样本量上万时,核矩阵可能占用几个GB的内存。如果内存不足,可以尝试以下方法。
使用LinearSVC替代SVC,它不需要存储核矩阵。或者使用Nystroem方法做核近似,把核矩阵降维到指定维度。还可以减小缓存大小,SVC的cache_size参数控制核矩阵缓存,默认200MB,调小一些可以降低内存占用,但训练速度会变慢。
如果数据量确实很大,考虑换用其他算法,比如随机森林或梯度提升树,它们对大规模数据的扩展性更好。SVM在样本量超过几万之后,性价比就不如这些集成方法了。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 训练时间过长 | 样本量大或参数搜索范围广 | 查看数据规模和网格大小 | 换LinearSVC、减小搜索范围、随机搜索 |
| 预测全为一类 | 类别不平衡或特征未缩放 | 检查类别分布和特征范围 | class_weight='balanced'、标准化特征 |
| 验证集得分远低于训练集 | 过拟合 | 对比训练和验证得分 | 减小C或gamma、增加正则化 |
| 训练集和验证集得分都低 | 欠拟合 | 学习曲线诊断 | 增大C或gamma、换RBF核 |
| 内存不足 | 核矩阵过大 | 监控内存使用 | 换LinearSVC、核近似、减小cache_size |
| 概率输出不准 | 概率校准不充分 | 检查probability参数 | 增大交叉验证折数、使用CalibratedClassifierCV |
6. 进阶技巧与性能优化
6.1 使用Pipeline避免数据泄露
数据泄露是机器学习中最隐蔽的错误之一。如果在整个数据集上做特征选择或缩放,然后才划分训练测试集,测试集的信息就会泄露到训练过程中。Pipeline可以彻底避免这个问题,因为它确保所有预处理步骤只在训练数据上拟合。
from sklearn.feature_selection import SelectKBest, f_classif pipe_with_fs = Pipeline([ ('scaler', StandardScaler()), ('select', SelectKBest(f_classif, k=10)), ('svm', SVC(kernel='rbf', C=10, gamma=0.01)) ]) pipe_with_fs.fit(X_train, y_train) print(f"带特征选择的测试集得分: {pipe_with_fs.score(X_test, y_test):.4f}")这个Pipeline先标准化,再选择最好的10个特征,最后训练SVM。所有步骤的拟合都只在训练数据上进行,评估结果更可靠。
6.2 自定义核函数与预计算核矩阵
Scikit-learn支持自定义核函数。你可以传入一个可调用对象作为kernel参数,它接收两个样本矩阵,返回核矩阵。也可以预先计算好核矩阵,把kernel设为precomputed,直接传入核矩阵。
预计算核矩阵在以下场景有用:核函数计算成本很高,需要复用;或者你有领域知识可以设计特定的核函数。比如在生物信息学中,可以根据基因序列的相似度设计专门的核函数。
def my_kernel(X, Y): return np.dot(X, Y.T) ** 2 svm_custom = SVC(kernel=my_kernel, C=1.0) svm_custom.fit(X_train_scaled, y_train) print(f"自定义核的测试集得分: {svm_custom.score(X_test_scaled, y_test):.4f}")这个自定义核实际上是一个二次多项式核。自定义核的灵活性很高,但要注意核函数必须满足Mercer条件,即对应的核矩阵必须是半正定的。
6.3 多分类问题的处理策略
SVM原生只支持二分类。Scikit-learn的SVC通过一对多或一对一策略扩展到多分类。一对多是每个类别训练一个分类器,将该类与其他所有类区分开。一对一是在每两个类别之间训练一个分类器,预测时投票决定。
SVC默认使用一对一策略,因为它在大多数情况下表现更好,而且每个子问题规模更小。但一对一策略需要训练n*(n-1)/2个分类器,类别数很多时计算量会很大。如果类别数超过10个,可以考虑使用LinearSVC,它使用一对多策略,扩展性更好。
多分类的评估不能只看准确率,还要看每个类别的精确率、召回率和F1分数。混淆矩阵可以直观展示哪些类别容易被混淆。
6.4 增量学习与在线更新
标准SVC不支持增量学习,每次有新数据都必须重新训练。如果数据是流式的,或者需要频繁更新模型,可以使用SGDClassifier,它支持partial_fit方法进行增量学习。
from sklearn.linear_model import SGDClassifier sgd = SGDClassifier(loss='hinge', penalty='l2', alpha=0.0001, max_iter=1000) sgd.fit(X_train_scaled, y_train) print(f"SGDClassifier测试集得分: {sgd.score(X_test_scaled, y_test):.4f}") # 增量更新 sgd.partial_fit(X_new_scaled, y_new)SGDClassifier使用随机梯度下降优化合页损失,本质上是一个线性SVM。它的训练速度很快,支持在线学习,但需要仔细调整学习率和正则化参数。alpha参数控制正则化强度,越大正则化越强。
6.5 模型可解释性分析
SVM的决策边界不像决策树那样容易解释,但也不是完全黑盒。对于线性核SVM,权重向量可以直接反映每个特征的重要性。对于RBF核,可以通过分析支持向量来理解模型的决策依据。
支持向量是那些位于间隔边界上或违反间隔的样本。它们决定了最终的决策边界。如果支持向量占总样本的比例很高,说明模型比较复杂,可能过拟合。如果支持向量很少,说明模型比较简洁。
svm_model = SVC(kernel='linear', C=1.0) svm_model.fit(X_train_scaled, y_train) # 线性核的权重 weights = svm_model.coef_[0] feature_importance = pd.DataFrame({ 'feature': data.feature_names, 'weight': weights, 'abs_weight': np.abs(weights) }).sort_values('abs_weight', ascending=False) print(feature_importance.head(10))这段代码展示了如何提取线性核SVM的特征权重。权重绝对值越大,说明该特征对决策的影响越大。正权重表示该特征增大时更倾向于正类,负权重则相反。
对于RBF核,可以用SHAP或LIME等解释工具做局部解释。这些工具可以告诉你对于某个具体样本,哪些特征对预测结果贡献最大。虽然计算成本较高,但在需要解释决策的场景下很有价值。
6.6 与其他算法的对比选型
SVM不是万能的,了解它的适用边界很重要。在中小规模、高维、非线性可分的数据上,SVM往往表现优异。但在大规模数据上,梯度提升树和随机森林通常更实用。
与逻辑回归相比,SVM在非线性可分数据上更有优势,但逻辑回归能直接输出概率,可解释性也更好。与神经网络相比,SVM在小样本上不容易过拟合,训练也更稳定,但在超大规模数据上不如深度学习灵活。
实际项目中,我通常会把SVM作为基准模型之一,和逻辑回归、随机森林一起跑一遍,看看哪个表现最好。如果SVM明显领先,再花时间精细调参。如果差距不大,就选更简单、更易解释的模型。
7. 写在最后的一些实操体会
SVM这个算法,入门容易精通难。几行代码就能跑起来,但要真正调出好结果,需要对C和gamma的耦合关系有直觉,需要理解核函数在做什么,需要知道什么场景下该换算法。我自己的经验是,不要一上来就上RBF核做网格搜索,先跑线性核看看基线,再决定要不要上非线性核。调参时先粗后细,先随机搜索缩小范围,再网格搜索精调。特征缩放千万别忘,这是最常见的翻车点。
还有一个容易被忽视的点:SVM的训练结果对随机种子不敏感,因为它的优化问题是凸的,有唯一全局最优解。但概率输出涉及交叉验证,会有随机性,设置random_state可以保证结果可复现。另外,如果用了网格搜索,最好固定交叉验证的随机种子,否则每次跑出来的最优参数可能略有不同。
最后分享一个我踩过的坑:有一次做文本分类,特征维度上万,我直接上了RBF核,训练了半个小时才跑完,结果还不如线性核。后来才想明白,高维稀疏数据本身就已经很容易线性可分了,RBF核反而引入了不必要的复杂性。所以核函数选择真的要看数据特性,不能盲目迷信RBF。