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

机器学习——支持向量机SVM

机器学习——支持向量机

  • 一、介绍
    • 1.概述
      • 1.1 概念
      • 1.2 SVM的优缺点
    • 2.硬间隔
      • 2.1 求解间隔
      • 2.2 对偶问题
    • 3.软间隔
      • 3.1 松驰变量
      • 3.2 对偶问题
    • 4.核函数
      • 4.1 概念
      • 4.2 常见的核函数
  • 二、代码实战
    • 1.实验要求
    • 2.具体实现
      • 2.1 词汇表加载
      • 2.2 邮件预处理函数
      • 2.3词索引转换为特征向量
      • 2.4 SVM 模型训练
      • 2.5 绘制决策边界
      • 2.6 主函数 main()
    • 3.完整代码
    • 4.运行结果
      • 4.1 线性核SVM决策边界(PCA降维)
      • 4.2 多项式核SVM决策边界(PCA降维)
      • 4.3 高斯核SVM决策边界(PCA降维)
      • 4.4 分类结果
      • 4.5 对 C 不同取值的分析
  • 三、感受

一、介绍

1.概述

1.1 概念

支持向量机(SVM) 是一类按监督学习方式对数据进行二元分类的广义线性分类器,其决策边界是对学习样本求解的最大边距超平面,可以将问题化为一个求解凸二次规划的问题。与逻辑回归和神经网络相比,支持向量机在学习复杂的非线性方程时提供了一种更为清晰、更加强大的方式。

具体来说:在线性可分时,在原空间寻找两类样本的最优分类超平面。在线性不可分时,加入松驰变量并通过使用非线性映射将低维度输入空间的样本映射到高维度空间,使其变成线性可分,这样就可以在该特征空间中寻找最优分类超平面。

  • 当训练样本线性可分时,通过硬间隔最大化,学习一个线性可分支持向量机。
  • 当训练样本近似线性可分时,通过软间隔最大化,学习一个线性支持向量机。
  • 当训练样本线性不可分时,通过核技巧和软间隔最大化,学习一个非线性支持向量机。

1.2 SVM的优缺点

  • 优点
    (1)效果好,尤其在小样本情况下。 【在样本数量有限但特征维度较高的场景下(如文本分类、生物信息学),SVM通常表现优越 】
    (2)适用于高维数据。【SVM擅长处理维数远大于样本数量的情况,并可有效避免“维度灾难” 】
    (3)具有良好的泛化能力。【通过最大化间隔(margin),SVM在训练集和测试集之间通常能保持较好的性能 】
    (4)支持非线性分类。【利用核函数(Kernel trick),SVM可以在原始特征空间中构造复杂的非线性决策边界 】
    (5)可以用于分类和回归任务。【除了分类(C-SVC、Nu-SVC),SVM还有回归版本(SVR、Nu-SVR) 】

  • 缺点
    (1)对大规模数据训练耗时高。【SVM的训练时间复杂度通常为 O ( n 2 ) O\left ( n^{2} \right ) O(n2) O ( n 3 ) O\left ( n^{3} \right ) O(n3),不适合上百万样本级别的数据 】
    (2)参数选择困难(C 和核函数参数)。【SVM模型依赖合适的超参数(如正则化参数C、核参数γ等),调参过程复杂且耗时 】
    (3)对缺失值和噪声敏感。【尤其在软间隔模型中,如果噪声或异常值较多,支持向量可能不稳定 】
    (4)对多分类问题支持不直接。【SVM本质是二分类模型,多分类需借助一对多(One-vs-All)或一对一(One-vs-One)策略实现,复杂度提高 】
    (5)结果不易解释。【与决策树等模型相比,SVM的决策边界和支持向量很难解释,不具备可解释性优势 】

2.硬间隔

只考虑二分类问题,假设有 n n n 个训练的 x i x_{i} xi ,每个训练点有一个指标 y i y_{i} yi 。训练集即为: T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) . . . ( x n , y n ) } T=\left \{ \left ( x_{1},y_{1} \right ),\left ( x_{2},y_{2} \right )\right.... \left (x_{n},y_{n} \right )\} T={(x1,y1),(x2,y2)...(xn,yn)} 其中 x i ∈ R N x_{i}\in R_{N} xiRN为输入变量,其分量称为特征或属性。 y i ∈ Y = { 1 , − 1 } y_{i}\in Y=\left \{ 1,-1 \right \} yiY={1,1}是输出指标。

【问题】 给定新的输入 x x x,如何推断它的输入是 y = 1 y=1 y=1 还是 y = − 1 y=-1 y=1
【方案】 找到一个函数 g : R N → R g:R_{N} \rightarrow R g:RNR,然后定义下面的决策函数实现输出。
f ( x ) = s i g n ( g ( x ) ) f(x)=sign(g(x)) f(x)=sign(g(x))
其中 s i g n ( z ) sign(z) sign(z) 是激活函数,也就是当 z ⩾ 0 z\geqslant 0 z0 时取值+1,否则取值-1。确定 g ( ) g() g() 的算法称为分类机。如果 g ( x ) = w T x + b g(x)=w^{T}x+b g(x)=wTx+b,则确定 w w w b b b 的算法称为线性分类机。

【线性可分】 考虑训练集 T T T,若存在 w ∈ R n w\in R^{n} wRn b ∈ R b\in R bR ε > 0 \varepsilon >0 ε>0 使得:对所有的 y i = 1 y_{i} = 1 yi=1 的指标 i i i,有 w T x i + b ⩾ ε w^{T}x_{i} + b \geqslant \varepsilon wTxi+bε ;而对所有的 y i = − 1 y_{i} = -1 yi=1 的指标 j j j,有 w T x i + b ⩽ ε w^{T}x_{i} + b \leqslant \varepsilon wTxi+bε 则称训练集 T T T 线性可分。
在这里插入图片描述
⭐ 假如数据是完全的线性可分的,那么学习到的模型可以称为硬间隔支持向量机 。换个说法,硬间隔指的就是完全分类准确,不能存在分类错误的情况。软间隔,就是允许一定量的样本分类错误

2.1 求解间隔

