感知机(perceptron)
<font color=#999AAA
一、感知机模型
感知机是一种线性分类模型,属于判别模型。感知机模型的假设空间是定义在特征空间中的所有线性分类模型或线性分类器。
感知机有如下解释:线性方程
w
⋅
x
+
b
=
0
w\cdot x+b=0
w⋅x+b=0为特征空间中的一个超平面,
w
w
w是超平面的法向量,b是超平面的截距。此平面将特征空间分为两个部分,对应正负两个类别。所以,该超平面称为分离超平面。感知机算法的目标是学习最佳的
w
w
w和b,使得错误分类的点数最少。
二、学习策略
1.损失函数
假设训练数据集是线性可分的,感知机学习的目标是求得一个能够将训练集正类点和负类点完全正确分开的分离超平面。为了找出这样得超平面,需要确定一个合适的损失函数并将损失函数极小化。首先想到的是将误分类点数作为损失函数,但是这样的损失函数不是参数
w
w
w和b的连续可导函数,不易优化。所以选择损失函数为误分类点到超平面的总距离。
中学就学过点线间的距离公式:
l
=
∣
a
⋅
x
+
b
⋅
y
+
c
∣
a
2
+
b
2
l=\frac{\left| a\cdot x+b\cdot y+c \right|}{\sqrt{a^2+b^2}}
l=a2+b2∣a⋅x+b⋅y+c∣
所以对于特征空间
R
n
R^n
Rn中的任意一点到超平面的距离为:
∣
w
⋅
x
+
b
∣
∥
w
∥
2
\frac{\left| w\cdot x+b \right|}{\left\| w \right\| _2}
∥w∥2∣w⋅x+b∣
其中,
∥
w
∥
2
\left\| w \right\| _2
∥w∥2为向量
w
w
w的l2范数,即欧式距离。而对于误分类 点来说,
−
y
i
∣
w
⋅
x
i
+
b
∣
>
0
-y_i\left| w\cdot x_i+b \right|>0
−yi∣w⋅xi+b∣>0
因为感知机的特征空间中的正负类点满足:
y
i
=
{
+
1
,
w
⋅
x
i
+
b
>
0
−
1
,
w
⋅
x
i
+
b
<
0
y_i=\begin{cases} +1,w\cdot x_i+b>0\\ -1,w\cdot x_i+b<0\\ \end{cases}
yi={+1,w⋅xi+b>0−1,w⋅xi+b<0
所以,对于所有误分类点到超平面的距离为:
∑
i
=
1
m
(
−
1
∥
w
∥
2
y
i
(
w
⋅
x
i
+
b
)
)
=
−
1
∥
w
∥
2
∑
i
=
1
m
(
y
i
(
w
⋅
x
i
+
b
)
)
\sum_{i=1}^{\,\,m}{\left( -\frac{1}{\left\| w \right\| _2}y_i\left( w\cdot x_i+b \right) \right)} \\ =-\frac{1}{\left\| w \right\| _2}\sum_{i=1}^{\,\,m}{\left( y_i\left( w\cdot x_i+b \right) \right)}
i=1∑m(−∥w∥21yi(w⋅xi+b))=−∥w∥21i=1∑m(yi(w⋅xi+b))
这里不考虑
1
∥
w
∥
2
\frac{1}{\left\| w \right\| _2}
∥w∥21,所以最终的损失函数为:
L
(
w
,
b
)
=
−
∑
i
=
1
m
(
y
i
(
w
⋅
x
i
+
b
)
)
L\left( w,b \right) =-\sum_{i=1}^{\,\,m}{\left( y_i\left( w\cdot x_i+b \right) \right)}
L(w,b)=−i=1∑m(yi(w⋅xi+b))
该函数针对误分类点,所以当无误分类点是,损失函数值为0。误分类点越少,误分类点离超平面越近,损失函数值越小。
2 优化算法
随机梯度下降算法
随机梯度下降算法的优化过程是一次随机选取一个误分类点使其梯度下降。假设误分类点集合M是固定的,那么损失函数的梯度为
∇
w
L
(
w
,
b
)
=
−
∑
i
=
1
m
y
i
x
i
∇
b
L
(
w
,
b
)
=
−
∑
i
=
1
m
y
i
\nabla _wL\left( w,b \right) =-\sum_{i=1}^{\,\,m}{y_ix_i} \\ \nabla _bL\left( w,b \right) =-\sum_{i=1}^{\,\,m}{y_i}
∇wL(w,b)=−i=1∑myixi∇bL(w,b)=−i=1∑myi
随机选取一个误分类点,对w和b更新,
w
=
w
+
l
r
∗
y
i
x
i
b
=
b
+
l
r
∗
y
i
w=w+lr*y_ix_i \\ b=b+lr*y_i
w=w+lr∗yixib=b+lr∗yi
其中,lr为学习率
三、代码实现
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_blobs
from sklearn.metrics import confusion_matrix
import matplotlib.pyplot as plt
np.random.seed(3)
X, Y = make_blobs(n_samples=200,n_features=2,centers=2)
for i in range(len(Y)):##将类别标签设置为1和-1
if Y[i]==0:
Y[i]=-1
x_train,x_test,y_train,y_test = train_test_split(X,Y,test_size=0.2)
class perceptron:#感知机
def __init__(self):
self.train_loss=[]
self.val_loss=[]
def __loss_function(self,x,y):##损失函数,为所有错误类点的函数间隔的和的相反数
mis_classes=y*np.dot(self._theta,x.T)*y#所有点的函数间隔
loss=0
for mis_class in mis_classes:#选出误分类点的函数间隔,计算loss值,如无误分类点,loss为0
if mis_class<=0:
loss=loss+mis_class
return -loss
def gradient(self,optimizer):#梯度下降法求解
if optimizer=="sgd":#随机梯度下降法
for j in range(self.epochs):
for i in range(len(self._x_train)):
if self._y_train[i]*np.dot(self._theta,self._x_train[i].T)<=0:
self._theta=self._theta+self.learn_rate*self._y_train[i]*self._x_train[i]
self.train_loss.append(self.__loss_function(self._x_train,self._y_train))#参数更新后计算损失loss
elif optimizer=="bgd":#批量梯度下降算法
pass
elif optimizer=="mbgd":#小批量梯度下降
pass
#训练函数,形参为x_train,y_train,学习率,迭代次数,梯度下降算法选择,后三为默认传参
def train(self,x_train,y_train,learn_rate=0.01,epochs=10,optimizer="sgd"):
self.learn_rate=learn_rate
self.epochs=epochs
self.optimizer=optimizer
self._x_train=np.append(x_train, np.ones((1,x_train.shape[0])).T,axis=1)
#self._x_train=np.insert(self._x_train,0,1,axis=1)##扩展x_train,表示常数项
self._y_train=y_train
self._theta=np.random.rand(self._x_train.shape[1]) #theta初始化
self.train_loss.append(self.__loss_function(self._x_train,self._y_train))
self.gradient(optimizer)
#预测函数
def predict(self,x_test):
x_test=np.append(x_test, np.ones((1,x_test.shape[0])).T,axis=1)##扩展x_test,表示常数项
y_test=np.zeros((len(x_test),1))
#x_test=p.insert(self._x_test,0,1,axis=1)
for i in range(len(x_test)):
if(np.dot(self._theta.T,x_test[i])>=0):
y_test[i]=1
else:
y_test[i]=-1
return y_test
if __name__=="__main__":
model=perceptron()
model.train(x_train,y_train,0.1,20,"sgd")
y_pred=model.predict(x_test)
print(confusion_matrix(y_pred,y_test))
plt.scatter(x_test[:,0],x_test[:,1],marker='x',c=y_test)
plt.scatter(x_train[:,0],x_train[:,1],c=y_train)
plt.plot(x_test[:,0],-model._theta[0]/model._theta[1]*x_test[:,0]-model._theta[2]/model._theta[1])
可以看到,决策超平面将正负两类以完全分开。但是,由于感知机模型的目标为使误分类点的个数最小,所以超平面不唯一,不同顺序的误分类点集,得到的 超平面可能不同,如下图所示: