二分问题的数据集
{
(
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
写博不易,敬请支持:
如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!