当前位置: 首页 > news >正文

2025全国大学生数学建模B题思路+模型+代码9.4开赛后第一时间更新,备战国赛,算法解析支持向量机(SVM)

2025全国大学生数学建模B题思路+模型+代码9.4开赛后第一时间更新,备战国赛,算法解析支持向量机(SVM),完整内容更新见文末名片

算法讲解:

支持向量机(SVM)原理与模型步骤(小白友好版)

一、算法原理:从“分班级”到“最优超平面”

支持向量机(SVM)是一种二分类模型,核心思想可以类比为:在教室中用一条线分隔两个班级的学生,这条线不仅要分开所有人,还要让两个班级最靠近线的学生之间的距离(“间隔”)尽可能大——这样即使有人稍微移动,也不容易站错队。这条“最优分隔线”在高维空间中称为超平面

1. 线性可分SVM(硬间隔SVM):数据完全可分的情况

当两类样本能用一条直线(二维)、平面(三维)或超平面(高维)完全分开时,称为“线性可分”。此时SVM的目标是找到最大间隔超平面

1.1 超平面:高维空间的“分隔线”

nnn维特征空间中,超平面的数学表达式为:
w⋅x+b=0(1)\boldsymbol{w} \cdot \boldsymbol{x} + b = 0 \tag{1}wx+b=0(1)
w=(w1,w2,...,wn)T\boldsymbol{w} = (w_1, w_2, ..., w_n)^Tw=(w1,w2,...,wn)T法向量(决定超平面方向,类似分隔线的斜率);
bbb偏置(决定超平面位置,类似分隔线的截距);
x=(x1,x2,...,xn)T\boldsymbol{x} = (x_1, x_2, ..., x_n)^Tx=(x1,x2,...,xn)T:样本的特征向量(比如学生的身高、体重等特征)。

举例:在二维空间(n=2n=2n=2),超平面就是直线w1x1+w2x2+b=0w_1x_1 + w_2x_2 + b = 0w1x1+w2x2+b=0;在三维空间,就是平面w1x1+w2x2+w3x3+b=0w_1x_1 + w_2x_2 + w_3x_3 + b = 0w1x1+w2x2+w3x3+b=0

1.2 间隔:衡量“分隔效果”的指标

为了找到“最好”的超平面,需要定义样本到超平面的“距离”——即间隔。间隔有两种:

1.2.1 函数间隔:受缩放影响的“伪距离”

对样本点(xi,yi)(\boldsymbol{x}_i, y_i)(xi,yi)yi=+1y_i=+1yi=+1111,表示类别),函数间隔定义为:
γ^i=yi(w⋅xi+b)(2)\hat{\gamma}_i = y_i (\boldsymbol{w} \cdot \boldsymbol{x}_i + b) \tag{2}γ^i=yi(wxi+b)(2)
含义:若样本被正确分类(yiy_iyiw⋅xi+bw·x_i + bwxi+b同号),则γ^i>0\hat{\gamma}_i>0γ^i>0,值越大说明样本离超平面越“远”;若γ^i≤0\hat{\gamma}_i \leq 0γ^i0,则样本被分错。

问题:函数间隔受w\boldsymbol{w}wbbb的缩放影响!
例如:若w→2w\boldsymbol{w} \to 2\boldsymbol{w}w2wb→2bb \to 2bb2b,超平面本身不变(2w⋅x+2b=02\boldsymbol{w}·x + 2b=02wx+2b=0等价于w⋅x+b=0\boldsymbol{w}·x + b=0wx+b=0),但函数间隔γ^i\hat{\gamma}_iγ^i会翻倍。因此,函数间隔不能反映真实距离。

1.2.2 几何间隔:真实的“垂直距离”

为消除缩放影响,将函数间隔除以∥w∥\|\boldsymbol{w}\|w(法向量的模长),得到几何间隔(真实距离):
γi=γ^i∥w∥=yi(w⋅xi+b)∥w∥(3)\gamma_i = \frac{\hat{\gamma}_i}{\|\boldsymbol{w}\|} = \frac{y_i (\boldsymbol{w} \cdot \boldsymbol{x}_i + b)}{\|\boldsymbol{w}\|} \tag{3}γi=wγ^i=wyi(wxi+b)(3)
含义:样本到超平面的垂直距离,与w\boldsymbol{w}wbbb的缩放无关。例如,二维空间中,点(x1,x2)(x_1,x_2)(x1,x2)到直线w1x1+w2x2+b=0w_1x_1 + w_2x_2 + b=0w1x1+w2x2+b=0的距离公式就是∣w1x1+w2x2+b∣w12+w22\frac{|w_1x_1 + w_2x_2 + b|}{\sqrt{w_1^2 + w_2^2}}w12+w22w1x1+w2x2+b,这里乘yiy_iyi是为了保证距离非负(正确分类时yiy_iyi与括号内同号)。

1.3 核心目标:最大化“最小几何间隔”

SVM的目标是找到超平面,使得所有样本中最小的几何间隔尽可能大(即“最大间隔”)。数学表达为:
max⁡w,bγs.t.γi≥γ(∀i)(4)\max_{\boldsymbol{w}, b} \gamma \quad \text{s.t.} \quad \gamma_i \geq \gamma \quad (\forall i) \tag{4}w,bmaxγs.t.γiγ(i)(4)
其中,γ=min⁡iγi\gamma = \min_i \gamma_iγ=miniγi(所有样本的最小几何间隔),约束条件表示“每个样本的几何间隔至少为γ\gammaγ”。

1.4 优化问题转化:从“最大化γ”到“最小化||w||²”

直接求解式(4)较复杂,需简化。注意到几何间隔γ=1∥w∥\gamma = \frac{1}{\|\boldsymbol{w}\|}γ=w1(推导见下方),因此“最大化γ\gammaγ”等价于“最小化∥w∥2\|\boldsymbol{w}\|^2w2”。

推导过程(关键!):

  1. 对所有样本,几何间隔γi≥γ\gamma_i \geq \gammaγiγ,即yi(w⋅xi+b)∥w∥≥γ\frac{y_i (\boldsymbol{w} \cdot \boldsymbol{x}_i + b)}{\|\boldsymbol{w}\|} \geq \gammawyi(wxi+b)γ
  2. 两边同乘∥w∥\|\boldsymbol{w}\|wyi(w⋅xi+b)≥γ∥w∥y_i (\boldsymbol{w} \cdot \boldsymbol{x}_i + b) \geq \gamma \|\boldsymbol{w}\|yi(wxi+b)γw
  3. 为简化,令γ∥w∥=1\gamma \|\boldsymbol{w}\| = 1γw=1(归一化处理,因w\boldsymbol{w}wbbb可缩放,不影响超平面),则γ=1∥w∥\gamma = \frac{1}{\|\boldsymbol{w}\|}γ=w1
  4. 此时目标“最大化γ\gammaγ”等价于“最大化1∥w∥\frac{1}{\|\boldsymbol{w}\|}w1”,即“最小化∥w∥2\|\boldsymbol{w}\|^2w2”(因∥w∥\|\boldsymbol{w}\|w越小,1∥w∥\frac{1}{\|\boldsymbol{w}\|}w1越大)。

最终优化问题变为(凸二次规划问题):
min⁡w,b12∥w∥2s.t.yi(w⋅xi+b)≥1(∀i)(6)\min_{\boldsymbol{w}, b} \frac{1}{2} \|\boldsymbol{w}\|^2 \quad \text{s.t.} \quad y_i (\boldsymbol{w} \cdot \boldsymbol{x}_i + b) \geq 1 \quad (\forall i) \tag{6}w,bmin21w2s.t.yi(wxi+b)1(i)(6)
目标函数12∥w∥2\frac{1}{2}\|\boldsymbol{w}\|^221w2(加1/2是为了求导后简化);
约束条件yi(w⋅xi+b)≥1y_i (\boldsymbol{w} \cdot \boldsymbol{x}_i + b) \geq 1yi(wxi+b)1(即所有样本的函数间隔至少为1,因γ∥w∥=1\gamma\|\boldsymbol{w}\|=1γw=1)。

