感知机简述
我们在学习一个算法的过程中,肯定希望知道我们在什么情况下可以使用这个算法,带着这个目标来进行学习,就像是预习之后带着问题来找答案一样,事半功倍。
感知机算法是一种在上世纪五十年代提出的一种算法,它是现在神经网络和支持向量机的基础,那就是说可能在实际生产过程中,只能作为一种知识基础或者baseline的作用。
感知机(perceptron)是一个二类分类的线性分类模型,其输入为实例的特征向量,输出为实例的类别,取+1和-1二值,感知机对应于输入空间(特征空间)中将实例划分为正负两类的分离超平面,属于之前提到的判别模型的类别。在本章主要考虑的是线性可分的感知机。
接下来,我们就统计学习方法的三要素来考虑该方法。
感知机模型
输入空间是
X
⊆
R
n
X \subseteq R^n
X⊆Rn,输出空间为
Y
=
{
+
1
,
−
1
}
Y = \{+1,-1\}
Y={+1,−1}。其中输入
x
∈
X
x\in X
x∈X表示实例的特征向量,对应于特征空间中的点,而输出
y
∈
Y
y\in Y
y∈Y表示实例的类别
f
(
x
)
=
s
i
g
n
(
w
∗
x
+
b
)
f(x) = sign(w*x+b)
f(x)=sign(w∗x+b)
该函数成为感知机函数,
w
w
w和
b
b
b为感知机参数,
w
∈
R
n
,
b
∈
R
w\in R^n,b\in R
w∈Rn,b∈R,前者成为权值向量,后者称为偏置。
s
i
g
n
sign
sign为符号函数。
f
(
x
)
=
{
+
1
,
x
≥
0
−
1
,
x<0
f(x) = \begin{cases} +1, & x\ge0 \\ -1, & \text{x<0} \\ \end{cases}
f(x)={+1,−1,x≥0x<0
模型的假设空间是定义在特征空间中的所有线性分类模型,即函数集合
{
f
∣
f
(
x
)
=
w
∗
x
+
b
}
\{f|f(x) = w*x+b\}
{f∣f(x)=w∗x+b}
感知机学习策略
数据集的线性可分
首先给出数据集的线性可分的定义
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
…
,
(
x
N
,
y
N
)
}
T = \{(x_1,y_1),(x_2,y_2),\dots,(x_N,y_N)\}
T={(x1,y1),(x2,y2),…,(xN,yN)}
其中,
x
i
∈
X
=
R
n
,
y
i
∈
Y
=
{
+
1
,
−
1
}
,
i
=
1
,
2
,
…
,
N
x_i\in X = R^n,y_i\in Y = \{+1,-1\},i = 1,2,\dots,N
xi∈X=Rn,yi∈Y={+1,−1},i=1,2,…,N,如果存在某个超平面
w
∗
x
+
b
=
0
w*x+b=0
w∗x+b=0将数据集的正实例点和负实例点正确的划分到超平面的两侧,则称该T数据集为线性可分数据集,否则称为线性不可分。
学习策略
首先假设数据集是线性可分的,感知机学习的目的是为了能够将正实例点和负实例点正确分开的超平面,即确定感知机的参数
w
,
b
w,b
w,b。
由此出发我们定义损失函数,并且将损失函数最小化,这里我们采用的损失函数是误分类点到超平面
S
S
S的总距离,经化简之后得到相应的损失函数。
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
…
,
(
x
N
,
y
N
)
}
T = \{(x_1,y_1),(x_2,y_2),\dots,(x_N,y_N)\}
T={(x1,y1),(x2,y2),…,(xN,yN)}
其中,
x
i
∈
X
=
R
n
,
y
i
∈
Y
=
{
+
1
,
−
1
}
,
i
=
1
,
2
,
…
,
N
x_i\in X = R^n,y_i\in Y = \{+1,-1\},i = 1,2,\dots,N
xi∈X=Rn,yi∈Y={+1,−1},i=1,2,…,N,感知机
s
i
g
n
(
w
∗
x
+
b
)
sign(w*x+b)
sign(w∗x+b)学习的损失函数定义为
L
(
w
,
b
)
=
−
∑
x
i
∈
M
y
i
(
w
∗
x
i
+
b
)
L(w,b) = -\sum_{x_i\in M}y_i(w*x_i+b)
L(w,b)=−xi∈M∑yi(w∗xi+b)
其中,
M
M
M为误分类点的集合,这个损失函数就是感知机学习的经验损失函数。感知机的学习策略就是在模型的假设空间中选取使得上式损失函数最小的模型参数
w
,
b
w,b
w,b。
感知机学习算法
原始形式
求解参数
w
,
b
w,b
w,b
m
i
n
w
,
b
L
(
w
,
b
)
=
−
∑
x
i
∈
M
y
i
(
w
∗
x
i
+
b
)
\underset{w,b}{min}L(w,b) = -\sum_{x_i\in M}y_i(w*x_i+b)
w,bminL(w,b)=−xi∈M∑yi(w∗xi+b)
其中
M
M
M为误分类点的集合,采用随机梯度下降法,首先随机初始化一个超平面
w
0
,
b
0
w_0,b_0
w0,b0,然后用梯度下降法不断地极小化目标函数。极小化过程中是随机选取一个误分类点使其梯度下降。
假设误分类点集合
M
M
M固定,那么损失函数
L
(
w
,
b
)
L(w,b)
L(w,b)的梯度为
∇
w
L
(
w
,
b
)
=
−
∑
x
i
∈
M
y
i
x
i
\nabla_wL(w,b) = -\sum_{x_i\in M}y_ix_i
∇wL(w,b)=−xi∈M∑yixi
∇
b
L
(
w
,
b
)
=
−
∑
x
i
∈
M
y
i
\nabla_bL(w,b) = -\sum_{x_i\in M}y_i
∇bL(w,b)=−xi∈M∑yi
随机选取一个误分类点
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi)对参数进行更新。
w
←
w
+
η
y
i
x
i
w\leftarrow w+\eta y_ix_i
w←w+ηyixi
b
←
b
+
η
y
i
b\leftarrow b+\eta y_i
b←b+ηyi
其中,
η
(
0
<
η
≤
1
)
\eta(0<\eta \le1)
η(0<η≤1)是步长,又称为学习率,通过迭代,可以期待损失函数
L
(
w
,
b
)
L(w,b)
L(w,b)不断减小,直到为0。
感知机学习原始形式算法
输入:训练数据集
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
…
,
(
x
N
,
y
N
)
}
T = \{(x_1,y_1),(x_2,y_2),\dots,(x_N,y_N)\}
T={(x1,y1),(x2,y2),…,(xN,yN)},其中,
x
i
∈
X
=
R
n
,
y
i
∈
Y
=
{
+
1
,
−
1
}
,
i
=
1
,
2
,
…
,
N
x_i\in X = R^n,y_i\in Y = \{+1,-1\},i = 1,2,\dots,N
xi∈X=Rn,yi∈Y={+1,−1},i=1,2,…,N;学习率
η
(
0
<
η
≤
1
)
\eta(0<\eta\le1)
η(0<η≤1);
输出:
w
,
b
w,b
w,b;感知机模型
f
(
x
)
=
s
i
g
n
(
w
∗
x
+
b
)
f(x) = sign(w*x+b)
f(x)=sign(w∗x+b)。
第一步:选取初值
w
0
,
b
0
w_0,b_0
w0,b0;
第二步:在训练集中选取数据
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi);
第三步:如果
y
i
(
w
∗
x
i
+
b
)
≤
0
y_i(w*x_i+b)\le0
yi(w∗xi+b)≤0,
w
←
w
+
η
y
i
x
i
w\leftarrow w+\eta y_ix_i
w←w+ηyixi
b
←
b
+
η
y
i
b\leftarrow b+\eta y_i
b←b+ηyi
第四步:重新执行第二步,直到训练集中没有误分类点。
算法的收敛性
对于线性可分的数据集,证明可得,经过有限次搜索可以找到将训练数据完全正确分开的分离超平面。具体证明过程在统计学习方法书中有表,在此略过。
感知机学习对偶形式算法
w , b w,b 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 0 = 0 , b 0 = 0 w_0 = 0,b_0 = 0 w0=0,b0=0, n i n_i ni为第 i i i个点在迭代中更新的总次数,实例点更新的次数越多,也说明,它离超平面越近,也就越难正确分类。
输入:线性可分数据集
T
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
…
,
(
x
N
,
y
N
)
}
T = \{(x_1,y_1),(x_2,y_2),\dots,(x_N,y_N)\}
T={(x1,y1),(x2,y2),…,(xN,yN)},其中,
x
i
∈
X
=
R
n
,
y
i
∈
Y
=
{
+
1
,
−
1
}
,
i
=
1
,
2
,
…
,
N
x_i\in X = R^n,y_i\in Y = \{+1,-1\},i = 1,2,\dots,N
xi∈X=Rn,yi∈Y={+1,−1},i=1,2,…,N;学习率
η
(
0
<
η
≤
1
)
\eta(0<\eta\le1)
η(0<η≤1);
输出:
a
,
b
a,b
a,b;感知机模型
f
(
x
)
=
s
i
g
n
(
∑
j
=
1
N
α
j
y
j
x
j
∗
x
+
b
)
f(x) = sign\left(\sum^N_{j=1}\alpha_jy_jx_j*x+b\right)
f(x)=sign(∑j=1Nαjyjxj∗x+b),其中
α
=
(
α
1
,
α
2
,
…
,
α
N
)
T
\alpha = (\alpha_1,\alpha_2,\dots,\alpha_N)^T
α=(α1,α2,…,αN)T。
第一步:
α
←
0
,
b
←
0
\alpha \leftarrow 0, b\leftarrow 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\left(\sum^N_{j=1}\alpha_jy_jx_j*x_i+b\right)\le0
yi(∑j=1Nαjyjxj∗xi+b)≤0,
α
i
←
α
i
+
η
\alpha_i\leftarrow \alpha_i + \eta
αi←αi+η
b
←
b
+
η
y
i
b\leftarrow b+\eta y_i
b←b+ηyi
第四步:重新执行第二步直到没有误分类数据
对于感知机学习算法的理解
对于原始形式的算法很好从字面意思上进行理解,就是遇到一个误分类点就执行变换参数的操作,而后再返回判断在这个超平面有没有将正负实例完全分开,如果还存在误分类点,那么就进行进行参数变换的操作,一致这样循环执行下去直到彻底没有误分类点(对于一个线性可分的数据集已经证明了这样的过程是收敛的)。
对于对偶形式的算法,将
w
,
b
w,b
w,b表示为实例
x
i
x_i
xi和
y
i
y_i
yi的线性组合的形式,通过求解其系数得到
w
,
b
w,b
w,b。假设
w
0
=
0
,
b
0
=
0
w_0 = 0,b_0 = 0
w0=0,b0=0,则有
w
=
∑
i
=
1
N
α
i
y
i
x
i
w = \sum^N_{i = 1}\alpha_iy_ix_i
w=i=1∑Nαiyixi
b
=
∑
i
=
1
N
α
i
y
i
b = \sum^N_{i=1}\alpha_iy_i
b=i=1∑Nαiyi
对偶形式常常在优化算法中提出,在一定条件下是原始形式的等价形式,求解对偶形式可以求出函数的解。
习题
下面是我实现的例题2.1的代码(所有代码的基础由chatgpt模型生成,这是生产力)
# 下面来自己实现一下例2.1的训练过程,直到完全训练完成
# 首先定义一下输入的训练样本点集合
import numpy as np
import matplotlib.pyplot as plt
# 这里一共有七个点(3,3),(2,3),(6,9),(4,3),(1,1),(-1,-10),(7.58,-3.27)
# 前面四个属于正实例点,后面三个属于负实例点
X = np.array([[3,3],[2,3],[6,9],[4,3],[1,1],[-1,-10],[7.58,-3.27]])
# 然后输入这些样本点的类别集合
Y = np.array([1,1,1,1,-1,-1,-1])
# 定义线性分类器模型的初始值,设置其初始值均为0
W = np.array([0,0])
b = np.array([0])
column_1 = [row[0] for row in X]
column_2 = [row[1] for row in X]
# 我们可以使用matplotlib的plot函数来绘制这些点
plt.plot(column_1[:4], column_2[:4], 'o')
plt.plot(column_1[4:],column_2[4:],'*')
# 设置训练步长lr的长度
lr = 0.625
epoch = 0
# 开始训练,直到训练完成才完全停止下来
while(True):
k = 0
for i in range(len(X)):
# print(X[i][0]*W[0]+X[i][1]*W[1]+b[0])
if(((X[i][0]*W[0]+X[i][1]*W[1]+b[0])*Y[i]<0)or((X[i][0]*W[0]+X[i][1]*W[1]+b[0])*Y[i]==0)):
W = W+lr*Y[i]*X[i]
b = b+lr*Y[i]
epoch = epoch+1
print('第'+str(epoch)+'次训练已完成!')
k = k+1
if(k==0):
# 退出训练
print("一共训练"+str(epoch)+'次')
break
print(W)
print(b)
x = np.array(range(10))
a = -W[0]/W[1]
plt.plot(x,a*x-b[0]/W[1],'-')
plt.show()
# 很显然,结果既有趣,又有效!
使用类的形式完成习题T2.2
import numpy as np
from matplotlib import pyplot as plt
# %matplotlib tk
class Perceptron:
def __init__(self, X, Y, lr=0.001, plot=True):
"""
初始化感知机
:param X: 特征向量
:param Y: 类别
:param lr: 学习率
:param plot: 是否绘制图形
"""
self.X = X
self.Y = Y
self.lr = lr
self.plot = plot
if plot:
self.__model_plot = self._ModelPlot(self.X, self.Y)
self.__model_plot.open_in()
def fit(self):
# (1)初始化weight, b
weight = np.zeros(self.X.shape[1])
b = 0
# 训练次数
train_counts = 0
# 分类错误标识
mistake_flag = True
while mistake_flag:
# 开始前,将mistake_flag设置为False,用于判断本次循环是否有分类错误
mistake_flag = False
# (2)从训练集中选取x,y
for index in range(self.X.shape[0]):
if self.plot:
self.__model_plot.plot(weight, b, train_counts)
# 损失函数
loss = self.Y[index] * (weight @ self.X[index] + b) # @符号进行矩阵乘法运算
# (3)如果损失函数小于0,则该点是误分类点
if loss <= 0:
# 更新weight, b
weight += self.lr * self.Y[index] * self.X[index]
b += self.lr * self.Y[index]
# 训练次数加1
train_counts += 1
print("Epoch {}, weight = {}, b = {}, formula: {}".format(
train_counts, weight, b, self.__model_plot.formula(weight, b)))
# 本次循环有误分类点(即分类错误),置为True
mistake_flag = True
break
if self.plot:
self.__model_plot.close()
# (4)直至训练集中没有误分类点
return weight, b
class _ModelPlot:
def __init__(self, X, Y):
self.X = X
self.Y = Y
@staticmethod
def open_in():
# 打开交互模式,用于展示动态交互图
plt.ion()
@staticmethod
def close():
# 关闭交互模式,并显示最终的图形
plt.ioff()
plt.show()
def plot(self, weight, b, epoch):
plt.cla()
# x轴表示x1
plt.xlim(0, np.max(self.X.T[0]) + 1)
# y轴表示x2
plt.ylim(0, np.max(self.X.T[1]) + 1)
# 画出散点图,并添加图示
scatter = plt.scatter(self.X.T[0], self.X.T[1], c=self.Y)
plt.legend(*scatter.legend_elements())
if True in list(weight == 0):
plt.plot(0, 0)
else:
x1 = -b / weight[0]
x2 = -b / weight[1]
# 画出分离超平面
plt.plot([x1, 0], [0, x2])
# 绘制公式
text = self.formula(weight, b)
plt.text(0.3, x2 - 0.1, text)
plt.title('Epoch %d' % epoch)
plt.pause(0.01)
@staticmethod
def formula(weight, b):
text = 'x1 ' if weight[0] == 1 else '%d*x1 ' % weight[0]
text += '+ x2 ' if weight[1] == 1 else (
'+ %d*x2 ' % weight[1] if weight[1] > 0 else '- %d*x2 ' % -weight[1])
text += '= 0' if b == 0 else ('+ %d = 0' %
b if b > 0 else '- %d = 0' % -b)
return text
X = np.array([[3, 3], [4, 3], [1, 1]])
Y = np.array([1, 1, -1])
model = Perceptron(X, Y, lr=1)
weight, b = model.fit()
第二部分代码的来源为 统计学习方法第二章习题答案