求解目标:假设两类数据可以被 H = { x : w T x + b ⩾ ε } H = \left \{ x:w^{T}x+b\geqslant \varepsilon \right \} H={x:wTx+bε} 分离,垂直于法向量 w w w,移动 H H H 直到碰到某个训练点,可以得到两个超平面 H 1 H_{1} H1 H 2 H_{2} H2 ,两个平面称为支撑超平面,它们分别支撑两类数据。而位于 H 1 H_{1} H1 H 2 H_{2} H2 正中间的超平面是分离这两类数据最好的选择。
在这里插入图片描述
法向量 w w w 有很多中选择,超平面 H 1 H_{1} H1 H 2 H_{2} H2 之间的距离称为间隔,这个间隔是 w w w 的函数,目的就是寻找这样的 w w w 使得间隔达到最大。

几何距离:假设划分超平面的线性方程为: w T x + b = 0 w^{T}x+b=0 wTx+b=0,其中 w = ( w 1 , w 2 , . . . , w n ) w=(w_{1},w_{2},...,w_{n}) w=(w1,w2,...,wn) 为法向量,决定了超平面的方向; b b b 为位移项,决定了超平面与源点之间的距离。显然划分超平面可被法向量 w w w 和位移 b b b 决定。

  • 样本到超平面 w T x + b = 0 w^{T}x+b=0 wTx+b=0 的距离: r = ∣ w T x i + b ∣ ∣ ∣ w ∣ ∣ = y i ( w T x i + b ) ∣ ∣ w ∣ ∣ r=\frac{|w^{T}x_i+b|}{||w||}=\frac{y_i(w^{T}x_i+b)}{||w||} r=∣∣w∣∣wTxi+b=∣∣w∣∣yi(wTxi+b)
  • 样本集到超平面 w T x + b = 0 w^{T}x+b=0 wTx+b=0 的距离: ρ = m i n ( x i , y i ) ∈ S y i ( w T x i + b ) ∣ ∣ w ∣ ∣ = a ∣ ∣ w ∣ ∣ \rho = \underset{(x_i,y_i)\in S}{min}\frac{y_i(w^{T}x_i+b)}{||w||}=\frac{a}{||w||} ρ=(xi,yi)Smin∣∣w∣∣yi(wTxi+b)=∣∣w∣∣a
  • 优化目标: m a x w , b a ∣ ∣ w ∣ ∣ s . t . y i ( w T x i + b ) ⩾ a , ∀ i \underset{w,b}{max}\, \, \frac{a}{||w||}\, \, \, s.t.\, \, \, y_i(w^Tx_i+b)\geqslant a,\forall i w,bmax∣∣w∣∣as.t.yi(wTxi+b)a,i
  • w ^ = w a , b ^ = b a \hat{w}=\frac{w}{a},\hat{b}=\frac{b}{a} w^=aw,b^=ab,则;
  • 优化目标转变: m a x w , b 1 ∣ ∣ w ^ ∣ ∣ s . t . y i ( w ^ T x i + b ^ ) ⩾ 1 , ∀ i \underset{w,b}{max}\frac{1}{||\hat{w}||}\, \, \,\, \, \, s.t.\, \, \, y_{i}(\hat{w}^{T}x_{i}+\hat{b})\geqslant 1,\, \, \, \forall i w,bmax∣∣w^∣∣1s.t.yi(w^Txi+b^)1,i
  • 转变后的目标函数不会影响模型的预测性能:
  • h ( x ) = s g n ( w T x + b ) = s g n ( a w ^ T x + a b ^ ) = s g n ( w ^ T x + b ^ ) ≅ h ^ ( x ) ( a > 0 ) h(x)=sgn(w^Tx+b)=sgn(a \hat{w}^Tx+a\hat{b})=sgn(\hat{w}^Tx+\hat{b})\cong \hat{h}(x)(a>0) h(x)=sgn(wTx+b)=sgn(aw^Tx+ab^)=sgn(w^Tx+b^)h^(x)(a>0)
  • 为后续求导方便,改进为:
  • m a x w , b 2 ∣ ∣ w ^ ∣ ∣ s . t . y i ( w ^ T x i + b ^ ) ⩾ 1 , ∀ i \underset{w,b}{max}\frac{2}{||\hat{w}||}\, \, \,\, \, \, s.t.\, \, \, y_{i}(\hat{w}^{T}x_{i}+\hat{b})\geqslant 1,\, \, \, \forall i w,bmax∣∣w^∣∣2s.t.yi(w^Txi+b^)1,i
  • 解释:求 x x x 的最大值等同于求解 2 x 2x 2x 的最大值。
  • 假设超平面 w T x + b = 0 w^{T}x+b=0 wTx+b=0 能将训练样本正确分类,取 a = 1 a=1 a=1,对于 ( x i , y i ) ∈ D (x_{i}, y_{i})\in D (xi,yi)D,则有:
  • { w T x i + b ⩾ + 1 , y = + 1 w T x i + b ⩽ − 1 , y = − 1 \left\{\begin{matrix} w^{T}x_{i}+b\geqslant +1, y=+1 & & \\ w^{T}x_{i}+b\leqslant -1, y=-1 \end{matrix}\right. {wTxi+b+1,y=+1wTxi+b1,y=1
  • 距离超平面最近的这几个训练样本点使上述不等式中等号可以成立,被称为支持向量,两个异类支持向量到超平面距离之和为 2 ∣ ∣ w ^ ∣ ∣ \frac{2}{||\hat{w}||} ∣∣w^∣∣2

最大化间隔(为了方便后续用 w w w 当做 w ^ \hat{w} w^

  • 因此最大化间隔问题就是求解一个凸二次规划问题
  • m a x w , b 2 ∣ ∣ w ∣ ∣ s . t . y i ( w T x i + b ) ⩾ 1 , i = 1 , 2 , . . . , n \underset{w,b}{max}\frac{2}{||w||}\, \, \,\, \, \, s.t.\, \, \, y_{i}(w^{T}x_{i}+b)\geqslant 1,\, \, \, i=1,2,...,n w,bmax∣∣w∣∣2s.t.yi(wTxi+b)1,i=1,2,...,n
  • 显然,为了最大化间隔,仅需要最大化 ∣ ∣ w ∣ ∣ − 1 ||w||^{-1} ∣∣w1,这等价于最小化 ∣ ∣ w ∣ ∣ 2 ||w||^{2} ∣∣w2。于是上式可写为:
  • m i n w , b 1 2 ∣ ∣ w ∣ ∣ 2 s . t . y i ( w T x i + b ) ⩾ 1 , i = 1 , 2 , . . . , n \underset{w,b}{min}\frac{1}{2}||w||^{2}\, \, \,\, \, \, s.t.\, \, \, y_{i}(w^{T}x_{i}+b)\geqslant 1,\, \, \, i=1,2,...,n w,bmin21∣∣w2s.t.yi(wTxi+b)1,i=1,2,...,n
  • 这就是支持向量机的基本型。

2.2 对偶问题

利用拉格朗日优化方法可以把2.1中的最大间隔问题转换为比较简单的对偶问题,首先定义凸二次规划的拉格朗日函数:

  • L ( w , b , α ) = 1 2 ∣ ∣ w ∣ ∣ 2 − ∑ i = 1 n α i [ y i ( w T x i + b ) − 1 ] L(w,b,\alpha )=\frac{1}{2}||w||^2-\sum_{i=1}^{n}\alpha_i[y_i(w^Tx_i+b)-1] L(w,b,α)=21∣∣w2i=1nαi[yi(wTxi+b)1]

其中 α = ( α 1 , α 2 , . . . , α n ) \alpha =(\alpha_1, \alpha_2,...,\alpha_n) α=(α1,α2,...,αn) 为拉格朗日乘子且 α i ⩾ 0 \alpha_i\geqslant 0 αi0。令 L ( w , b , a ) L(w,b,a) L(w,b,a) w w w b b b 的偏导为0(凸优化研究的是只有一个山顶的问题。另外,一阶偏导为0,是可微多元函数取极值的必要条件)即可:

  • ∂ L ∂ w = 0 → w = ∑ i = 1 n α i y i x i \frac{\partial L}{\partial w}=0\rightarrow w=\sum_{i=1}^{n}\alpha_iy_ix_i wL=0w=i=1nαiyixi

  • ∂ L ∂ b = 0 → ∑ i = 1 n a i y i = 0 \frac{\partial L}{\partial b}=0\rightarrow \sum_{i=1}^{n}a_iy_i=0 bL=0i=1naiyi=0

  • ∀ i α i [ y i ( w T x i + b ) − 1 ] = 0 \forall i\, \, \, \alpha_i[y_i(w^Tx_i+b)-1]=0 iαi[yi(wTxi+b)1]=0(约束条件)

将结果带入 L ( w , b , a ) = 1 2 ∣ ∣ w ∣ ∣ 2 − ∑ i = 1 n α i [ y i ( w T x i + b ) − 1 ] L(w,b,a)=\frac{1}{2}||w||^2-\sum_{i=1}^{n}\alpha_i[y_i(w^Tx_i+b)-1] L(w,b,a)=21∣∣w2i=1nαi[yi(wTxi+b)1] 中可将w和b消除,得到:

  • i n f L w , b ( w , b , α ) = 1 2 w T w + ∑ i = 1 m α i − ∑ i = 1 m a i y i w T x i − ∑ i = 1 m a i y i b \underset{w,b}{inf L}(w,b,\alpha )=\frac{1}{2}w^Tw+\sum_{i=1}^{m}\alpha_i-\sum_{i=1}^{m}a_iy_iw^Tx_i-\sum_{i=1}^{m}a_iy_ib w,binfL(w,b,α)=21wTw+i=1mαii=1maiyiwTxii=1maiyib

  • = 1 2 w T ∑ i = 1 m a i y i x i − w T ∑ i = 1 m a i y i x i + ∑ i = 1 m α i − b ∑ i = 1 m a i y i =\frac{1}{2}w^T\sum_{i=1}^{m}a_iy_ix_i-w^T\sum_{i=1}^{m}a_iy_ix_i+\sum_{i=1}^{m}\alpha_i-b\sum_{i=1}^{m}a_iy_i =21wTi=1maiyixiwTi=1maiyixi+i=1mαibi=1maiyi

  • = − 1 2 w T ∑ i = 1 m a i y i x i + ∑ i = 1 m α i − b ∑ i = 1 m a i y i =-\frac{1}{2}w^T\sum_{i=1}^{m}a_iy_ix_i+\sum_{i=1}^{m}\alpha_i-b\sum_{i=1}^{m}a_iy_i =21wTi=1maiyixi+i=1mαibi=1maiyi

由于 ∑ i = 1 n a i y i = 0 \sum_{i=1}^{n}a_iy_i=0 i=1naiyi=0 ,所以上式子最后一项可化为0,于是得

  • i n f L w , b ( w , b , α ) = − 1 2 w T ∑ i = 1 m a i y i x i + ∑ i = 1 m α i \underset{w,b}{inf L}(w,b,\alpha )=-\frac{1}{2}w^T\sum_{i=1}^{m}a_iy_ix_i+\sum_{i=1}^{m}\alpha_i w,binfL(w,b,α)=21wTi=1maiyixi+i=1mαi

  • = − 1 2 ( ∑ i = 1 m a i y i x i ) T ∑ i = 1 m a i y i x i + ∑ i = 1 m α i =-\frac{1}{2}(\sum_{i=1}^{m}a_iy_ix_i)^T\sum_{i=1}^{m}a_iy_ix_i+\sum_{i=1}^{m}\alpha_i =21(i=1maiyixi)Ti=1maiyixi+i=1mαi

只有 x i x_i xi 是向量,因此:

  • = − 1 2 ∑ i = 1 m a i y i x i T ∑ i = 1 m a i y i x i + ∑ i = 1 m α i =-\frac{1}{2}\sum_{i=1}^{m}a_iy_ix_i^T\sum_{i=1}^{m}a_iy_ix_i+\sum_{i=1}^{m}\alpha_i =21i=1maiyixiTi=1maiyixi+i=1mαi

根据矩阵乘法分配律和标量乘法结合律可得:

  • ∑ i = 1 m a i y i x i T ∑ i = 1 m a i y i x i = ∑ i = 1 m ∑ j = 1 m α i α j y i y j x i T x j \sum_{i=1}^{m}a_iy_ix_i^T\sum_{i=1}^{m}a_iy_ix_i=\sum_{i=1}^{m}\sum_{j=1}^{m}\alpha _i\alpha _jy_iy_jx_i^Tx_j i=1maiyixiTi=1maiyixi=i=1mj=1mαiαjyiyjxiTxj

即:

  • = ∑ i = 1 m α i − 1 2 ∑ i = 1 m ∑ j = 1 m α i α j y i y j x i T x j =\sum_{i=1}^{m}\alpha_i-\frac{1}{2}\sum_{i=1}^{m}\sum_{j=1}^{m}\alpha _i\alpha _jy_iy_jx_i^Tx_j =i=1mαi21i=1mj=1mαiαjyiyjxiTxj

即:

  • m a x a ∑ i = 1 m α i − 1 2 ∑ i = 1 n ∑ j = 1 n α i α j y i y j x i T x j \underset{a}{max}\sum_{i=1}^{m}\alpha _i-\frac{1}{2}\sum_{i=1}^{n}\sum_{j=1}^{n}\alpha_i\alpha_jy_iy_jx_i^Tx_j amaxi=1mαi21i=1nj=1nαiαjyiyjxiTxj

  • s . t . ∑ i = 1 n y i α i = 0 s.t.\, \,\sum_{i=1}^{n}y_i\alpha_i=0 s.t.i=1nyiαi=0

  • α > 0 \alpha > 0 α>0

其中 ∑ i = 1 n y i α i = 0 \sum_{i=1}^{n}y_i\alpha_i=0 i=1nyiαi=0 L ( w , b , a ) L(w,b,a) L(w,b,a) b b b 的偏导为 0 的结果,作为约束。

这是一个不等式约束下的二次函数极值问题,存在唯一解。根据 KKT条件,解中将只有一部分(通常是很小的一部分)不为零,这些不为0的解所对应的样本就是支持向量。

假设 α ∗ \alpha ^{*} α 是上面凸二次规划问题的最优解,则 α ∗ ≠ 0 \alpha ^{*}\neq 0 α=0 。假设满足 α ∗ > 0 \alpha ^{*}>0 α>0 ,按下面方式计算出的解为原问题的唯一最优解:

  • w ∗ = ∑ i = 1 n α i ∗ y i x i w^*=\sum_{i=1}^{n}\alpha_i^{*}y_ix_i w=i=1nαiyixi

  • b ∗ = y i − ∑ i = 1 n α i ∗ y i x i T x i b^*=y_i-\sum_{i=1}^{n}\alpha_i^*y_ix_i^Tx_i b=yii=1nαiyixiTxi

3.软间隔

3.1 松驰变量

线性不可分即指部分训练样本不能满足 y i ( w T x i + b ) ⩾ 1 y_i(w^Tx_i+b)\geqslant 1 yi(wTxi+b)1 的条件。由于原本的优化问题的表达式要考虑所有的样本点,在此基础上寻找正负类之间的最大几何间隔,而几何间隔本身代表的是距离,是非负的,像这样有噪声的情况会使整个问题无解。

解决办法比较简单,即利用松弛变量允许一些点到分类平面的距离不满足原先的要求。具体约束条件中增加一个松弛项参数 ε i ⩾ 0 \varepsilon _i\geqslant 0 εi0 ,变成:

  • y i ( w T x i + b ) ⩾ 1 − ε i i = 1 , 2 , . . . , n y_i(w^Tx_i+b)\geqslant 1-\varepsilon_i\, \, \, \, \, \, i=1,2,...,n yi(wTxi+b)1εii=1,2,...,n

显然当 ε i ⩾ 0 \varepsilon _i\geqslant 0 εi0 足够大时,训练点就可以满足以上条件。虽然得到的分类间隔越大越好。但需要避免 ε i \varepsilon_i εi 取太大的值。所以在目标函数中加入惩罚项 C C C,得到下面的优化问题:

  • m i n ( 1 2 ∣ ∣ w ∣ ∣ 2 + C ∑ i = 1 n ε i ) min\left ( \frac{1}{2}||w||^2+C\sum_{i=1}^{n}\varepsilon_i \right ) min(21∣∣w2+Ci=1nεi)

  • s . t . y i ( w T x i + b ) ⩾ 1 − ε i i = 1 , 2 , . . . , n s.t. \, \, \, y_i(w^Tx_i+b)\geqslant 1-\varepsilon_i\, \, \, i=1,2,...,n s.t.yi(wTxi+b)1εii=1,2,...,n

  • ε i ⩾ 0 i = 1 , 2 , . . . , n \varepsilon_i\geqslant 0\, \, \, \, i=1,2,...,n εi0i=1,2,...,n

其中 ε ∈ R n \varepsilon \in R^n εRn C C C 是一个惩罚参数。目标函数意味着要最小化 ∣ ∣ w ∣ ∣ 2 ||w||^2 ∣∣w2 (即最大间隔化),又要最小化 ∑ i = 1 n ε i \sum_{i=1}^{n} \varepsilon_i i=1nεi (即约束条件的 y i ( w T x i + b ) ⩾ 1 y_i(w^Tx_i+b)\geqslant 1 yi(wTxi+b)1 的破坏程度),参数 C C C 体现了两者总体的一个权衡。

3.2 对偶问题

求解这一优化问题的方法与求解线性问题最优分类面时所用的方法几乎相同,都是转换为一个二次函数极值问题,只是在凸二次规划中条件变为: 0 ⩽ α i ⩽ C , i = 1 , 2 , . . . , n 0\leqslant \alpha_i\leqslant C, i=1,2,...,n 0αiC,i=1,2,...,n

定义拉格朗日函数:

  • L ( w , b , α , ε , β ) = 1 2 ∣ ∣ w ∣ ∣ 2 + C ∑ i = 1 n ε i + ∑ i = 1 n α i [ 1 − ε i − y i ( w T x i + b ) ] − ∑ i = 1 n β i ε i L(w,b,\alpha ,\varepsilon ,\beta )=\frac{1}{2}||w||^2+C\sum_{i=1}^{n}\varepsilon_i +\sum_{i=1}^{n}\alpha_i[1-\varepsilon_i-y_i(w^Tx_i+b)]-\sum_{i=1}^{n}\beta_i\varepsilon_i L(w,b,α,ε,β)=21∣∣w2+Ci=1nεi+i=1nαi[1εiyi(wTxi+b)]i=1nβiεi

其中 α i ⩾ 0 , β i ⩾ 0 \alpha_i\geqslant 0,\beta_i \geqslant 0 αi0,βi0 是拉格朗日乘子。

L ( w , b , α , ε , β ) L(w,b,\alpha ,\varepsilon ,\beta ) L(w,b,α,ε,β) w w w, b b b, ε \varepsilon ε 求偏导为0可得:

  • ∂ L ∂ w = 0 → w = ∑ i = 1 n α i y i x i \frac{\partial L}{\partial w}=0\rightarrow w=\sum_{i=1}^{n}\alpha_iy_ix_i wL=0w=i=1nαiyixi

  • ∂ L ∂ b = 0 → ∑ i = 1 n a i y i = 0 \frac{\partial L}{\partial b}=0\rightarrow \sum_{i=1}^{n}a_iy_i=0 bL=0i=1naiyi=0

  • ∂ L ∂ ε = 0 → C = α i + β i \frac{\partial L}{\partial \varepsilon }=0\rightarrow C=\alpha_i+\beta_i εL=0C=αi+βi

带入拉格朗日函数,可以消除 w w w, b b b ,再消去 β i \beta_i βi 得到对偶问题:

  • L ( w , b , α , ε , β ) = 1 2 ∣ ∣ w ∣ ∣ 2 + C ∑ i = 1 n ε i + ∑ i = 1 n α i [ 1 − ε i − y i ( w T x i + b ) ] − ∑ i = 1 n β i ε i L(w,b,\alpha ,\varepsilon ,\beta )=\frac{1}{2}||w||^2+C\sum_{i=1}^{n}\varepsilon_i +\sum_{i=1}^{n}\alpha_i[1-\varepsilon_i-y_i(w^Tx_i+b)]-\sum_{i=1}^{n}\beta_i\varepsilon_i L(w,b,α,ε,β)=21∣∣w2+Ci=1nεi+i=1nαi[1εiyi(wTxi+b)]i=1nβiεi

  • = 1 2 ∣ ∣ w ∣ ∣ 2 + ∑ i = 1 n α i [ 1 − y i ( w T x i + b ) ] + C ∑ i = 1 n ε i − ∑ i = 1 n α i ε i − ∑ i = 1 n β i ε i =\frac{1}{2}||w||^2+\sum_{i=1}^{n}\alpha_i[1-y_i(w^Tx_i+b)]+C\sum_{i=1}^{n}\varepsilon_i -\sum_{i=1}^{n}\alpha_i\varepsilon_i-\sum_{i=1}^{n}\beta_i\varepsilon_i =21∣∣w2+i=1nαi[1yi(wTxi+b)]+Ci=1nεii=1nαiεii=1nβiεi

  • = 1 2 w T w + ∑ i = 1 n α i − ∑ i = 1 n a i y i w T x i − ∑ i = 1 n a i y i b + C ∑ i = 1 n ε i − ∑ i = 1 n α i ε i − ∑ i = 1 n β i ε i =\frac{1}{2}w^Tw+\sum_{i=1}^{n}\alpha_i-\sum_{i=1}^{n}a_iy_iw^Tx_i-\sum_{i=1}^{n}a_iy_ib+C\sum_{i=1}^{n}\varepsilon_i -\sum_{i=1}^{n}\alpha_i\varepsilon_i-\sum_{i=1}^{n}\beta_i\varepsilon_i =21wTw+i=1nαii=1naiyiwTxii=1naiyib+Ci=1nεii=1nαiεii=1nβiεi

  • = 1 2 w T ∑ i = 1 n a i y i x i − w T ∑ i = 1 n a i y i x i + ∑ i = 1 n α i − b ∑ i = 1 n a i y i + C ∑ i = 1 n ε i − ∑ i = 1 n α i ε i − ∑ i = 1 n β i ε i =\frac{1}{2}w^T\sum_{i=1}^{n}a_iy_ix_i-w^T\sum_{i=1}^{n}a_iy_ix_i+\sum_{i=1}^{n}\alpha_i-b\sum_{i=1}^{n}a_iy_i+C\sum_{i=1}^{n}\varepsilon_i -\sum_{i=1}^{n}\alpha_i\varepsilon_i-\sum_{i=1}^{n}\beta_i\varepsilon_i =21wTi=1naiyixiwTi=1naiyixi+i=1nαibi=1naiyi+Ci=1nεii=1nαiεii=1nβiεi

  • = − 1 2 ∑ i = 1 n a i y i x i T ∑ i = 1 n a i y i x i + ∑ i = 1 n α i − b ∑ i = 1 n a i y i + C ∑ i = 1 n ε i − ∑ i = 1 n α i ε i − ∑ i = 1 n β i ε i =-\frac{1}{2}\sum_{i=1}^{n}a_iy_ix_i^T\sum_{i=1}^{n}a_iy_ix_i+\sum_{i=1}^{n}\alpha_i-b\sum_{i=1}^{n}a_iy_i+C\sum_{i=1}^{n}\varepsilon_i -\sum_{i=1}^{n}\alpha_i\varepsilon_i-\sum_{i=1}^{n}\beta_i\varepsilon_i =21i=1naiyixiTi=1naiyixi+i=1nαibi=1naiyi+Ci=1nεii=1nαiεii=1nβiεi

  • = − 1 2 ∑ i = 1 n a i y i x i T ∑ i = 1 n a i y i x i + ∑ i = 1 n α i + C ∑ i = 1 n ε i − ∑ i = 1 n α i ε i − ∑ i = 1 n β i ε i =-\frac{1}{2}\sum_{i=1}^{n}a_iy_ix_i^T\sum_{i=1}^{n}a_iy_ix_i+\sum_{i=1}^{n}\alpha_i+C\sum_{i=1}^{n}\varepsilon_i -\sum_{i=1}^{n}\alpha_i\varepsilon_i-\sum_{i=1}^{n}\beta_i\varepsilon_i =21i=1naiyixiTi=1naiyixi+i=1nαi+Ci=1nεii=1nαiεii=1nβiεi

  • = − 1 2 ∑ i = 1 n a i y i x i T ∑ i = 1 n a i y i x i + ∑ i = 1 n α i + ( C − α i − β i ) ∑ i = 1 n ε i =-\frac{1}{2}\sum_{i=1}^{n}a_iy_ix_i^T\sum_{i=1}^{n}a_iy_ix_i+\sum_{i=1}^{n}\alpha_i+(C-\alpha_i-\beta_i)\sum_{i=1}^{n}\varepsilon_i =21i=1naiyixiTi=1naiyixi+i=1nαi+(Cαiβi)i=1nεi

  • = ∑ i = 1 n α i − 1 2 ∑ i = 1 n ∑ j = 1 n α i α j y i y j x i T x j ′ =\sum_{i=1}^{n} \displaystyle \alpha_i-\frac{1}{2}\sum_{i=1}^{n}\sum_{j=1}^{n}\alpha _i\alpha _jy_iy_jx_i^Tx_j' =i=1nαi21i=1nj=1nαiαjyiyjxiTxj

对偶问题:

  • m a x α ∑ i = 1 n α i − 1 2 ∑ i = 1 n ∑ j = 1 n α i α j y i y j x i T x j \underset{\alpha }{max}\sum_{i=1}^{n}\alpha_i-\frac{1}{2}\sum_{i=1}^{n}\sum_{j=1}^{n}\alpha _i\alpha _jy_iy_jx_i^Tx_j αmaxi=1nαi21i=1nj=1nαiαjyiyjxiTxj

  • s . t . ∑ i = 1 n a i y i = 0 s.t.\: \: \: \sum_{i=1}^{n}a_iy_i=0 s.t.i=1naiyi=0

  • 0 < α < C , i = 1 , 2 , . . . , n 0< \alpha < C,i=1,2,...,n 0<α<C,i=1,2,...,n

假设 α ∗ \alpha ^{*} α 是上面凸二次规划问题的最优解,则 α ∗ ≠ 0 \alpha ^{*}\neq 0 α=0 。假设满足 α ∗ > 0 \alpha ^{*}>0 α>0 ,按下面方式计算出的解为原问题的唯一最优解:

  • w ∗ = ∑ i = 1 n α i ∗ y i x i w^*=\sum_{i=1}^{n}\alpha_i^{*}y_ix_i w=i=1nαiyixi

在KKT条件下我们希望带入一个支持向量的值来求出 b ∗ b^* b,但是求解 b b b 的值需要 ε \varepsilon ε 的帮助而想要求出 ε \varepsilon ε 的值需要 b ∗ b^* b 的帮助这样的话就形成了死锁什么值都求不出来。恰好我们有另外一个条件的支撑向量( α ⩽ C \alpha \leqslant C αC 的情况下,称之为自由支持向量),这种支持向量的 ε \varepsilon ε 值为0。这样我们可以利用自由支持向量来求出 b ∗ b^* b的值。

  • 0 < α ∗ < C 0< \alpha^* < C 0<α<C:这时 ε \varepsilon ε 的值为0,可以通过下式计算 b ∗ = y i − ∑ i = 1 n α i ∗ y i x i T x i b^*=y_i-\sum_{i=1}^{n}\alpha_i^*y_ix_i^Tx_i b=yii=1nαiyixiTxi

  • α ∗ = 0 \alpha^*=0 α=0:不是支持向量机,无法计算。

  • α ∗ = C \alpha^* =C α=C:这个时候 b ∗ = y i − ∑ i = 1 n α i ∗ y i x i T x i − ε i b^*=y_i-\sum_{i=1}^{n}\alpha_i^*y_ix_i^Tx_i-\varepsilon_i b=yii=1nαiyixiTxiεi,求解 b ∗ b^* b 需要 ε \varepsilon ε ,产生死锁。

4.核函数

4.1 概念

支持向量机算法分类和回归方法的中都支持线性性和非线性类型的数据类型。非线性类型通常是二维平面不可分,为了使数据可分,需要通过一个函数将原始数据映射到高维空间,从而使得数据在高维空间很容易可分,需要通过一个函数将原始数据映射到高维空间,从而使得数据在高维空间很容易区分,这样就达到数据分类或回归的目的,而实现这一目标的函数称为核函数

在这里插入图片描述
在这里插入图片描述

4.2 常见的核函数

线性核(Linear Kernel)

  • k ( x i , x j ) = x i T x j k(x_i,x_j)=x_i^Tx_j k(xi,xj)=xiTxj

多项式核(Polynomial Kernel)

  • k ( x i , x j ) = ( x i T x j ) d k(x_i,x_j)=(x_i^Tx_j)^d k(xi,xj)=(xiTxj)d

高斯核(Gaussian Kernel)

  • k ( x i , x j ) = e x p ( − ∣ ∣ x i − x j ∣ ∣ 2 2 σ 2 ) k(x_i,x_j)=exp(-\frac{||x_i-x_j||^2}{2\sigma^2}) k(xi,xj)=exp(2σ2∣∣xixj2)

拉普拉斯核(Laplacian Kernel)

  • k ( x i , x j ) = e x p ( − ∣ ∣ x i − x j ∣ ∣ 2 σ ) k(x_i,x_j)=exp(-\frac{||x_i-x_j||}{2\sigma}) k(xi,xj)=exp(2σ∣∣xixj∣∣)

Sigmoid核(Sigmoid Kernel)

  • k ( x i , x j ) = tanh ⁡ ( β x i T x j + θ ) k(x_i,x_j)=\tanh(\beta x_i^Tx_j+\theta ) k(xi,xj)=tanh(βxiTxj+θ)

二、代码实战

1.实验要求

实战要求:使用SVM建立自己的垃圾邮件过滤器。首先需要将每个邮件x变成一个n维的特征向量,并训练一个分类器来分类给定的电子邮件x是否属于垃圾邮件 ( y = 1 ) (y=1) (y=1) 或者非垃圾邮件 ( y = 0 ) (y=0) (y=0)

数据集:emailSample1.txt, vocab.txt, spamTrain.mat, spamTest.mat

2.具体实现

2.1 词汇表加载

从 vocab.txt 文件中读取词汇表,每行是 编号\t单词 格式,返回一个 dict[word] = index。

def load_vocab(vocab_path='vocab.txt'):vocab = {}try:with open(vocab_path, 'r', encoding='utf-8') as f:for line in f:idx, word = line.strip().split('\t')vocab[word] = int(idx)except FileNotFoundError:print(f"错误:找不到词汇表文件 {vocab_path}")return vocab

2.2 邮件预处理函数

功能:

  • 读取邮件内容并小写化

  • 正则表达式清洗文本:去除HTML、网址、邮箱、数字、$符号等

  • 分词(使用标点符号和空白符切分)

  • 过滤长度 <=1 的 token

  • 把词转换成词汇表中的索引,返回索引列表

def process_email(email_path, vocab):try:with open(email_path, 'r', encoding='utf-8') as f:email = f.read().lower()except FileNotFoundError:print(f"错误:找不到邮件文件 {email_path}")return []email = re.sub('<[^<>]+>', ' ', email)email = re.sub(r'(http|https)://[^\s]+', 'httpaddr', email)email = re.sub(r'[^\s]+@[^\s]+', 'emailaddr', email)email = re.sub(r'[0-9]+', 'number', email)email = re.sub(r'[$]+', 'dollar', email)tokens = re.split(r'[\s{}]+'.format(re.escape(string.punctuation)), email)tokens = [t for t in tokens if len(t) > 1]word_indices = [vocab[token] for token in tokens if token in vocab]return word_indices

2.3词索引转换为特征向量

返回一个 vocab_size 维的 0-1 向量,表示当前邮件中是否包含词汇表中的词。

def email_to_feature_vector(word_indices, vocab_size=1899):features = np.zeros(vocab_size)for idx in word_indices:if 1 <= idx <= vocab_size:features[idx - 1] = 1return features

2.4 SVM 模型训练

封装 SVM 训练函数:

  • 支持 linear、poly(多项式)、rbf(高斯核)

  • 隐藏警告信息(训练时可能会出现收敛警告)

def train_svm(X, y, kernel='linear', C=1.0, degree=3):if kernel == 'linear':clf = LinearSVC(C=C, max_iter=5000, random_state=42)else:clf = SVC(kernel=kernel, C=C, degree=degree, max_iter=5000, random_state=42)with warnings.catch_warnings():warnings.simplefilter("ignore")clf.fit(X, y)return clf

2.5 绘制决策边界

  • 使用 PCA 降维后的数据绘制 SVM 的决策边界
  • 绘制 margin 线(±1)和支持向量间隔
  • 可视化红色点(非垃圾邮件)和蓝色点(垃圾邮件)
def plot_decision_boundary(clf, X, y, title):x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1xx, yy = np.meshgrid(np.linspace(x_min, x_max, 500),np.linspace(y_min, y_max, 500))if hasattr(clf, "decision_function"):Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])else:Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]Z = Z.reshape(xx.shape)plt.figure(figsize=(8, 6))plt.contour(xx, yy, Z, levels=[-1, 0, 1], linestyles=['--', '-', '--'], colors='k')plt.scatter(X[y == 0, 0], X[y == 0, 1], c='red', label='非垃圾邮件', edgecolors='k')plt.scatter(X[y == 1, 0], X[y == 1, 1], c='blue', label='垃圾邮件', edgecolors='k')plt.title(title)plt.xlabel('PCA 特征1')plt.ylabel('PCA 特征2')plt.legend()plt.show()

2.6 主函数 main()

主要流程为:

  1. 加载词汇表和数据文件(.mat)
  2. 提取训练和测试数据
  3. PCA降维用于可视化
  4. 多种核函数下的模型训练和评估
  5. 对示例邮件进行分类测试
def main():vocab = load_vocab('vocab.txt')if not vocab:returntry:train_data = loadmat('spamTrain.mat')test_data = loadmat('spamTest.mat')except FileNotFoundError:print("错误:数据文件不存在。请确认 spamTrain.mat 和 spamTest.mat 在当前目录。")returnX_train = train_data['X']y_train = train_data['y'].ravel()X_test = test_data['Xtest']y_test = test_data['ytest'].ravel()# PCA降维到2维,用于可视化pca = PCA(n_components=2, random_state=42)X_train_pca = pca.fit_transform(X_train)X_test_pca = pca.transform(X_test)kernels = {'线性核': 'linear','多项式核': 'poly','高斯核': 'rbf'}models = {}for name, kernel in kernels.items():print(f"\n训练 {name} SVM (C={C})...")if kernel == 'poly':clf = train_svm(X_train_pca, y_train, kernel=kernel, degree=3, C=C)else:clf = train_svm(X_train_pca, y_train, kernel=kernel, C=C)models[name] = clftrain_acc = clf.score(X_train_pca, y_train)test_acc = clf.score(X_test_pca, y_test)print(f"{name} 训练集准确率: {train_acc * 100:.2f}%")print(f"{name} 测试集准确率: {test_acc * 100:.2f}%")plot_decision_boundary(clf, X_train_pca, y_train, f"{name} SVM 决策边界 (PCA降维)")# 示例邮件测试word_indices = process_email('emailSample1.txt', vocab)if not word_indices:print("示例邮件处理失败,退出")returnprint("示例邮件词索引(前10个):", word_indices[:10])email_features = email_to_feature_vector(word_indices)email_features_pca = pca.transform(email_features.reshape(1, -1))print("\n示例邮件预测结果:")for name, clf in models.items():pred = clf.predict(email_features_pca)[0]print(f"{name}: {'垃圾邮件' if pred == 1 else '非垃圾邮件'}")

3.完整代码

import numpy as np
import re
import string
from scipy.io import loadmat
from sklearn.svm import LinearSVC, SVC
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
import warnings# === 可调参数 ===
C = 10.0  # 修改这里的 C 即可改变模型复杂度# === 字体设置(支持中文显示)===
font_path = "C:/Windows/Fonts/simhei.ttf"
font_prop = FontProperties(fname=font_path)
plt.rcParams['font.family'] = font_prop.get_name()
plt.rcParams['axes.unicode_minus'] = Falsedef load_vocab(vocab_path='vocab.txt'):vocab = {}try:with open(vocab_path, 'r', encoding='utf-8') as f:for line in f:idx, word = line.strip().split('\t')vocab[word] = int(idx)except FileNotFoundError:print(f"错误:找不到词汇表文件 {vocab_path}")return vocabdef process_email(email_path, vocab):try:with open(email_path, 'r', encoding='utf-8') as f:email = f.read().lower()except FileNotFoundError:print(f"错误:找不到邮件文件 {email_path}")return []email = re.sub('<[^<>]+>', ' ', email)email = re.sub(r'(http|https)://[^\s]+', 'httpaddr', email)email = re.sub(r'[^\s]+@[^\s]+', 'emailaddr', email)email = re.sub(r'[0-9]+', 'number', email)email = re.sub(r'[$]+', 'dollar', email)tokens = re.split(r'[\s{}]+'.format(re.escape(string.punctuation)), email)tokens = [t for t in tokens if len(t) > 1]word_indices = [vocab[token] for token in tokens if token in vocab]return word_indicesdef email_to_feature_vector(word_indices, vocab_size=1899):features = np.zeros(vocab_size)for idx in word_indices:if 1 <= idx <= vocab_size:features[idx - 1] = 1return featuresdef train_svm(X, y, kernel='linear', C=1.0, degree=3):if kernel == 'linear':clf = LinearSVC(C=C, max_iter=5000, random_state=42)else:clf = SVC(kernel=kernel, C=C, degree=degree, max_iter=5000, random_state=42)with warnings.catch_warnings():warnings.simplefilter("ignore")clf.fit(X, y)return clfdef plot_decision_boundary(clf, X, y, title):x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1xx, yy = np.meshgrid(np.linspace(x_min, x_max, 500),np.linspace(y_min, y_max, 500))if hasattr(clf, "decision_function"):Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])else:Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]Z = Z.reshape(xx.shape)plt.figure(figsize=(8, 6))plt.contour(xx, yy, Z, levels=[-1, 0, 1], linestyles=['--', '-', '--'], colors='k')plt.scatter(X[y == 0, 0], X[y == 0, 1], c='red', label='非垃圾邮件', edgecolors='k')plt.scatter(X[y == 1, 0], X[y == 1, 1], c='blue', label='垃圾邮件', edgecolors='k')plt.title(title)plt.xlabel('PCA 特征1')plt.ylabel('PCA 特征2')plt.legend()plt.show()def main():vocab = load_vocab('vocab.txt')if not vocab:returntry:train_data = loadmat('spamTrain.mat')test_data = loadmat('spamTest.mat')except FileNotFoundError:print("错误:数据文件不存在。请确认 spamTrain.mat 和 spamTest.mat 在当前目录。")returnX_train = train_data['X']y_train = train_data['y'].ravel()X_test = test_data['Xtest']y_test = test_data['ytest'].ravel()# PCA降维到2维,用于可视化pca = PCA(n_components=2, random_state=42)X_train_pca = pca.fit_transform(X_train)X_test_pca = pca.transform(X_test)kernels = {'线性核': 'linear','多项式核': 'poly','高斯核': 'rbf'}models = {}for name, kernel in kernels.items():print(f"\n训练 {name} SVM (C={C})...")if kernel == 'poly':clf = train_svm(X_train_pca, y_train, kernel=kernel, degree=3, C=C)else:clf = train_svm(X_train_pca, y_train, kernel=kernel, C=C)models[name] = clftrain_acc = clf.score(X_train_pca, y_train)test_acc = clf.score(X_test_pca, y_test)print(f"{name} 训练集准确率: {train_acc * 100:.2f}%")print(f"{name} 测试集准确率: {test_acc * 100:.2f}%")plot_decision_boundary(clf, X_train_pca, y_train, f"{name} SVM 决策边界 (PCA降维)")# 示例邮件测试word_indices = process_email('emailSample1.txt', vocab)if not word_indices:print("示例邮件处理失败,退出")returnprint("示例邮件词索引(前10个):", word_indices[:10])email_features = email_to_feature_vector(word_indices)email_features_pca = pca.transform(email_features.reshape(1, -1))print("\n示例邮件预测结果:")for name, clf in models.items():pred = clf.predict(email_features_pca)[0]print(f"{name}: {'垃圾邮件' if pred == 1 else '非垃圾邮件'}")if __name__ == "__main__":main()

