Skip to content

WARNING

🧪 Beta公测版本提示:教程主体已完成,正在优化细节,欢迎大家提Issue反馈问题或建议。

ml04 支持向量机 (SVM) — demo.py 代码详解

Download demo.py

运行方式

bash
cd docs/ml/classic/svm/code
python demo.py

代码逐段详解

第1步:LinearSVM 类 — Hinge Loss + SGD

python
class LinearSVM:
    def fit(self, X, y):
        lambda_ = 1.0 / (2.0 * self.C)
        for epoch in range(self.n_epochs):
            for each sample (x_i, y_i):
                margin = y_i * (w^T x_i + b)
                dw = 2 * lambda_ * w
                if margin < 1:
                    dw -= y_i * x_i
                    db -= y_i
                w -= lr * dw
                b -= lr * db

损失函数的数学形式为:

J(w,b)=1ni=1nmax(0,1yi(wTxi+b))+λw2

λ=1/(2C) 的关系:sklearn 用 C(惩罚参数,越大越像硬间隔),而数学公式中通常用 λ(正则化系数)。它们的转换关系为 λ=12C。因此 Cλ0(无正则化,硬间隔)。

为什么每次迭代要打乱数据? 这是 SGD 的标准做法:如果不打乱,数据的顺序会影响梯度更新的路径,可能导致收敛到局部最优或震荡。np.random.permutation 在每轮开始前打乱索引。

第2步:Hinge Loss 的子梯度

python
if margin < 1:
    dw -= y_i * x_i   # Hinge Loss 对 w 的子梯度
    db -= y_i          # Hinge Loss 对 b 的梯度

Hinge Loss 的数学定义为:

hinge(y,f(x))=max(0,1yf(x))

它在 z=yf(x) 上的导数是分段常数:

  • z1(正确分类且在间隔外):梯度 = 0(无贡献)
  • z<1(在间隔内或错误分类):梯度 = -1

因此对 w 的链式求导:

max(0,1y(wTx+b))w={yxif y(wTx+b)<10otherwise

需要注意的是,Hinge Loss 在 z=1 处不可导(有一个尖点),这里使用的是子梯度(subgradient)——即任意在导数不存在处的"单侧导数"都适用。

第3步:支持向量的识别

python
def get_support_vector_mask(self, X, y):
    y_svm = np.where(y <= 0, -1, 1)
    margins = y_svm * self.decision_function(X)
    sv_mask = (margins >= 0.99) & (margins <= 1.01)
    return sv_mask

在 SGD 方法中,支持向量通过间隔值来近似识别:落在 yi(wTxi+b)1 附近的点(容差 ±0.01),即位于间隔边界上的点。这些点是在训练过程中"被推动到边界上"的——它们一直不满足 yf>1,因此持续贡献梯度直到被推到边界处。

第4步:RBF 核函数

python
def rbf_kernel(X, Y, gamma):
    sq_X = np.sum(X ** 2, axis=1, keepdims=True)
    sq_Y = np.sum(Y ** 2, axis=1)
    sq_dists = np.maximum(sq_X + sq_Y - 2 * X @ Y.T, 0.0)
    return np.exp(-gamma * sq_dists)

RBF 核:

K(x,y)=exp(γxy2)

展开平方距离:

xy2=x2+y22xTy

这与 k-NN 中欧氏距离的展开技巧完全一致,避免显式广播大矩阵。

第5步:Gamma 参数的效果展示

python
gammas = [0.1, 1.0, 10.0, 50.0]

γ 控制 RBF 核中每个训练样本的"影响半径":

  • γ=0.1:高斯函数的"钟形曲线"很宽,每个点的影响范围大 → 决策边界平滑,可能欠拟合
  • γ=1.0:适中的影响范围 → 边界复杂度恰当
  • γ=10.0:每个点影响范围很小 → 决策边界复杂,开始过拟合
  • γ=50.0:每个点几乎只影响自己 → 严重过拟合,决策边界围绕每个训练点形成"孤岛"

γ 的支持向量数量也反映了过拟合程度——γ 越大,支持向量越多(几乎所有训练样本都变成支持向量),模型的 VC 维越高。

关键概念速查表

概念数学形式代码位置关键说明
间隔yi(wTxi+b)/|w|margin样本到超平面的距离
Hinge Lossmax(0,1yf(x))fit()仅在违反间隔时产生梯度
L2 正则化λ|w|2dw = 2*lambda_*w权重衰减
λCλ=1/(2C)lambda_ = 1/(2*C)C 大 = 弱正则化
子梯度yxyf<1dw -= y_i*x_i不可导点用子梯度
支持向量margin 1get_support_vector_mask()αi>0 的样本
RBF 核exp(γ|xy|2)rbf_kernel()无限维映射
γ 参数影响半径gamma 平滑, 大 复杂

源码位置

clone 后打开(相对仓库根目录):

docs/ml/classic/svm/code/demo.py