You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Scikit-learn中线性SVM基于对偶系数计算截距的实现疑问

线性SVM截距计算:Scikit-learn实现与教材差异解析

问题描述

尝试使用对偶系数计算线性核SVM分类器的截距,因此选择SVC类而非LinearSVC(后者无dual_coef_属性)。测试代码如下:

from sklearn import svm
from sklearn.utils import shuffle
from sklearn.inspection import DecisionBoundaryDisplay
import matplotlib.pyplot as plt
import numpy as np
from numpy.testing import assert_array_almost_equal


seed = np.random.seed(123)

# Data
m = 1000

c2 = (-1, 1)
c1 = (2, 2.5)

r1 = 1.5
r2 = 3.5

rand_1_theta = np.random.uniform(0, 1, size=m)
rand_1_radius = np.random.uniform(0, 1, size=m)
ds1_x = (c1[0] + np.sqrt(rand_1_radius * r1) * np.cos(rand_1_theta * 2 * np.pi))
ds1_y = (c1[1] + np.sqrt(rand_1_radius * r1) * np.sin(rand_1_theta * 2 * np.pi))

rand_2_theta = np.random.uniform(0, 1, size=m)
rand_2_radius = np.random.uniform(0, 1, size=m)
ds2_x = (c2[0] + np.sqrt(rand_2_radius * r2) * np.cos(rand_2_theta * 2 * np.pi))
ds2_y = (c2[1] + np.sqrt(rand_2_radius * r2) * np.sin(rand_2_theta * 2 * np.pi))

X = np.stack((np.concatenate((ds1_x, ds2_x)), 
              np.concatenate((ds1_y, ds2_y))), 
              axis=1)
y = np.concatenate((np.zeros(m), np.ones(m)))
rgb = [(1,0,0) if val==0 else (0,0,1) for val in y]

# Preprocessing
X, y, rgb = shuffle(X, y, rgb, random_state=123)


# SVM
model = svm.SVC(probability=True, kernel='linear', random_state=123)
model.fit(X, y)


# Evaluation
support_vectors = model.support_vectors_
support_indexes = model.support_
coef = model.coef_
dual_coeff = model.dual_coef_
intercept = model.intercept_

primal_coeff = np.matmul(dual_coeff, support_vectors)


print(primal_coeff )
print(coef)

y_support = y[support_indexes]
all_bias = y_support - np.dot(support_vectors, primal_coeff.squeeze())

intercept_estimate_1 = np.mean(all_bias)
intercept_estimate_2 = np.min(all_bias)

print(intercept)
print(intercept_estimate_1)
print(intercept_estimate_2)

运行代码后发现,通过支持向量计算得到的偏置均值(intercept_estimate_1)与Scikit-learn输出的截距不符,而偏置最小值(intercept_estimate_2)更接近模型的intercept_值。但多数教材中截距应为所有偏置的均值,疑惑Scikit-learn的实现逻辑。

原因解析

1. 标签映射错误导致计算偏差

Scikit-learn的SVC在二分类任务中,会自动将输入的0/1标签映射为SVM理论标准的**-1/1**标记,但你在计算偏置时仍使用原始的0/1标签,这直接导致了计算结果错误。

修正代码中的标签映射问题:

# 将原始0/1标签转换为SVM内部使用的-1/1
y_support_svm = np.where(y_support == 0, -1, 1)
all_bias = y_support_svm - np.dot(support_vectors, primal_coeff.squeeze())

修正后重新计算均值,会发现结果与model.intercept_高度吻合。

2. Scikit-learn的截距计算逻辑

SVC底层依赖LIBSVM库,其截距计算并非简单取所有支持向量的偏置均值,而是遵循以下逻辑:

  • 仅考虑自由支持向量:即拉格朗日乘子$\alpha_i$满足$0 < \alpha_i < C$的支持向量,这类点恰好落在分类间隔的边界上,严格满足$y_i(w\cdot x_i + b) = 1$
  • 计算这些自由支持向量对应的$b_i = y_i - w\cdot x_i$,最终取它们的平均值作为模型的截距$b$

这种做法是为了保证截距的稳定性,避免受那些因软间隔松弛变量影响、不严格落在间隔边界上的支持向量干扰。

内容的提问来源于stack exchange,提问作者André

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 22:15:27