4.运行结果

4.1 线性核SVM决策边界(PCA降维)

在这里插入图片描述

4.2 多项式核SVM决策边界(PCA降维)

在这里插入图片描述

4.3 高斯核SVM决策边界(PCA降维)

在这里插入图片描述

4.4 分类结果

在这里插入图片描述

4.5 对 C 不同取值的分析

  • 线性核为例,使用不同的C值进行分析:

  • 在函数中通过定义 C_list = [0.01, 0.1, 1, 10, 100] 进行遍历不同的 C 值。

  • 得到以下的运行结果

在这里插入图片描述

  • 分析:当C取值较小值时,可能会出现欠拟合的情形,泛化能力较差;当C取值中等时,拟合良好,泛化能力最佳;当C取较大值时,可能会出现欠拟合的情形,泛化能力较差。

三、感受

在本次关于支持向量机(SVM)的实验中,我对SVM的基本原理、数学推导以及实际应用有了更深入的理解。通过理论学习与动手实践相结合的方式,我掌握了硬间隔与软间隔的概念,并能初步使用SVM进行线性与非线性分类问题建模。

实验过程中,通过构建最大间隔超平面,我深刻体会到支持向量对分类边界的重要性,以及如何通过优化目标函数提升模型的泛化能力。此外,核函数的引入也让我理解了SVM在高维空间处理非线性问题的强大之处,尤其是利用核技巧将低维不可分问题映射为高维线性可分问题的数学思想。

