为何sklearn.svm.LinearSVC执行耗时远超sklearn.svm.SVC?
我在使用scikit-learn的LinearSVC和SVC类进行超参数调优时遇到了一个反常现象:虽然SVC的参数搜索次数是LinearSVC的10倍(前者是10组C×10组gamma=100组参数,后者仅10组C参数),但LinearSVC的总耗时高达1087635ms,而SVC仅用了36961ms。我原本以为LinearSVC作为线性模型性能会更优,想请教这一现象的原因。使用的数据集是Olivetti人脸数据集,调优代码如下:
from sklearn.svm import LinearSVC from sklearn.svm import SVC from sklearn.model_selection import GridSearchCV import numpy as np import time #LinearSVC hyperparameter tuning ----------------------- param_grid = [ {'svc__C': np.logspace(-3,3, num=10)} ] full_pipeline = Pipeline([ ("preprocessing", StandardScaler(with_mean=False)), ("svc",LinearSVC(random_state=0)) ]) svc_rnd_search = GridSearchCV(full_pipeline, param_grid=param_grid, cv=10, scoring='accuracy',n_jobs=-1) # Measure the execution time start_time = time() #Run search svc_rnd_search.fit(X_train, y_train) # Calculate the execution time end_time = time() execution_time_ms = (end_time - start_time) * 1000 print(f"Execution time: {execution_time_ms:.3f}ms") #SVC hyperparameter tuning----------------------------------- param_grid = [ {'svc__C': np.logspace(-2,3, num=10), 'svc__gamma': np.logspace(-5,1, num=10), 'svc__kernel': ['rbf']} ] full_pipeline = Pipeline([ ("preprocessing", StandardScaler(with_mean=False)), ("svc",SVC()) ]) svc_rnd_search = GridSearchCV(full_pipeline, param_grid=param_grid, cv=10, scoring='accuracy',n_jobs=-1) # Measure the execution time start_time = time() #Run search svc_rnd_search.fit(X_train, y_train) # Calculate the execution time end_time = time() execution_time_ms = (end_time - start_time) * 1000 print(f"Execution time: {execution_time_ms:.3f}ms")
核心原因分析
计算复杂度的实际差异
Olivetti人脸数据集是400个样本、4096维特征的高维小样本场景。LinearSVC的训练时间复杂度是O(样本数×特征数),单次训练要处理400×4096=163万次计算;而用rbf核的SVC时间复杂度是O(样本数²),单次训练仅400²=16万次计算。哪怕SVC要跑100组参数,总计算量和LinearSVC的10组参数差不多,但libsvm对核函数计算有缓存优化,实际跑起来更快。多分类策略的开销不同
LinearSVC默认用「一对其余」策略处理多分类:40个类别就要训练40个二分类器,每个分类器都要遍历全量高维特征,叠加起来的开销很大。而SVC默认用「一对一」策略,虽然理论上要训练780个二分类器,但libsvm对这个策略做了专门优化,核函数计算时能共享中间结果,实际开销并没有看起来那么夸张。收敛条件的影响
LinearSVC默认的max_iter=1000,在高维数据下很多模型没法在1000次迭代内收敛,只能跑满上限,硬生生拉长了训练时间。而SVC的收敛条件是基于损失变化的阈值(tol=1e-3),在小样本场景下很容易快速达到收敛标准,提前停止训练。并行计算的效率差异
虽然两者都开了n_jobs=-1用全量CPU并行,但LinearSVC单任务耗时太长,并行调度的开销(比如进程通信、任务分配)占比变大,实际加速效果打了折扣;而SVC单任务耗时短,并行调度的开销几乎可以忽略,能接近线性加速,所以总耗时反而更低。
内容的提问来源于stack exchange,提问作者ghost wizard