1.5 对偶问题:用拉格朗日乘子法“降维”

直接求解式(6)(原问题)需处理高维变量w\boldsymbol{w}w,通过拉格朗日乘子法转化为“对偶问题”,可利用样本内积简化计算。

1.5.1 拉格朗日函数:融合目标与约束

定义拉格朗日函数(将约束条件“罚”到目标函数中):
L(w,b,α)=12∥w∥2∑i=1mαi[yi(w⋅xi+b)1](7)\mathcal{L}(\boldsymbol{w}, b, \boldsymbol{\alpha}) = \frac{1}{2} \|\boldsymbol{w}\|^2 \sum_{i=1}^m \alpha_i \left[ y_i (\boldsymbol{w} \cdot \boldsymbol{x}_i + b) 1 \right] \tag{7}L(w,b,α)=21w2i=1mαi[yi(wxi+b)1](7)
其中,αi≥0\alpha_i \geq 0αi0(拉格朗日乘子,每个样本对应一个),mmm为样本数。

1.5.2 KKT条件:最优解的“必要条件”

对偶问题需满足KKT条件(凸优化问题的最优解必满足),核心包括:

  1. 梯度为0(极值点导数为0):
    w\boldsymbol{w}w求导:KaTeX parse error: \tag works only in display equations
    w\boldsymbol{w}w由样本xi\boldsymbol{x}_ixi、标签yiy_iyi和乘子αi\alpha_iαi线性表示!)

    bbb求导:KaTeX parse error: \tag works only in display equations
    αiyi\alpha_i y_iαiyi的总和为0,保证正负样本平衡)

  2. 互补松弛条件KaTeX parse error: \tag works only in display equations
    含义:对每个样本,要么αi=0\alpha_i=0αi=0,要么yi(w⋅xi+b)=1y_i (\boldsymbol{w} \cdot \boldsymbol{x}_i + b) = 1yi(wxi+b)=1(样本在“间隔边界”上)。

1.5.3 对偶问题:只含样本内积的优化

将式(8)(9)代入拉格朗日函数(7),消去w\boldsymbol{w}wbbb,得到对偶问题:
max⁡α∑i=1mαi12∑i=1m∑j=1mαiαjyiyj(xi⋅xj)(11)\max_{\boldsymbol{\alpha}} \sum_{i=1}^m \alpha_i \frac{1}{2} \sum_{i=1}^m \sum_{j=1}^m \alpha_i \alpha_j y_i y_j (\boldsymbol{x}_i \cdot \boldsymbol{x}_j) \tag{11}αmaxi=1mαi21i=1mj=1mαiαjyiyj(xixj)(11)
s.t.∑i=1mαiyi=0,αi≥0(∀i)\text{s.t.} \quad \sum_{i=1}^m \alpha_i y_i = 0, \quad \alpha_i \geq 0 \quad (\forall i)s.t.i=1mαiyi=0,αi0(i)
优势:变量从w\boldsymbol{w}w(高维)变为α\boldsymbol{\alpha}αmmm维,样本数),且目标函数只含样本内积xi⋅xj\boldsymbol{x}_i \cdot \boldsymbol{x}_jxixj,为后续核函数铺垫。

1.6 支持向量:决定超平面的“关键样本”

由互补松弛条件(10):若αi>0\alpha_i > 0αi>0,则yi(w⋅xi+b)=1y_i (\boldsymbol{w} \cdot \boldsymbol{x}_i + b) = 1yi(wxi+b)=1——即样本位于“间隔边界”上,称为支持向量(Support Vector)。

特点
支持向量数量少(稀疏性),大部分样本αi=0\alpha_i=0αi=0(不影响超平面);
超平面完全由支持向量决定(移除非支持向量,超平面不变)。

举例:二维空间中,分隔两类点的直线仅由几个“边界点”(支持向量)决定,其他点离得远,不影响直线位置。

2. 线性不可分SVM(软间隔SVM):允许“少量错误”

实际数据常含噪声或非线性,无法用直线完全分开(线性不可分)。此时需允许部分样本不满足间隔约束,但要“惩罚”这种错误——引入软间隔SVM

2.1 松弛变量:允许“偏离”的“宽容度”

对每个样本iii,引入松弛变量ξi≥0\xi_i \geq 0ξi0,表示样本iii的“偏离程度”(ξi=0\xi_i=0ξi=0:无偏离;ξi>0\xi_i>0ξi>0:偏离间隔约束)。

优化问题变为:
min⁡w,b,ξ12∥w∥2+C∑i=1mξi(12)\min_{\boldsymbol{w}, b, \xi} \frac{1}{2} \|\boldsymbol{w}\|^2 + C \sum_{i=1}^m \xi_i \tag{12}w,b,ξmin21w2+Ci=1mξi(12)
s.t.yi(w⋅xi+b)≥1ξi(∀i),ξi≥0(∀i)(13)\text{s.t.} \quad y_i (\boldsymbol{w} \cdot \boldsymbol{x}_i + b) \geq 1 \xi_i \quad (\forall i), \quad \xi_i \geq 0 \quad (\forall i) \tag{13}s.t.yi(wxi+b)1ξi(i),ξi0(i)(13)

目标函数12∥w∥2\frac{1}{2}\|\boldsymbol{w}\|^221w2(最大化间隔)+C∑ξi+ C\sum\xi_i+Cξi(最小化总偏离,C>0C>0C>0为惩罚参数);
约束条件:样本的函数间隔至少为1ξi1 \xi_i1ξi(允许偏离,但偏离越大ξi\xi_iξi越大,惩罚越重)。

2.2 惩罚参数CCC的作用

CCC越大:对偏离的惩罚越重,模型越“严格”,倾向于少允许错误(可能过拟合,间隔小);
CCC越小:允许更多偏离,模型更“宽容”,间隔可能更大(可能欠拟合)。

2.3 对偶问题与支持向量分类

软间隔的对偶问题与硬间隔类似,仅增加αi≤C\alpha_i \leq CαiC(由拉格朗日乘子法推导,此处省略细节)。此时支持向量分为三类(由互补松弛条件推导):

| αi\alpha_iαi取值 | 含义 |
|||
| αi∈(0,C)\alpha_i \in (0, C)αi(0,C) | ξi=0\xi_i=0ξi=0,样本在间隔边界上(“标准支持向量”) |
| αi=C\alpha_i = Cαi=Cξi∈(0,1)\xi_i \in (0,1)ξi(0,1) | 样本在间隔内(未分错,但偏离边界) |
| αi=C\alpha_i = Cαi=Cξi≥1\xi_i \geq 1ξi1 | 样本被分错(偏离严重,yi(w⋅xi+b)<0y_i (\boldsymbol{w}·x_i + b) < 0yi(wxi+b)<0) |

3. 非线性SVM:用核函数“升维”解决非线性

若数据在原始空间(如二维)非线性可分(例如“异或”问题:(0,0)、(1,1)为类1,(0,1)、(1,0)为类2,无法用直线分开),需通过核函数将样本映射到高维空间,使其线性可分。

3.1 核函数:高维内积的“低维计算”