虽然SVM具有较强的分类能力,但实验中我也意识到其对参数敏感(如惩罚参数C和核参数γ),在样本量较大时计算复杂度也较高,因此在实际应用中需要结合交叉验证和调参技巧来提升性能。

总体而言,本次实验不仅提升了我对机器学习算法的理解,更增强了我将数学理论应用于实际数据分析场景的能力。未来我希望能进一步探索SVM与其他模型(如逻辑回归、神经网络)在不同任务中的表现差异,以选择更适合的解决方案。

相关文章:

  • Android学习之登录界面(包含忘记密码 记住密码)(java + 详细注释 + 源码)
  • 基于大模型的大肠癌全流程预测与诊疗方案研究报告
  • Github 2025-05-25 php开源项目日报 Top10
  • 最好用的wordpress外贸主题
  • 反序列化之Wakeup方法绕过
  • Flink流水线集成Gravitino
  • Java反射详解
  • JVM 的内存模型
  • MySQL#秘籍#一条SQL语句执行时间以及资源分析
  • 超简单Translation翻译模型部署
  • 信奥赛CSP小学五年级动态规划入门
  • 【docker】--compose介绍
  • 高级特性实战:死信队列、延迟队列与优先级队列(二)
  • Go 语言基础 2 Func,流程控制
  • 【linux篇】系统世界跳跃的音符:指令
  • 嵌入式STM32学习—— 定时器中断(应用-利用定时器中断实现LED亮一秒灭一秒)
  • 并发编程知识点
  • MyBatis实战指南(二)如何实现小鸟图标与导入Teacher数据库表实战
  • 位图与布隆过滤器
  • RabbitMQ核心机制——延迟队列
  • 繁体企业网站源码/竞价推广价格
  • 网站建设美工招聘/商丘优化公司
  • 企业网站怎么做的/关键词优化靠谱推荐
  • 影视公司需要的许可证/上海专业优化排名工具
  • 如何用word做网站/谷歌浏览器在线打开
  • 黑龙江省住房和建设厅网站首页/艾滋病阻断药