前言
感知机是感知机是一个二分类线性判别模型,其输入为实例的特征向量,输出为实例的类别,取+1和-1二值。感知机对应输入空间(特征空间)中将实例划分为正负两类的分离超平面,属于判别模型。
一、感知机模型
假设输入
x
∈
R
n
x\in \mathbb{R}^n
x∈Rn,输出
y
∈
{
−
1
,
+
1
}
y\in\{-1,+1\}
y∈{−1,+1},感知机为如下函数:
f
(
x
)
=
s
i
g
n
(
w
T
x
+
b
)
,
s
i
g
n
(
z
)
=
{
1
x
≥
0
−
1
x
<
0
f(x)=sign(w^Tx+b), \\sign(z)=\left\{\begin{aligned} 1 \qquad \quad x\ge0\\ -1 \qquad\quad x<0 \end{aligned}\right.
f(x)=sign(wTx+b),sign(z)={1x≥0−1x<0
其中,w叫做权重,是分类超平面的法向量;b叫做偏置,是超平面的截距。
设数据集线性可分,感知机的损失函数为所有误分类点到分类超平面的函数间隔,即:
L
(
w
,
b
)
=
−
∑
x
i
∈
M
y
i
(
w
T
x
+
b
)
L(w,b)=-\sum_{x_i\in M}y_i(w^Tx+b)
L(w,b)=−xi∈M∑yi(wTx+b)
二、感知机学习算法的原始形式
输入:训练数据集
T
=
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
.
.
,
(
x
N
,
y
N
)
T={(x_1,y_1),(x_2,y_2),..,(x_N,y_N)}
T=(x1,y1),(x2,y2),..,(xN,yN),其中
x
i
∈
R
n
x_i\in \mathbb{R}^n
xi∈Rn,
Y
i
∈
{
−
1
,
1
}
Y_i\in\{-1,1\}
Yi∈{−1,1};学习率
η
∈
(
0
,
1
]
\eta\in(0,1]
η∈(0,1]
输出:w,b;感知机模型
f
(
x
)
=
s
i
g
n
(
w
T
x
+
b
)
f(x)=sign(w^Tx+b)
f(x)=sign(wTx+b)
- 随机任选一个超平面 w 0 , b 0 w_0,b_0 w0,b0,一般都初始化为0
- 在训练集中选取数据 ( x i , y i ) (x_i,y_i) (xi,yi)
- 如果
y
i
(
w
T
x
i
+
b
)
≤
0
y_i(w^Tx_i+b)\le 0
yi(wTxi+b)≤0,则更新w和b:
w = w + η y i x i b = b + η y i w=w+\eta y_ix_i \\b=b+\eta y_i w=w+ηyixib=b+ηyi - 转至第二步,直到训练集中没有误分点
三、感知机学习算法的对偶形式
对偶形式的基本想法是,将w和b表示为实例
x
i
x_i
xi和标签
y
i
y_i
yi的线性组合的形式,通过求解其系数而求得w和b。由感知机算法的原始形式可以得出,修改n次过后w,b关于
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi)的增量分别是
α
i
y
i
x
i
\alpha_iy_ix_i
αiyixi和
α
i
y
i
\alpha_iy_i
αiyi,这里
α
i
=
n
i
η
\alpha_i=n_i\eta
αi=niη。这样,最后学习到的w和b可以分别表示为:
w
=
∑
i
=
1
N
α
i
y
i
x
i
b
=
∑
i
=
1
N
α
i
y
i
w=\sum_{i=1}^N\alpha_iy_ix_i \\b=\sum_{i=1}^N\alpha_iy_i
w=i=1∑Nαiyixib=i=1∑Nαiyi
具体算法为:
输入:训练数据集 T = ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . , ( x N , y N ) T={(x_1,y_1),(x_2,y_2),..,(x_N,y_N)} T=(x1,y1),(x2,y2),..,(xN,yN),其中 x i ∈ R n x_i\in \mathbb{R}^n xi∈Rn, Y i ∈ { − 1 , 1 } Y_i\in\{-1,1\} Yi∈{−1,1};学习率 η ∈ ( 0 , 1 ] \eta\in(0,1] η∈(0,1]
输出: α \alpha α,b;感知机模型 f ( x ) = s i g n ( ∑ i = 1 N α i y i x i x + b ) f(x)=sign(\sum_{i=1}^N\alpha_iy_ix_ix+b) f(x)=sign(∑i=1Nαiyixix+b),其中 α = ( α 1 , α 2 , . . . , α N ) T \alpha=(\alpha_1,\alpha_2,...,\alpha_N)^T α=(α1,α2,...,αN)T
- 令 α = 0 , b = 0 \alpha=\boldsymbol 0, b=0 α=0,b=0
- 在训练集中选取数据 ( x i , y i ) (x_i,y_i) (xi,yi)
- 如果
y
i
(
∑
j
=
1
N
α
j
y
j
x
j
x
i
+
b
)
≤
0
y_i(\sum_{j=1}^N\alpha_jy_jx_jx_i+b)\le 0
yi(∑j=1Nαjyjxjxi+b)≤0,则更新w和b:
α = α + η b = b + η y i \alpha=\alpha+\eta \\b=b+\eta y_i α=α+ηb=b+ηyi - 转至第二步,知道训练集中没有误分点
四、收敛性和依赖性
收敛性:对于线性可分的T,经过有限次搜索,可将T正确分开的分离超平面,对于线性不可分的T,算法收敛。
依赖性:不同的初值选择,或者迭代过程中不同误分类点的选择顺序,可能得到不同的分离超平面。(为了得到唯一的分离超平面,需要约束条件)
五、代码实现
代码如下(示例):
"""
感知机
"""
import numpy as np
from sklearn.datasets import load_digits
class Perceptron(object):
def __init__(self, m):
self.w = np.zeros(m) # 权重
self.b = 0 # 偏置
def traditional_train(self, train_x, train_y, batch_size,
epochs, test_x=None, test_y=None, learning_rate=1):
"""
感知机学习算法的原始形式
"""
num = train_x.shape[0] # 训练集大小
for epoch in range(epochs):
cur = 0
while cur < num:
if cur+batch_size < num:
batch_xs = train_x[cur:cur+batch_size, :]
batch_ys = train_y[cur:cur+batch_size]
else:
batch_xs = train_x[cur:, :]
batch_ys = train_y[cur:]
pred = np.dot(batch_xs, self.w) + self.b
pred = pred.reshape((-1,))
pred[pred >= 0] = 1
pred[pred < 0] = -1
err_index = pred != batch_ys
self.w += np.mean(batch_xs[err_index] * batch_ys[err_index].reshape(-1, 1), 0) * learning_rate
self.b += np.mean(batch_ys[err_index]) * learning_rate
cur += batch_size
# 每个epoch结束输出在测试集上的精度
if test_x is not None and test_y is not None:
accuracy = self.test(test_x, test_y)
print('Epoch:%d, accuracy:%.4f' % (epoch + 1, accuracy))
def test(self, test_x, test_y):
'''
测试函数
:param test_x:
:param text_y:
:return:
'''
p = np.dot(test_x, self.w) + self.b
pred_y = (p >= 0).astype(int)
pred_y[pred_y == 0] = -1
accuracy = (pred_y == test_y).sum() / test_x.shape[0]
return accuracy
if __name__ == '__main__':
digits = load_digits()
features = digits.data
# 0~4为类别0,5~9为类别1
targets = (digits.target > 4).astype(int)
targets[targets == 0] = -1
shuffle_indices = np.random.permutation(features.shape[0])
features = features[shuffle_indices]
targets = targets[shuffle_indices]
# 划分训练、测试集
train_count = int(len(features)*0.8)
train_x, train_y = features[:train_count], targets[:train_count]
test_x, test_y = features[train_count:], targets[train_count:]
percetron = Perceptron(train_x.shape[1])
batch_size = 64
epochs = 20
percetron.traditional_train(train_x, train_y, batch_size, epochs, test_x, test_y)