WARNING
🧪 Beta公测版本提示:教程主体已完成,正在优化细节,欢迎大家提Issue反馈问题或建议。
ml04 支持向量机 (SVM) — 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损失函数的数学形式为:
为什么每次迭代要打乱数据? 这是 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 的数学定义为:
它在
(正确分类且在间隔外):梯度 = 0(无贡献) (在间隔内或错误分类):梯度 = -1
因此对
需要注意的是,Hinge Loss 在
第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 方法中,支持向量通过间隔值来近似识别:落在
第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-NN 中欧氏距离的展开技巧完全一致,避免显式广播大矩阵。
第5步:Gamma 参数的效果展示
python
gammas = [0.1, 1.0, 10.0, 50.0]:高斯函数的"钟形曲线"很宽,每个点的影响范围大 → 决策边界平滑,可能欠拟合 :适中的影响范围 → 边界复杂度恰当 :每个点影响范围很小 → 决策边界复杂,开始过拟合 :每个点几乎只影响自己 → 严重过拟合,决策边界围绕每个训练点形成"孤岛"
关键概念速查表
| 概念 | 数学形式 | 代码位置 | 关键说明 |
|---|---|---|---|
| 间隔 | margin | 样本到超平面的距离 | |
| Hinge Loss | fit() 中 | 仅在违反间隔时产生梯度 | |
| L2 正则化 | dw = 2*lambda_*w | 权重衰减 | |
lambda_ = 1/(2*C) | |||
| 子梯度 | dw -= y_i*x_i | 不可导点用子梯度 | |
| 支持向量 | margin | get_support_vector_mask() | |
| RBF 核 | rbf_kernel() | 无限维映射 | |
| 影响半径 | gamma | 小 |
源码位置
clone 后打开(相对仓库根目录):
docs/ml/classic/svm/code/demo.py