映射函数:设ϕ:X→H\phi: \mathcal{X} \to \mathcal{H}ϕ:XH,将原始空间X\mathcal{X}X的样本x\boldsymbol{x}x映射到高维特征空间H\mathcal{H}Hϕ(x)\phi(\boldsymbol{x})ϕ(x)
核函数:直接计算高维空间内积ϕ(x)⋅ϕ(z)\phi(\boldsymbol{x})·\phi(\boldsymbol{z})ϕ(x)ϕ(z),但无需显式写出ϕ\phiϕ,公式为:
K(x,z)=ϕ(x)⋅ϕ(z)(15)K(\boldsymbol{x}, \boldsymbol{z}) = \phi(\boldsymbol{x}) \cdot \phi(\boldsymbol{z}) \tag{15}K(x,z)=ϕ(x)ϕ(z)(15)
(“核技巧”:用低维计算替代高维内积,避免“维度灾难”)

3.2 常见核函数及适用场景

| 核函数类型 | 公式 | 适用场景 |
||||
| 线性核 | K(x,z)=x⋅zK(\boldsymbol{x}, \boldsymbol{z}) = \boldsymbol{x} \cdot \boldsymbol{z}K(x,z)=xz | 线性可分数据(退化为线性SVM) |
| 多项式核 | K(x,z)=(x⋅z+c)dK(\boldsymbol{x}, \boldsymbol{z}) = (\boldsymbol{x} \cdot \boldsymbol{z} + c)^dK(x,z)=(xz+c)d | 中等非线性数据(ddd为次数,c≥0c\geq0c0) |
| 高斯核(RBF) | K(x,z)=exp⁡(γ∥xz∥2)K(\boldsymbol{x}, \boldsymbol{z}) = \exp\left(\gamma \|\boldsymbol{x} \boldsymbol{z}\|^2\right)K(x,z)=exp(γxz2) | 强非线性数据(γ>0\gamma>0γ>0控制局部性,γ\gammaγ大→局部影响强) |
| Sigmoid核 | K(x,z)=tanh⁡(βx⋅z+c)K(\boldsymbol{x}, \boldsymbol{z}) = \tanh(\beta \boldsymbol{x} \cdot \boldsymbol{z} + c)K(x,z)=tanh(βxz+c) | 模拟神经网络(较少用) |

3.3 非线性SVM的决策函数

