二分问题的数据集 { ( x i , y i ) } \{(\boldsymbol{x}_i,y_i)\} {(xi,yi)}, i = 1 , 2 , ⋯ , m i=1,2,\cdots,m i=1,2,⋯,m中,特征数据 { x i } \{\boldsymbol{x}_i\} {xi}未必能被一块超平面按其标签值 y i ∈ { − 1 , 1 } y_i\in\{-1,1\} yi∈{−1,1}分隔,即二分问题未必是线性可分的。如果存在超平面 π \pi π: ( x ⊤ , 1 ) w 0 = 0 (\boldsymbol{x}^\top,1)\boldsymbol{w}_0=0 (x⊤,1)w0=0,使得大多数样本点的特征数据被 π \pi π按标签数据取值分隔,满足约束 y i ( x i , 1 ) w 0 ≥ 1 y_i(\boldsymbol{x}_i,1)\boldsymbol{w}_0\geq1 yi(xi,1)w0≥1,但有少数样本点特征数据不满足此约束而交叉纠结,如下图所示。这样的问题,称为近似线性可分问题。图中的超平面 ( x ⊤ , 1 ) w 0 = ± 1 (\boldsymbol{x}^\top,1)\boldsymbol{w}_0=\pm1 (x⊤,1)w0=±1称为软边界。
解决近似线性可分问题的支持向量机模型引入松弛变量 z = ( z 1 z 2 ⋮ z m ) \boldsymbol{z}=\begin{pmatrix}z_1\\z_2\\\vdots\\z_m\end{pmatrix} z=
z1z2⋮zm
,构造优化问题
{ min 1 2 w ⊤ H w + c ⊤ z s.t. y i ( x i , 1 ) w ≥ 1 − z i z i ≥ 0 i = 1 , 2 , ⋯ m . ( 1 ) \begin{cases} \min\quad \frac{1}{2}\boldsymbol{w}^\top\boldsymbol{Hw}+\boldsymbol{c}^\top\boldsymbol{z}\\ \text{s.t.\ }\quad y_i(\boldsymbol{x}_i,1)\boldsymbol{w}\geq1-z_i\\ \quad\quad\ \ z_i\geq0 \end{cases}\quad i=1,2,\cdots m.\quad(1) ⎩
⎨
⎧min21w⊤Hw+c⊤zs.t. yi(xi,1)w≥1−zi zi≥0i=1,2,⋯m.(1)
其中, H = ( 1 0 ⋯ 0 0 0 1 ⋯ 0 0 ⋮ ⋮ ⋱ ⋮ ⋮ 0 0 ⋯ 1 0 0 0 ⋯ 0 0 ) \boldsymbol{H}=\begin{pmatrix}1&0&\cdots&0&0\\0&1&\cdots&0&0\\\vdots&\vdots&\ddots&\vdots&\vdots\\0&0&\cdots&1&0\\0&0&\cdots&0&0\end{pmatrix} H=
10⋮0001⋮00⋯⋯⋱⋯⋯00⋮1000⋮00
, c = ( C C ⋮ C ) \boldsymbol{c}=\begin{pmatrix}C\\C\\\vdots\\C\end{pmatrix} c=
CC⋮C
,正则化参数 C > 0 C>0 C>0为松弛变量的罚因子。其值越大,模型对误分类的惩罚越强;反之,模型将侧重于分类间隔。当C足够大时,上述问题退化为问题。
{ min 1 2 w ⊤ H w s.t. y i ( x i ⊤ , 1 ) w ≥ 1 i = 1 , 2 , ⋯ , m . \begin{cases} \min\quad\frac{1}{2}\boldsymbol{w}^\top\boldsymbol{Hw}\\ \text{s.t.}\quad\quad y_i(\boldsymbol{x}_i^\top,1)\boldsymbol{w}\geq1\quad i=1,2,\cdots,m. \end{cases} {min21w⊤Hws.t.yi(xi⊤,1)w≥1i=1,2,⋯,m.
根据强对偶定理,可以证明二次规划问题(1)的最优解 w 0 \boldsymbol{w}_0 w0与其对偶问题
{ min 1 2 μ ⊤ Q μ − μ ⊤ 1 s.t. μ ⊤ y = 0 o ≤ μ ≤ c . ( 2 ) \begin{cases} \min\quad\frac{1}{2}\boldsymbol{\mu}^\top\boldsymbol{Q\mu}-\boldsymbol{\mu}^\top\boldsymbol{1}\\ \text{s.t.\ \ }\quad\boldsymbol{\mu}^\top\boldsymbol{y}=0\\ \quad\quad\ \ \ \boldsymbol{o}\leq\boldsymbol{\mu}\leq\boldsymbol{c} \end{cases}.\quad{(2)} ⎩
⎨
⎧min21μ⊤Qμ−μ⊤1s.t. μ⊤y=0 o≤μ≤c.(2)
的最优解 μ 0 \boldsymbol{\mu}_0 μ0等价。其中, Q = X ⊤ X \boldsymbol{Q}=\boldsymbol{X}^\top\boldsymbol{X} Q=X⊤X, X = ( y 1 x 1 , y 2 x 2 , ⋯ , y m x m ) \boldsymbol{X}=(y_1\boldsymbol{x}_1,y_2\boldsymbol{x}_2,\cdots,y_m\boldsymbol{x}_m) X=(y1x1,y2x2,⋯,ymxm), μ ∈ R m \boldsymbol{\mu}\in\text{R}^m μ∈Rm。等价的意义为
w 0 = ( ∑ i ∈ s μ 0 i y i x i 1 m s ∑ j ∈ s ( y j − ∑ i ∈ s μ 0 i y i x j ⊤ x i ) ) . \boldsymbol{w}_0=\begin{pmatrix}\sum\limits_{i\in s}\mu_{0_i}y_i\boldsymbol{x}_i\\\frac{1}{m_s}\sum\limits_{j\in s}\left(y_j-\sum\limits_{i\in s}\mu_{0_i}y_i\boldsymbol{x}_j^\top\boldsymbol{x}_i\right)\end{pmatrix}. w0=
i∈s∑μ0iyixims1j∈s∑(yj−i∈s∑μ0iyixj⊤xi)
.
其中,
s = { i ∣ 0 ≤ i ≤ m , 0 < μ 0 i < C } s=\{i|0\leq i\leq m, 0<\mu_{0_i}<C\} s={i∣0≤i≤m,0<μ0i<C}
为支持向量下标集, m s m_s ms为 s s s支持向量个数。
下列代码实现通过求解二次规划问题(2)的近似线性可分问题支持向量机模型。
import numpy as np
from scipy.optimize import minimize
class ALineSvc(Classification, LineModel): #近似线性可分支持向量机分类器
def __init__(self, C = 1e+4): #构造函数
self.C = C
self.tagVal = np.sign
def obj(self, mu): #目标函数
return 0.5 * (mu @ (self.Q @ mu)) - mu.sum()
def ynormalize(self, y, trained): #标签数据预处理
if not trained:
self.ymin = 0
self.ymax = 1
return (y - self.ymin) / (self.ymax - self.ymin)
def fit(self, X, Y, mu = None): #训练函数
print("训练中...,稍候")
m, n = X.shape
self.scalar = (len(X.shape) == 1)
self.X, self.Y = self.pretreat(X, Y)
if not isinstance(mu, np.ndarray):
if mu == None:
mu = np.random.random(m)
else:
mu = np.array([mu] * m)
Qk = np.array([[self.X[i] @ self.X[j] #内积矩阵
for j in range(m)]
for i in range(m)])
self.Q=np.outer(self.Y, self.Y) * Qk #系数矩阵
h = lambda x: self.Y @ x #等式约束条件函数
g1 = lambda x: x #不等式约束函数1
g2 = lambda x: self.C - x #不等式约束函数2
cons = [{'type': 'eq', 'fun': h}, #约束条件列表
{'type': 'ineq', 'fun': g1},
{'type': 'ineq', 'fun': g2}]
res = minimize(self.obj, mu, constraints = cons) #求解最优化问题
self.mu0 = res.x
self.support_ = np.where((self.mu0 > 1e-5) & #支持向量下标
(self.mu0 < self.C))[0]
self.w0 = (self.mu0[self.support_] * self.Y[self.support_]) @\
self.X[self.support_,0:n] #模型参数前n项
b0 = (self.Y[self.support_]-(self.mu0[self.support_] * self.Y[self.support_])\
@ Qk[np.ix_(self.support_, self.support_)]).mean() #模型参数最末项
self.w0 = np.append(self.w0, b0) #整合模型参数
self.coef_, self.intercept_ = self.coef_inte() #计算超平面系数和截距
print("%d次迭代后完成训练。"%res.nit)
程序中第3~44行定义的近似线性可分支持向量机分类器类ALineSvc继承了Classification(见博文《最优化方法Python计算:无约束优化应用——线性回归分类器》)和LineModel(见博文《最优化方法Python计算:无约束优化应用——线性回归模型》)的属性与方法。类定义体中
- 第4~6行定义构造函数,设置松弛变量上限C的默认值为 1 0 4 10^4 104。第6行将标签值函数tagVal设置为Numpy的sign函数,以适于计算分类预测值。
- 第7~8行定义问题(2)的目标函数obj,返回值为 1 2 μ ⊤ Q μ − μ ⊤ 1 \frac{1}{2}\boldsymbol{\mu}^\top\boldsymbol{Q}\boldsymbol{\mu}-\boldsymbol{\mu}^\top\boldsymbol{1} 21μ⊤Qμ−μ⊤1。
- 第9~11行重载标签数据归一化函数ynormalize。由于近似线性可分支持向量机模型中的标签数据不需要归一化,所以在训练时将self.ymin和self.ymax设置为0和1。第12行返回归一化后的标签数据。做了这样的调整,我们在进行预测操作时,按式
y = y ⋅ ( max y − min y ) + min y y=y\cdot(\max y-\min y)+\min y y=y⋅(maxy−miny)+miny
仍保持 y y y的值不变,进而可保持LineModel的predict函数代码不变(见博文《最优化方法Python计算:无约束优化应用——线性回归分类器》)。 - 第14~44行重载训练函数fit。对比程序4.1中定义的父类fit函数可见第15~23行的代码是保持一致的。
- 第24~26行计算内积矩阵 Q k = ( x 1 ⊤ x 1 x 1 ⊤ x 2 ⋯ x 1 ⊤ x m x 2 ⊤ x 1 x 2 ⊤ x 2 ⋯ x 2 ⊤ x m ⋮ ⋮ ⋱ ⋮ x m ⊤ x 1 x m ⊤ x 2 ⋯ x m ⊤ x m ) \boldsymbol{Q}_k=\begin{pmatrix}\boldsymbol{x}_1^\top\boldsymbol{x}_1&\boldsymbol{x}_1^\top\boldsymbol{x}_2&\cdots&\boldsymbol{x}_1^\top\boldsymbol{x}_m\\ \boldsymbol{x}_2^\top\boldsymbol{x}_1&\boldsymbol{x}_2^\top\boldsymbol{x}_2&\cdots&\boldsymbol{x}_2^\top\boldsymbol{x}_m\\\vdots&\vdots&\ddots&\vdots\\\boldsymbol{x}_m^\top\boldsymbol{x}_1&\boldsymbol{x}_m^\top\boldsymbol{x}_2&\cdots&\boldsymbol{x}_m^\top\boldsymbol{x}_m\end{pmatrix} Qk= x1⊤x1x2⊤x1⋮xm⊤x1x1⊤x2x2⊤x2⋮xm⊤x2⋯⋯⋱⋯x1⊤xmx2⊤xm⋮xm⊤xm
第27行计算目标函数系数矩阵 Q = ( y 1 y 1 x 1 ⊤ x 1 y 1 y 2 x 1 ⊤ x 2 ⋯ y 1 y m x 1 ⊤ x m y 2 y 1 x 2 ⊤ x 1 y 2 y 2 x 2 ⊤ x 2 ⋯ y 2 y m x 2 ⊤ x m ⋮ ⋮ ⋱ ⋮ y m y 1 x m ⊤ x 1 y m y 2 x m ⊤ x 2 ⋯ y m y m x m ⊤ x m ) . \boldsymbol{Q}=\begin{pmatrix}y_1y_1\boldsymbol{x}_1^\top\boldsymbol{x}_1&y_1y_2\boldsymbol{x}_1^\top\boldsymbol{x}_2&\cdots&y_1y_m\boldsymbol{x}_1^\top\boldsymbol{x}_m\\ y_2y_1\boldsymbol{x}_2^\top\boldsymbol{x}_1&y_2y_2\boldsymbol{x}_2^\top\boldsymbol{x}_2&\cdots&y_2y_m\boldsymbol{x}_2^\top\boldsymbol{x}_m\\ \vdots&\vdots&\ddots&\vdots\\ y_my_1\boldsymbol{x}_m^\top\boldsymbol{x}_1&y_my_2\boldsymbol{x}_m^\top\boldsymbol{x}_2&\cdots&y_my_m\boldsymbol{x}_m^\top\boldsymbol{x}_m \end{pmatrix}. Q= y1y1x1⊤x1y2y1x2⊤x1⋮ymy1xm⊤x1y1y2x1⊤x2y2y2x2⊤x2⋮ymy2xm⊤x2⋯⋯⋱⋯y1ymx1⊤xmy2ymx2⊤xm⋮ymymxm⊤xm .
- 第28~30行定义等式约束条件函数h和不等式约束条件函数g1和g2。第31~33行构造约束条件列表cons。第34行调用minimize函数求解优化问题(2),返回值赋予res。第35行将res的x属性赋予mu0,为问题(2)的最优解 μ 0 \boldsymbol{\mu}_0 μ0。
- 第36~37行按条件
0 < μ 0 i < C , i = 1 , 2 , ⋯ , m 0<\mu_{0_i}<C, i=1, 2, \cdots, m 0<μ0i<C,i=1,2,⋯,m
查找支持向量的下标集 s s s并赋予属性support_。
- 第38~39行计算原问题(1)的最优解 w 0 \boldsymbol{w}_0 w0的前 n n n个元素 ∑ i ∈ s μ 0 i y i x i \sum\limits_{i\in s}\mu_{0_i}y_i\boldsymbol{x}_i i∈s∑μ0iyixi。第40~41行计算原问题(1)的最优解 w 0 \boldsymbol{w}_0 w0的最后一个元素 b 0 b_0 b0。第43行连接两部分构成 w 0 \boldsymbol{w}_0 w0赋予属性w0。
- 第44行调用父类的coef_inte函数(见博文《最优化方法Python计算:无约束优化应用——线性回归模型》)计算决策面的系数coef_与截距intercept_。
综合案例
井字棋是很多人儿时喜爱的游戏。方形棋盘被井字形的4条直线分成9个空格。两个玩家各执标有一符:或为“ × \times ×”,或为“ ∘ \circ ∘”的棋子,轮流在棋盘中放置一子。一方所执符号棋子占据棋盘中一直线(水平或竖直或对角)上的三个空格(见题图),即胜出。文件tic-tac-toe.csv(来自UC Irvine Machine Learning Repository)罗列了958例棋盘格局及其胜负判断
x | x | x | x | o | o | x | o | o | positive |
---|---|---|---|---|---|---|---|---|---|
x | x | x | x | o | o | o | x | o | positive |
⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ |
b | b | b | b | o | o | x | x | x | positive |
x | x | o | x | x | o | o | b | o | negative |
⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ |
o | x | o | o | x | x | x | o | x | negative |
o | o | x | x | x | o | o | x | x | negative |
文件中,每一行表示一种棋盘格局及对该格局胜负方的判定。前9个数据表示按行优先排列的棋盘格局,第10个数据“positive”表示执“ × \times ×”者胜,“negative”表示执“ ∘ \circ ∘”者胜。例如,第一行中前9个数据“x x x x o o x o o”表示棋盘格局
由于执“ × \times ×”者占满了第1行和第1列,故判断执“ × \times ×”者胜出,即第10个数据为“positive”。注意,格局数据中“b”表示对应格子未置入任何棋子。例如,数据“b b b b o o x x x”表示棋盘格局
下列代码试图用文件中的70例数据训练一个ALineSvc支持向量机分类模型,用其余数据对其进行测试。
import numpy as np
data = np.loadtxt('tic-tac-toe.csv', #读取数据文件
delimiter = ',', dtype = str)
X = np.array(data) #转换为数组
Y = X[:, 9] #读取标签数据
X = np.delete(X, [9], axis = 1) #去掉标签列
m, n=X.shape
print('共有%d个数据样本'%m)
for i in range(m):
for j in range(n):
if X[i, j] == 'x':
X[i, j] = 1
if X[i, j] == 'o':
X[i, j] = -1
if X[i, j] == 'b':
X[i, j] = 0
X = X.astype(float)
Y = np.array([1 if y == 'positive' else #类别数值化
-1 for y in Y]).astype(int)
a = np.arange(m) #数据项下标
m1=70 #训练集样本容量
np.random.seed(3489)
print('随机抽取%d个样本作为训练数据。'%(m1))
train = np.random.choice(a,m1,replace=False)
test = np.setdiff1d(a,train) #检测数据下标
tictactoe = ALineSvc(C = 10) #创建模型
tictactoe.fit(X[train], Y[train]) #训练模型
print('系数:', tictactoe.coef_)
print('截距:%.4f'%tictactoe.intercept_)
support = tictactoe.support_ #支持向量下标
print('支持向量:%s'%support)
acc=tictactoe.score(X[test], Y[test]) * 100
print('对其余%d个样本数据测试,正确率:%.2f'%(m - m1,acc) + '%')
程序的第2~6行从文件中读取数据并转换为数组X,并从中拆分出标签数据Y。由于表示格局的特征数据和表示胜负的标签数据都是字符型,应转换为数值类型。对特征数据,作映射:
x → 1 , o → − 1 , b → 0 \text{x}\rightarrow1, \text{o}\rightarrow-1,\text{b}\rightarrow0 x→1,o→−1,b→0
第9~16行的嵌套for循环完成9个格局数据按上述对应关系的类型转换。第18~19行将标签数据按映射
positive → 1 , negative → − 1 \text{positive}\rightarrow1, \text{negative}\rightarrow-1 positive→1,negative→−1
进行类型转换。第20~25行从958个棋盘格局数据中随机选取70(m1)个作为训练数据集X[train]、Y[train],其余888个作为训练数据集X[test]、Y[test]。第26行声明ALineSvc类对象tictactoe作为支持向量机分类模型,设置罚项系数C为10.0。第27行调用tictactoe的fit函数用X[train]、Y[train]对其进行训练。第32行调用tictactoe的score函数用X[test]、Y[test]对其进行测试。运行程序,输出
共有958个数据样本
随机抽取100个样本作为训练数据。
训练中...,稍候
15次迭代后完成训练。
系数: [2. 2. 2. 2. 2. 2. 2. 2. 2.]
截距:-19.0286
支持向量:[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69]
对其余888个样本数据测试,正确率:98.31%
意为用70个训练数据,经过15次迭代完成训练。对其余888个数据对训练所得模型测试,正确率为98.31%。训练给出了决策面
π : 2 x 1 + 2 x 2 + 2 x 3 + 2 x 4 + 2 x 5 + 2 x 6 + 2 x 7 + 2 x 8 + 2 x 9 − 19.03 = 0 \pi:2x_1+2x_2+2x_3+2x_4+2x_5+2x_6+ 2x_7+2x_8+2x_9-19.03=0 π:2x1+2x2+2x3+2x4+2x5+2x6+2x7+2x8+2x9−19.03=0
写博不易,敬请支持:
如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!