对偶问题目标函数用核函数替换内积:
max⁡α∑i=1mαi12∑i=1m∑j=1mαiαjyiyjK(xi,xj)(16)\max_{\boldsymbol{\alpha}} \sum_{i=1}^m \alpha_i \frac{1}{2} \sum_{i=1}^m \sum_{j=1}^m \alpha_i \alpha_j y_i y_j K(\boldsymbol{x}_i, \boldsymbol{x}_j) \tag{16}αmaxi=1mαi21i=1mj=1mαiαjyiyjK(xi,xj)(16)
(约束条件:∑αiyi=0\sum \alpha_i y_i=0αiyi=00≤αi≤C0 \leq \alpha_i \leq C0αiC

求解后,分类决策函数为:
f(x)=sign(∑i=1mαiyiK(xi,x)+b)(17)f(\boldsymbol{x}) = \text{sign}\left( \sum_{i=1}^m \alpha_i y_i K(\boldsymbol{x}_i, \boldsymbol{x}) + b \right) \tag{17}f(x)=sign(i=1mαiyiK(xi,x)+b)(17)
含义:对新样本x\boldsymbol{x}x,计算其与所有支持向量(αi>0\alpha_i>0αi>0)的核函数值,加权求和后加bbb,用sign\text{sign}sign函数输出类别(+1+1+1111)。

二、模型步骤:手把手教你用SVM

Step 1:数据预处理

特征标准化:SVM对特征尺度敏感!需将所有特征标准化(如xi→xiμσx_i \to \frac{x_i \mu}{\sigma}xiσxiμμ\muμ为均值,σ\sigmaσ为标准差),避免大尺度特征主导超平面。
标签统一:将类别标签转化为y∈{+1,1}y \in \{+1, 1\}y{+1,1}(SVM要求标签为+1/1)。

Step 2:选择核函数与参数

核函数选择
线性数据(可画直线分开):选线性核
非线性数据(如环形分布):选高斯核(RBF)(最常用,适用性强);
中等非线性:可选多项式核(需调次数ddd)。

参数调优:通过交叉验证(如5折交叉验证)选择最优参数:
惩罚参数CCC(控制过拟合);
核函数参数(如RBF核的γ\gammaγ,多项式核的ddd)。

Step 3:求解对偶问题,得到αi\alpha_iαi

对偶问题是凸二次规划问题,实际中用SMO算法(序列最小优化,高效求解)计算αi\alpha_iαi
结果:大部分αi=0\alpha_i=0αi=0,仅支持向量的αi>0\alpha_i>0αi>0(稀疏性)。

Step 4:计算偏置bbb

选择一个αs∈(0,C)\alpha_s \in (0, C)αs(0,C)的支持向量(xs,ys)(\boldsymbol{x}_s, y_s)(xs,ys)(此时ξs=0\xi_s=0ξs=0,由软间隔互补松弛条件),代入ys(w⋅xs+b)=1y_s (\boldsymbol{w}·\boldsymbol{x}_s + b) = 1ys(wxs+b)=1,结合w=∑αiyixi\boldsymbol{w} = \sum \alpha_i y_i \boldsymbol{x}_iw=αiyixi,解得:
b=ys∑i=1mαiyiK(xi,xs)(18)b = y_s \sum_{i=1}^m \alpha_i y_i K(\boldsymbol{x}_i, \boldsymbol{x}_s) \tag{18}b=ysi=1mαiyiK(xi,xs)(18)
(为提高稳定性,通常用所有αi∈(0,C)\alpha_i \in (0,C)αi(0,C)的支持向量求bbb的平均值)

Step 5:构建决策函数并预测

对新样本x\boldsymbol{x}x,代入式(17):
f(x)=sign(∑i=1mαiyiK(xi,x)+b)f(\boldsymbol{x}) = \text{sign}\left( \sum_{i=1}^m \alpha_i y_i K(\boldsymbol{x}_i, \boldsymbol{x}) + b \right)f(x)=sign(i=1mαiyiK(xi,x)+b)
输出+1+1+1111,即样本类别。

总结:SVM为什么好用?

核心优势:最大化间隔→泛化能力强;支持向量稀疏→计算高效;核函数→处理非线性问题。
适用场景:小样本、中高维数据(如图像、文本分类),不适合超大数据集(训练慢)。

核心公式速查表

| 名称 | 公式 |
|||
| 超平面 | w⋅x+b=0\boldsymbol{w} \cdot \boldsymbol{x} + b = 0wx+b=0 |
| 几何间隔 | γi=yi(w⋅xi+b)∥w∥\gamma_i = \frac{y_i (\boldsymbol{w} \cdot \boldsymbol{x}_i + b)}{\|\boldsymbol{w}\|}γi=wyi(wxi+b) |
| 对偶问题(非线性) | max⁡α∑i=1mαi12∑i,jαiαjyiyjK(xi,xj)\max_{\boldsymbol{\alpha}} \sum_{i=1}^m \alpha_i \frac{1}{2} \sum_{i,j} \alpha_i \alpha_j y_i y_j K(\boldsymbol{x}_i, \boldsymbol{x}_j)maxαi=1mαi21i,jαiαjyiyjK(xi,xj) |
| 决策函数 | f(x)=sign(∑i=1mαiyiK(xi,x)+b)f(\boldsymbol{x}) = \text{sign}\left( \sum_{i=1}^m \alpha_i y_i K(\boldsymbol{x}_i, \boldsymbol{x}) + b \right)f(x)=sign(i=1mαiyiK(xi,x)+b) |

Python实现代码:

支持向量机(SVM)算法实现与数据模拟(修正版)

一、常见错误预判与避免措施(保持不变)
  1. 数据线性不可分问题:模拟数据时未确保线性可分,导致硬间隔SVM无法收敛。
    避免措施:生成数据时使用明显分离的两个高斯分布,确保线性可分。

  2. SMO算法实现错误:变量选择逻辑、alpha更新公式或KKT条件判断错误。
    避免措施:严格遵循SMO步骤,实现alpha对选择、误差计算、边界裁剪(L/H)和阈值b更新逻辑。

  3. 核函数计算错误:矩阵维度不匹配或核函数参数(如RBF的gamma)设置不当。
    避免措施:先实现线性核验证基础功能,使用np.linalg.norm确保距离计算正确。

  4. 收敛条件设置不当:迭代次数不足或容忍误差(tol)过大导致模型未收敛。
    避免措施:设置合理最大迭代次数(如1000)和小容忍误差(1e3)。

  5. 矩阵运算维度错误:特征矩阵或核矩阵维度不匹配(如样本数与特征数混淆)。
    避免措施:显式定义矩阵维度(m样本数,n特征数),使用reshape确保向量维度正确。

二、完整代码实现(修正版,增强注释)
import numpy as np  # 导入数值计算库
import matplotlib.pyplot as plt  # 导入可视化库def generate_linear_separable_data(n_samples=100, seed=42):"""生成线性可分的二分类模拟数据参数:n_samples: 总样本数(默认100)seed: 随机种子(默认42,确保结果可复现)返回:X: 特征矩阵,shape=(n_samples, 2),每行是一个2维样本y: 标签向量,shape=(n_samples,),取值为1(类别1)或1(类别1)"""np.random.seed(seed)  # 设置随机种子,保证每次运行生成相同数据# 生成类别1样本:均值(2,2),协方差矩阵[[1,0],[0,1]](各特征独立,方差为1)class1 = np.random.multivariate_normal(mean=[2, 2],  # 均值向量(中心位置)cov=[[1, 0], [0, 1]],  # 协方差矩阵(控制数据分散程度)size=n_samples//2  # 样本数:总样本的一半)# 生成类别1样本:均值(2,2),协方差矩阵[[1,0],[0,1]](与类别1明显分离)class2 = np.random.multivariate_normal(mean=[2, 2],  # 均值向量(与类别1中心距离远,确保线性可分)cov=[[1, 0], [0, 1]],  # 协方差矩阵(与类别1相同,保证分布均匀)size=n_samples  n_samples//2  # 样本数:总样本的另一半(处理奇数情况))X = np.vstack((class1, class2))  # 垂直堆叠两类样本,形成特征矩阵(n_samples, 2)# 生成标签:前半为1,后半为1y = np.hstack((np.ones(n_samples//2), np.ones(n_samples  n_samples//2)))return X, y  # 返回特征矩阵和标签向量class SVM:"""支持向量机模型(基于SMO算法优化,支持线性核、多项式核、RBF核)"""def __init__(self, kernel='linear', C=1.0, gamma=1.0, degree=3, tol=1e3, max_iter=1000):"""初始化SVM模型超参数参数:kernel: 核函数类型(默认'linear'),可选'linear'(线性)、'poly'(多项式)、'rbf'(高斯核)C: 软间隔惩罚参数(默认1.0),C→∞时为硬间隔SVM,C越小容错能力越强gamma: RBF/多项式核的核系数(默认1.0),控制核函数的影响范围degree: 多项式核的次数(默认3),仅在kernel='poly'时有效tol: KKT条件判断的容忍误差(默认1e3),控制收敛精度max_iter: 最大迭代次数(默认1000),防止算法陷入无限循环"""self.kernel = kernel          # 核函数类型self.C = C                    # 软间隔惩罚参数self.gamma = gamma            # 核函数系数(RBF/多项式)self.degree = degree          # 多项式核次数self.tol = tol                # KKT条件容忍误差self.max_iter = max_iter      # 最大迭代次数self.alpha = None             # 拉格朗日乘子(待优化参数,shape=(n_samples,))self.b = 0.0                  # 决策阈值(偏置项)self.support_vectors = None   # 支持向量特征(shape=(n_sv, n_features))self.support_alpha = None     # 支持向量对应的alpha(shape=(n_sv,))self.support_y = None         # 支持向量对应的标签(shape=(n_sv,))def _kernel_function(self, x1, x2):"""计算两个样本向量的核函数值(私有方法,内部调用)参数:x1: 第一个样本向量,shape=(n_features,)x2: 第二个样本向量,shape=(n_features,)返回:核函数值K(x1, x2)(标量)"""if self.kernel == 'linear':# 线性核:K(x1, x2) = x1·x2(内积)return np.dot(x1, x2)elif self.kernel == 'poly':# 多项式核:K(x1, x2) = (gamma·x1·x2 + 1)^degreereturn (self.gamma * np.dot(x1, x2) + 1) ** self.degreeelif self.kernel == 'rbf':# RBF核(高斯核):K(x1, x2) = exp(gamma·||x1x2||²),gamma>0return np.exp(self.gamma * np.linalg.norm(x1  x2) ** 2)else:# 不支持的核函数类型,抛出异常raise ValueError("不支持的核函数!可选:'linear'/'poly'/'rbf'")def _compute_kernel_matrix(self, X):"""预计算核矩阵(加速训练,私有方法)核矩阵K[i,j] = K(X[i], X[j]),存储所有样本对的核函数值参数:X: 训练特征矩阵,shape=(n_samples, n_features)返回:K: 核矩阵,shape=(n_samples, n_samples)"""n_samples = X.shape[0]  # 样本数量K = np.zeros((n_samples, n_samples))  # 初始化核矩阵(全0)# 双重循环计算所有样本对的核函数值for i in range(n_samples):for j in range(n_samples):K[i, j] = self._kernel_function(X[i], X[j])  # 计算K[i,j]return K  # 返回核矩阵def _compute_error(self, i, K, y):"""计算样本i的预测误差E_i = f(x_i)  y_i(私有方法)其中f(x_i)是SVM对样本i的预测值:f(x_i) = sum(alpha_j·y_j·K[i,j]) + b参数:i: 样本索引(0 <= i < n_samples)K: 核矩阵,shape=(n_samples, n_samples)y: 标签向量,shape=(n_samples,)返回:E_i: 误差值(标量)"""# 计算预测值f(x_i):alpha_j·y_j·K[i,j]的累加和 + bf_i = np.sum(self.alpha * y * K[i, :]) + self.b  # K[i,:]是第i行,对应所有j的K[i,j]return f_i  y[i]  # 误差 = 预测值  真实标签def _select_second_alpha(self, i, E_i, E_cache, y):"""SMO算法:选择第二个待优化的alpha(j),与第一个alpha(i)配对(私有方法)策略:优先选择使|E_i  E_j|最大的j(加速收敛),若无则随机选择参数:i: 第一个alpha的索引(已违反KKT条件)E_i: 样本i的误差(E_i = f(x_i)  y_i)E_cache: 误差缓存数组,shape=(n_samples,),存储所有样本的误差(未计算为inf)y: 标签向量,shape=(n_samples,)返回:j: 第二个alpha的索引(j != i)E_j: 样本j的误差(E_j = f(x_j)  y_j)"""j = 1  # 初始化j为无效索引max_delta_E = 0  # 最大误差差的绝对值(|E_i  E_j|)E_j = 0  # 样本j的误差E_cache[i] = E_i  # 更新误差缓存:标记样本i的误差已计算# 获取已计算误差的样本索引(E_cache != inf的样本)valid_indices = np.where(E_cache != float('inf'))[0]if len(valid_indices) > 1:  # 若有多个已计算误差的样本(至少2个,含i)# 遍历有效样本,寻找使|E_i  E_j|最大的j(j != i)for k in valid_indices:if k == i:continue  # 跳过自身(i不能与自身配对)E_k = self._compute_error(k, self.K, y)  # 计算样本k的误差delta_E = abs(E_i  E_k)  # 当前误差差的绝对值if delta_E > max_delta_E:max_delta_E = delta_E  # 更新最大误差差j = k  # 更新j为当前kE_j = E_k  # 记录样本j的误差# 若无可选j(如初始状态,E_cache多数为inf),随机选择j != iif j == 1:j = i  # 初始化为i(后续循环确保j != i)while j == i:# 随机生成0~m1的整数(m为样本数)j = np.random.randint(0, self.m)E_j = self._compute_error(j, self.K, y)  # 计算随机j的误差return j, E_j  # 返回第二个alpha的索引和误差def _clip_alpha(self, alpha_j, L, H):"""将alpha_j裁剪到[L, H]范围内(满足不等式约束,私有方法)参数:alpha_j: 未裁剪的alpha_j(可能超出[0, C]或约束范围)L: 下界(lower bound)H: 上界(upper bound)返回:裁剪后的alpha_j(确保在[L, H]内)"""if alpha_j > H:return H  # 超出上界,取Helif alpha_j < L:return L  # 低于下界,取Lelse:return alpha_j  # 在范围内,保持原值def fit(self, X, y):"""训练SVM模型(核心方法,基于SMO算法优化alpha和b)参数:X: 训练特征矩阵,shape=(n_samples, n_features)y: 训练标签向量,shape=(n_samples,),取值必须为1或1(二分类)"""self.m, self.n = X.shape  # m=样本数,n=特征数(m=X.shape[0], n=X.shape[1])self.alpha = np.zeros(self.m)  # 初始化alpha为全0(shape=(m,))self.b = 0.0  # 初始化决策阈值b为0self.K = self._compute_kernel_matrix(X)  # 预计算核矩阵(加速训练,避免重复计算)E_cache = np.full(self.m, float('inf'))  # 误差缓存:初始化为inf表示未计算(shape=(m,))iter_count = 0  # 迭代计数器(记录连续未更新alpha的次数)# SMO主循环:迭代优化alpha,直到达到最大迭代次数或收敛while iter_count < self.max_iter:alpha_updated = 0  # 标记本轮迭代是否更新了alpha(0=未更新,>0=已更新)# 遍历所有样本,寻找违反KKT条件的alpha_i(外层循环)for i in range(self.m):# 步骤1:计算样本i的误差E_i = f(x_i)  y_iE_i = self._compute_error(i, self.K, y)# 步骤2:判断alpha_i是否违反KKT条件(核心!)# KKT条件(对偶问题约束):# 1. alpha_i=0 → y_i·f(x_i) ≥ 1(样本在间隔外,无惩罚)# 2. 0<alpha_i<C → y_i·f(x_i) = 1(样本在间隔边界,支持向量)# 3. alpha_i=C → y_i·f(x_i) ≤ 1(样本在间隔内,被惩罚)# 违反KKT条件的判断(考虑容忍误差tol):#  若alpha_i < C且y_i·E_i < tol → y_i·f(x_i) = y_i·(E_i + y_i) = y_i·E_i + 1 < 1  tol → 违反条件1#  若alpha_i > 0且y_i·E_i > tol → y_i·f(x_i) = y_i·E_i + 1 > 1 + tol → 违反条件2/3if (y[i] * E_i < self.tol and self.alpha[i] < self.C) or \(y[i] * E_i > self.tol and self.alpha[i] > 0):# 步骤3:选择第二个alpha_j(与i配对优化)j, E_j = self._select_second_alpha(i, E_i, E_cache, y)alpha_i_old = self.alpha[i].copy()  # 保存alpha_i旧值(用于后续判断更新幅度)alpha_j_old = self.alpha[j].copy()  # 保存alpha_j旧值# 步骤4:计算alpha_j的边界L和H(确保alpha_i和alpha_j满足0≤alpha≤C及等式约束)if y[i] != y[j]:# 情况1:y_i != y_j(不同类别),等式约束:alpha_i  alpha_j = k(常数)# 边界:L = max(0, alpha_j_old  alpha_i_old),H = min(C, C + alpha_j_old  alpha_i_old)L = max(0.0, alpha_j_old  alpha_i_old)H = min(self.C, self.C + alpha_j_old  alpha_i_old)else:# 情况2:y_i == y_j(相同类别),等式约束:alpha_i + alpha_j = k(常数)# 边界:L = max(0, alpha_i_old + alpha_j_old  C),H = min(C, alpha_i_old + alpha_j_old)L = max(0.0, alpha_i_old + alpha_j_old  self.C)H = min(self.C, alpha_i_old + alpha_j_old)if L == H:continue  # 边界重合(无优化空间),跳过当前i# 步骤5:计算eta(目标函数二阶导数,决定更新步长)K_ii = self.K[i, i]  # K(x_i, x_i) = 核函数值(样本i与自身)K_jj = self.K[j, j]  # K(x_j, x_j) = 核函数值(样本j与自身)K_ij = self.K[i, j]  # K(x_i, x_j) = 核函数值(样本i与j)eta = K_ii + K_jj  2 * K_ij  # eta = K_ii + K_jj  2*K_ij(线性核下eta≥0)if eta <= 0:continue  # 非正定核可能导致eta≤0(目标函数非凸),无法优化,跳过# 步骤6:更新alpha_j(未裁剪)# 公式推导:alpha_j_new = alpha_j_old + y_j*(E_i  E_j)/etaalpha_j_new = alpha_j_old + y[j] * (E_i  E_j) / eta# 裁剪alpha_j到[L, H]范围内(满足不等式约束)alpha_j_new = self._clip_alpha(alpha_j_new, L, H)# 步骤7:若alpha_j更新幅度太小(<1e5),视为未更新,跳过if abs(alpha_j_new  alpha_j_old) < 1e5:continue# 步骤8:更新alpha_i(根据等式约束sum(y·alpha)=0,alpha_i与alpha_j联动更新)# 公式:alpha_i_new = alpha_i_old + y_i·y_j·(alpha_j_old  alpha_j_new)alpha_i_new = alpha_i_old + y[i] * y[j] * (alpha_j_old  alpha_j_new)# 步骤9:更新决策阈值b(确保分类超平面正确)# 计算b1(基于alpha_i更新):b1 = b  E_i  y_i*(alpha_i_new  alpha_i_old)*K_ii  y_j*(alpha_j_new  alpha_j_old)*K_ijb1 = self.b  E_i  y[i]*(alpha_i_new  alpha_i_old)*K_ii  y[j]*(alpha_j_new  alpha_j_old)*K_ij# 计算b2(基于alpha_j更新):b2 = b  E_j  y_i*(alpha_i_new  alpha_i_old)*K_ij  y_j*(alpha_j_new  alpha_j_old)*K_jjb2 = self.b  E_j  y[i]*(alpha_i_new  alpha_i_old)*K_ij  y[j]*(alpha_j_new  alpha_j_old)*K_jj# 根据alpha_i和alpha_j是否在(0, C)内选择b(支持向量对应的b更可靠)if 0 < alpha_i_new < self.C:self.b = b1  # alpha_i在(0,C)→支持向量,取b1elif 0 < alpha_j_new < self.C:self.b = b2  # alpha_j在(0,C)→支持向量,取b2else:self.b = (b1 + b2) / 2.0  # 均在边界,取平均(提高稳定性)# 步骤10:更新alpha值和误差缓存self.alpha[i] = alpha_i_new  # 更新alpha_i为新值self.alpha[j] = alpha_j_new  # 更新alpha_j为新值E_cache[i] = self._compute_error(i, self.K, y)  # 更新样本i的误差缓存E_cache[j] = self._compute_error(j, self.K, y)  # 更新样本j的误差缓存alpha_updated += 1  # 标记alpha已更新# 步骤11:判断是否收敛(若本轮无alpha更新,迭代计数+1;否则重置计数)if alpha_updated == 0:iter_count += 1  # 无更新,接近收敛else:iter_count = 0  # 有更新,重置计数(需继续迭代)# 训练结束:提取支持向量(alpha>1e5的样本,考虑浮点误差,alpha≈0的非支持向量忽略)support_mask = self.alpha > 1e5  # 支持向量掩码(布尔数组,True表示支持向量)self.support_vectors = X[support_mask]  # 支持向量特征(仅保留alpha>1e5的样本)self.support_alpha = self.alpha[support_mask]  # 支持向量对应的alphaself.support_y = y[support_mask]  # 支持向量对应的标签def predict(self, X):"""对新样本进行预测(二分类)参数:X: 待预测特征矩阵,shape=(n_samples, n_features)返回:y_pred: 预测标签向量,shape=(n_samples,),取值为1或1"""y_pred = []  # 存储预测结果的列表# 遍历每个待预测样本for x in X:# 计算预测值f(x) = sum(alpha_j·y_j·K(x_j, x)) + b(仅用支持向量计算,非支持向量alpha=0)fx = 0.0for i in range(len(self.support_alpha)):# 累加支持向量的贡献:alpha_j·y_j·K(x_j, x)fx += self.support_alpha[i] * self.support_y[i] * self._kernel_function(self.support_vectors[i], x)fx += self.b  # 加上决策阈值b# 符号函数:fx>0→1(类别1),fx<0→1(类别1),fx=0→1(默认)y_pred.append(np.sign(fx))return np.array(y_pred)  # 转换为numpy数组返回# 主程序:数据模拟、模型训练与结果可视化
if __name__ == "__main__":# 1. 生成线性可分的二分类数据(100个样本,2个特征)X, y = generate_linear_separable_data(n_samples=100, seed=42)print(f"生成数据:{X.shape[0]}个样本,{X.shape[1]}个特征")  # 输出:生成数据:100个样本,2个特征# 2. 初始化并训练SVM模型(线性核,硬间隔)# 参数说明:kernel='linear'(线性核),C=10.0(大C≈硬间隔),max_iter=1000(足够迭代次数)svm = SVM(kernel='linear', C=10.0, max_iter=1000)svm.fit(X, y)  # 训练模型print(f"支持向量数量:{len(svm.support_vectors)}")  # 输出支持向量数量(通常为2~5个,线性可分数据)# 3. 在训练集上预测并计算准确率y_pred = svm.predict(X)  # 对训练集预测accuracy = np.mean(y_pred == y)  # 准确率 = 预测正确样本数 / 总样本数print(f"训练集准确率:{accuracy:.2f} (线性可分数据应接近1.0)")  # 输出:训练集准确率:1.00# 4. 可视化结果(特征空间+决策边界+支持向量)plt.figure(figsize=(8, 6))  # 设置画布大小(宽8,高6)# 绘制原始样本点(不同类别用不同颜色/形状)plt.scatter(X[y == 1, 0], X[y == 1, 1], c='r', marker='o', label='Class 1')  # 类别1:红色圆圈plt.scatter(X[y == 1, 0], X[y == 1, 1], c='b', marker='x', label='Class 1')  # 类别1:蓝色叉号# 绘制支持向量(绿色空心圆,突出显示)plt.scatter(svm.support_vectors[:, 0], svm.support_vectors[:, 1], s=150, facecolors='none', edgecolors='g', linewidths=2, label='Support Vectors')# 绘制决策边界(通过网格点预测生成)# 生成网格点坐标(覆盖特征空间范围)x_min, x_max = X[:, 0].min()  1, X[:, 0].max() + 1  # x轴范围(扩展1单位避免边界截断)y_min, y_max = X[:, 1].min()  1, X[:, 1].max() + 1  # y轴范围xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),  # x轴网格点(步长0.02,密集网格确保边界平滑)np.arange(y_min, y_max, 0.02)   # y轴网格点)# 对网格点预测标签(ravel展平网格点,c_按列拼接,predict预测后reshape回网格形状)Z = svm.predict(np.c_[xx.ravel(), yy.ravel()])Z = Z.reshape(xx.shape)# 填充决策区域(不同类别区域用不同颜色)plt.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.Paired)  # alpha=0.3:透明度,避免遮挡样本点# 设置图表标签和标题plt.xlabel('Feature 1', fontsize=12)  # x轴标签(特征1)=plt.ylabel('Feature 2', fontsize=12)  # y轴标签(特征2)plt.title('SVM Classification (Linear Kernel)', fontsize=14)  # 图表标题plt.legend(fontsize=10)  # 显示图例(标签说明)plt.grid(alpha=0.3)  # 添加网格线(透明度0.3)plt.show()  # 显示图表

代码逐一讲解(重点参数与核心逻辑)

1. 数据生成模块(generate_linear_separable_data

功能:生成线性可分的二分类样本,用于测试SVM。
核心参数
n_samples:总样本数(默认100),分为两类(各50个)。
seed:随机种子(默认42),确保数据可复现。
数据分布
类别1:均值[2,2],协方差[[1,0],[0,1]](中心在右上)。
类别1:均值[2,2],协方差[[1,0],[0,1]](中心在左下)。
两类别中心距离远(欧氏距离≈5.66),确保线性可分。

2. SVM类初始化(__init__方法)

核心参数(超参数,需用户指定或调优):
kernel:核函数类型(默认'linear'),可选'linear'(线性)、'poly'(多项式)、'rbf'(高斯核)。线性可分数据用线性核,非线性数据用RBF核
C:软间隔惩罚参数(默认1.0),控制对误分类样本的惩罚力度。C越大→硬间隔(不允许误分类),C越小→软间隔(允许更多误分类,防止过拟合)。线性可分数据建议C≥10(接近硬间隔)。
gamma:RBF/多项式核的系数(默认1.0)。gamma越大→核函数影响范围越小(过拟合风险高),gamma越小→影响范围越大(欠拟合风险高)。通常通过交叉验证调优(如gamma=0.1,1,10)。
degree:多项式核次数(默认3),次数越高→模型越复杂(过拟合风险高),一般取2或3。
tol:KKT条件容忍误差(默认1e3),tol越小→收敛判断越严格(训练慢但精度高)
max_iter:最大迭代次数(默认1000),确保算法在有限时间内收敛,样本量大时可增大(如5000)。

3. 核函数(_kernel_function方法)

作用:将低维特征映射到高维空间,使非线性数据线性可分(通过核技巧避免显式映射)。
线性核K(x1,x2)=x1·x2,适用于线性可分数据,计算快,无额外参数。
RBF核K(x1,x2)=exp(gamma·||x1x2||²),适用于非线性数据,最常用,需调优gamma

4. SMO算法核心(fit方法)

优化目标:求解对偶问题,找到最优拉格朗日乘子alpha和决策阈值b
KKT条件判断:通过误差E_i判断样本是否违反KKT条件,是选择优化样本的关键。
alpha更新逻辑
选择两个违反KKT条件的样本iji遍历,j选最大误差差)。
计算边界LH,确保alpha[0,C]内。
更新alpha_jalpha_i,并调整决策阈值b
收敛条件:连续max_iter次迭代无alpha更新时停止。

5. 预测与可视化

预测函数:利用支持向量计算样本的核函数加权和,加b后取符号得到类别。
可视化:绘制样本点、支持向量(绿色空心圆)和决策边界,直观展示SVM分类效果。

关键参数设置建议

线性可分数据kernel='linear'C=10~100(硬间隔),max_iter=1000
非线性数据kernel='rbf',通过交叉验证调优C(1100)和`gamma`(0.0110),max_iter=5000
防止过拟合:减小C或增大gamma(RBF核)。

运行结果说明

支持向量数量:线性可分数据中,支持向量是距离分类超平面最近的样本(通常2~5个)。
准确率:线性可分数据上准确率接近1.0(理想情况100%)。
决策边界:线性核下为直线,将两类样本完全分开,支持向量位于边界上。

Matlab实现代码:

一、常见错误预判及避免措施(补充修改后措施)

  1. 数据线性不可分:硬间隔SVM无解。
    避免措施:生成数据时确保两类样本间隔明显(如均值差≥3倍标准差),本代码中两类均值差为√[(41)²+(41)²]=√18≈4.24,标准差为√0.5≈0.707,间隔充足。
  2. 变量维度不匹配:新增输入检查(如y必须为列向量、样本数与标签长度匹配)。
  3. 二次规划参数错误:Q矩阵通过矩阵运算构造(替代双重循环),避免手动循环误差;确保H矩阵正定(线性可分数据下Q正定)。
  4. 支持向量提取错误:新增支持向量数量检查,避免n_sv=0导致除零错误。

二、自定义函数实现(修改后)

1. svm_train.m(线性硬间隔SVM训练函数)
function [w, b, sv_indices] = svm_train(X, y)
% SVM训练函数(线性硬间隔):求解权重w、偏置b及支持向量索引
% 输入:
%   X:样本矩阵(n×d,n个样本,d个特征)
%   y:标签向量(n×1,元素为1或1,必须为列向量)
% 输出:
%   w:权重向量(d×1)
%   b:偏置(标量)
%   sv_indices:支持向量在样本中的索引(向量)%% 输入检查:确保标签y为列向量
if size(y, 2) > 1  % 若y是行向量(如1×n)y = y';  % 转置为列向量(n×1),避免后续矩阵运算维度错误
end%% 获取样本数n和特征数d
n = size(X, 1);  % 样本数(X的行数)
d = size(X, 2);  % 特征数(X的列数)%% 构造SVM对偶问题的二次规划参数
% 目标函数:min (1/2)α'Qα  e'α,其中Q_ij = y_i y_j (x_i'x_j)
% 矩阵运算优化Q构造:y*y'为n×n外积矩阵(元素y_i y_j),X*X'为n×n内积矩阵(元素x_i'x_j),逐元素相乘得Q
Q = (y * y') .* (X * X');  % 替代双重循环,高效且避免手动计算误差
H = Q;  % quadprog的目标函数H矩阵(对应(1/2)α'Hα)
f = ones(n, 1);  % quadprog的线性项f向量(对应f'α,原目标函数中为e'α,故f=e)%% 约束条件设置
% 等式约束:y'α = 0(1个等式,Aeq为1×n矩阵,beq为标量0)
Aeq = y';  % y是n×1,y'是1×n,满足Aeq*α = y'α = 0
beq = 0;   
% 不等式约束:α_i ≥ 0(n个不等式,lb为n×1下界向量,ub无上界)
lb = zeros(n, 1);  % α的每个分量下界为0
ub = [];  % 无上界约束%% 调用quadprog求解拉格朗日乘子α
options = optimoptions('quadprog', 'Display', 'off');  % 关闭求解过程输出(避免冗余信息)
[alpha, ~] = quadprog(H, f, [], [], Aeq, beq, lb, ub, [], options);  % 求解二次规划问题%% 提取支持向量索引(考虑数值精度,α>1e6视为非零)
sv_indices = find(alpha > 1e6);  % 支持向量对应的样本索引
n_sv = length(sv_indices);  % 支持向量数量
if n_sv == 0  % 容错检查:线性可分数据必须有支持向量error('未找到支持向量!数据可能线性不可分或存在数值问题');
end%% 计算权重向量w(w = Σ(α_i y_i x_i))
w = zeros(d, 1);  % 初始化权重向量(d×1)
for i = 1:n_svidx = sv_indices(i);  % 第i个支持向量的样本索引w = w + alpha(idx) * y(idx) * X(idx,:)';  % X(idx,:)'将行向量转为列向量,确保维度匹配(d×1end%% 计算偏置b(支持向量满足y_i(w'x_i + b)=1 → b = (1/y_i)  w'x_i,取均值提高稳定性)
b = 0;
for i = 1:n_svidx = sv_indices(i);x_i = X(idx,:)';  % 支持向量列向量(d×1)b = b + (1 / y(idx)  w' * x_i);  % 单个支持向量计算的b值
end
b = b / n_sv;  % 平均所有支持向量的b值,减少数值波动影响end
2. svm_predict.m(预测函数,未修改)
function y_pred = svm_predict(X_test, w, b)
% SVM预测函数:根据权重w和偏置b预测样本标签
% 输入:
%   X_test:测试样本矩阵(m×d,m个样本,d个特征)
%   w:权重向量(d×1,训练输出)
%   b:偏置(标量,训练输出)
% 输出:
%   y_pred:预测标签(m×1,元素为1或1)% 计算决策函数值:f(x) = w'x + b(x为样本特征向量)
% X_test'为d×m矩阵(每行一个特征,每列一个样本),w'*X_test'为1×m(每个样本的决策值),加b后扩展为1×m
f = w' * X_test' + b;  
% 符号函数分类:f(x)>0→1,f(x)<0→1
y_pred = sign(f)';  % 转置为m×1向量,与样本数匹配end

三、主程序实现(修改后,增强数据检查)

% main_svm.m:线性硬间隔SVM完整流程(数据生成→训练→预测→可视化)
% 步骤:生成线性可分样本→训练SVM模型→评估准确率→可视化结果%% 1. 生成线性可分的二维高斯分布样本
rng(1);  % 设置随机数种子(1),确保结果可复现
n = 50;  % 每类样本数量(总样本数2n=100)% 第一类样本(标签1):均值[1,1],协方差0.5I(方差小,分布集中)
mu1 = [1, 1];  % 均值向量(控制样本中心位置)
sigma1 = 0.5 * eye(2);  % 协方差矩阵(对角矩阵,对角线元素为方差,控制样本分散程度)
X1 = mvnrnd(mu1, sigma1, n);  % 生成n个样本(n×2矩阵,每行一个样本)
y1 = ones(n, 1);  % 标签向量(n×1,全1)% 第二类样本(标签1):均值[4,4],协方差0.5I(与第一类间隔大,确保线性可分)
mu2 = [4, 4];  % 均值向量(与第一类均值差√[(41)²+(41)²]≈4.24,远大于2倍标准差√(2*0.5)=√1=1)
sigma2 = 0.5 * eye(2);  % 协方差矩阵(与第一类相同,保证分布形状一致)
X2 = mvnrnd(mu2, sigma2, n);  % 生成n个样本(n×2矩阵)
y2 = ones(n, 1);  % 标签向量(n×1,全1)% 合并样本矩阵和标签向量
X = [X1; X2];  % 总样本矩阵(100×2,前50行第一类,后50行第二类)
y = [y1; y2];  % 总标签向量(100×1)% 数据维度检查(新增,避免样本数与标签数不匹配)
if size(X, 1) ~= length(y)error('样本矩阵行数(%d)与标签向量长度(%d)不匹配', size(X,1), length(y));
end
fprintf('数据生成完成:%d个样本(%d类,每类%d个),%d个特征\n', size(X,1), 2, n, size(X,2));%% 2. 训练SVM模型(调用自定义训练函数)
[w, b, sv_indices] = svm_train(X, y);  % 输出权重w(2×1)、偏置b(标量)、支持向量索引%% 3. 模型评估(训练集准确率)
y_pred = svm_predict(X, w, b);  % 对训练数据预测标签(理论线性可分应全对)
accuracy = mean(y_pred == y);  % 准确率=正确预测样本数/总样本数
fprintf('训练集准确率:%.2f%%\n', accuracy * 100);  % 输出准确率(预期100%)%% 4. 可视化结果(样本分布+支持向量+超平面)
figure;  % 创建图形窗口% 绘制两类样本点(区分颜色和形状)
plot(X1(:,1), X1(:,2), 'bo', 'MarkerSize', 6, 'DisplayName', 'Class 1');  % 第一类:蓝色圆点
hold on;  % 保持当前图形(后续叠加绘制)
plot(X2(:,1), X2(:,2), 'rx', 'MarkerSize', 6, 'DisplayName', 'Class 1');  % 第二类:红色叉号% 绘制支持向量(黑色方框标记,位于间隔边界)
X_sv = X(sv_indices, :);  % 支持向量样本(n_sv×2矩阵)
plot(X_sv(:,1), X_sv(:,2), 'ks', 'MarkerSize', 8, 'LineWidth', 1.5, 'DisplayName', 'Support Vectors');  % 黑色方框% 绘制分类超平面(二维情况下为直线:w1x1 + w2x2 + b = 0 → x2 = (w1x1  b)/w2)
x1_range = linspace(min(X(:,1))1, max(X(:,1))+1, 100);  % x1轴范围(扩展1单位,确保超平面完整显示)
x2_hyperplane = (w(1)*x1_range  b) / w(2);  % 超平面上x2值(w(2)≠0,否则超平面垂直于x1轴)
plot(x1_range, x2_hyperplane, 'g', 'LineWidth', 2, 'DisplayName', 'Decision Boundary');  % 绿色实线超平面% 图形美化设置
legend('Location', 'best');  % 图例放在最佳位置(避免遮挡样本)
xlabel('Feature 1 (x_1)'); ylabel('Feature 2 (x_2)');  % 坐标轴标签(带数学符号)
title('SVM Classification (Linear Hard Margin)');  % 图形标题
grid on;  % 显示网格线(辅助观察样本分布)
axis equal;  % 等比例坐标轴(避免超平面倾斜视觉误差)
hold off;  % 结束图形叠加%% 关键参数说明(训练过程核心参数)
% 1. 样本生成参数:
%    mu1/mu2:均值向量(控制两类样本中心距离,距离越大越易分,此处[1,1]与[4,4]间隔充足)
%    sigma1/sigma2:协方差矩阵(对角线元素为特征方差,值越小样本越集中,此处0.5确保无重叠)
% 2. SVM训练参数:
%    Q矩阵:对偶问题核心矩阵,通过y*y'(外积)和X*X'(内积)逐元素相乘构造,避免双重循环
%    quadprog参数:H=Q(二次项矩阵)、f=ones(n,1)(线性项向量)、Aeq=y'(等式约束)、lb=0(下界约束)
%    支持向量阈值:alpha>1e6(数值精度容错,避免因计算误差将非零alpha误判为0)
% 3. 模型参数:
%    w:权重向量(d×1),由支持向量的α_i y_i x_i累加得到,决定超平面方向
%    b:偏置(标量),通过支持向量的均值计算,决定超平面位置(上下平移)

四、代码逐一讲解(核心参数与逻辑)

1. 样本生成参数(主程序Section 1)

mu1 = [1, 1]mu2 = [4, 4]
控制两类样本的中心位置。均值差的欧氏距离为√[(41)²+(41)²]≈4.24,远大于样本标准差(√0.5≈0.707)的2倍,确保两类样本无重叠,满足线性可分条件。
sigma1 = 0.5 * eye(2)
协方差矩阵为对角矩阵,对角线元素0.5为特征方差。方差越小,样本点越集中在均值附近,进一步保证线性可分。
n = 50:每类样本数,总样本数100,数量适中,避免过拟合或欠拟合。

2. SVM训练核心参数(svm_train.m)

Q矩阵构造:Q = (y * y') .* (X * X')
y * y':n×n外积矩阵,元素(i,j)=y_i y_j(标签乘积,同类样本为1,异类为1)。
X * X':n×n内积矩阵,元素(i,j)=x_i’x_j(样本特征向量内积,衡量样本相似度)。
逐元素相乘后,Q(i,j)=y_i y_j x_i'x_j,恰好是SVM对偶问题目标函数中的二次项矩阵,替代双重循环提升效率。

quadprog函数参数
H=Q:二次规划目标函数的二次项系数矩阵,对应(1/2)α’Hα。
f=ones(n,1):线性项系数向量,原对偶问题目标函数为(1/2)α’Qα e’α,与quadprog标准形式(1/2)x’Hx + f’x对比,得f=e(e为全1向量)。
Aeq=y', beq=0:等式约束y’α=0(保证超平面不偏向某一类)。
lb=zeros(n,1):不等式约束α_i≥0(拉格朗日乘子非负性)。

支持向量提取:sv_indices = find(alpha > 1e6)
由于数值计算误差,严格α=0可能存在极小非零值,用1e6作为阈值判断“非零α”,对应支持向量(位于分类间隔边界的样本)。

3. 模型参数计算(w和b)

权重向量w
公式:w = Σ(α_i y_i x_i)(仅对支持向量求和,非支持向量α_i=0)。
alpha(idx):支持向量的拉格朗日乘子(非零)。
y(idx):支持向量的标签(1或1)。
X(idx,:)':支持向量的特征向量(行向量转列向量,确保与w维度匹配)。
物理意义:w是支持向量特征的加权组合,方向垂直于分类超平面。

偏置b
公式:对支持向量取均值b = mean(1/y_i w'x_i)
理论依据:支持向量满足y_i(w'x_i + b)=1b=1/y_i w'x_i
取均值原因:避免单个支持向量的计算误差影响b值,提升稳定性。

五、运行结果

准确率:输出“训练集准确率:100.00%”(线性可分数据理论准确率100%)。
可视化:图形窗口显示蓝点(Class 1)、红叉(Class 1)、黑方框(支持向量)和绿线(超平面),超平面将两类样本完美分隔,支持向量位于间隔边界。

总结

修改后的代码通过矩阵运算优化、数据维度检查和容错处理,增强了稳定性和效率,同时保留了SVM核心逻辑。关键参数(如均值、协方差、Q矩阵、quadprog约束)的设置直接影响模型能否正确训练,需严格遵循SVM理论和数值计算要求。

http://www.dtcms.com/a/352586.html

相关文章:

  • 华为云之CodeArts IDE Online平台部署Homepage个人导航页【玩转华为云】
  • k230 canMV 单路、双路、三路摄像头高清显示源代码
  • 数据存储工具 ——Redis
  • 构建面向人工智能决策的世界模型引擎所需的基本知识体系
  • 视觉工具:文字显示、图像标注与多模板匹配
  • Mysql——一条 update 语句的执行过程
  • Prometheus 指标类型
  • Solon Web 的两种 Context-Path 配置
  • Vuex 和 Pinia 各自的优点
  • MATLAB中函数的详细使用
  • Linux-孤儿进程和僵死进程
  • RAG中使用到的相关函数注释——LangChain核心函数
  • tracebox工具使用
  • LKT4202UGM耗材防伪安全芯片,守护您的消费电子产品
  • 从串口到屏幕:如何用C#构建一个军工级数据实时监控
  • JUC之synchronized关键字
  • Dify 从入门到精通(第 57/100 篇):Dify 的知识库扩展(进阶篇)
  • 8.26学习总结
  • 在 C# 中使用 Consul 客户端库实现服务发现
  • 卷积操作现实中的意义
  • 发力低空经济领域,移动云为前沿产业加速崛起注入云端动能
  • 微服务-24.网关登录校验-实现登录校验
  • Linux系统日志分析与存储
  • 机器学习:前篇
  • 从行业智能体到一站式开发平台,移动云推动AI智能体规模化落地
  • 产品经理操作手册(3)——产品需求文档
  • Duplicate Same Files Searcher v10.7.0,秒扫全盘重复档,符号链接一键瘦身
  • 【软件测试面试】全网最全,自动化测试面试题总结大全(付答案)
  • 告别出差!蓝蜂物联网网关让PLC程序远程修改零延迟
  • 二、JVM 入门 —— (四)堆以及 GC