感知机(perceptron):原理、python实现及sklearn.linear_model.Perceptron参数详解

1.感知机模型介绍

感知机是一个二分类线性分类模型,二分类是指输出 Y Y Y的分类只有两个值,取+1和-1,线性分类是指模型将训练数据集用一个线性超平面分离(如果特征空间 X X X ⊆ \sube R n R^n Rn,那么该线性超平面就是n-1维)。

感知机模型属于判别模型,即通过输入的样本直接学习到 f f f( x x x),而没有学习到 X X X Y Y Y的联合分布 P P P( X X X, Y Y Y

感知机模型的形式: f ( x ) = s i g n ( w ⋅ x + b ) f(x) = sign(w \centerdot x+b) f(x)=sign(wx+b) sign为符号函数,当 w ⋅ x w \centerdot x wx + + + b >0时取1,<0时取-1。 w ⋅ x w \centerdot x wx + + +b即是该模型要学习的超平面,将实例划分为正负两类。

感知机模型的假设空间:定义在特征空间中的所有线性分类模型,{ f f f| f ( x ) f(x) f(x)= w ⋅ x w \centerdot x wx + + +b}

2.感知机学习策略

假设空间中有无数个模型,那么哪一个才是最好的呢?这就涉及到模型的学习策略

假设数据集是线性可分的,即存在一个超平面 w ⋅ x w \centerdot x wx + + +b=0将数据集的正例和反例完全分开。正例在 w ⋅ x w \centerdot x wx + + +b>0一侧,反例在另一侧。
y i = { + 1 if  w ⋅ x + b > 0 − 1 if  w ⋅ x + b < 0 y_i =\begin{cases} +1 &\text{if } w \centerdot x+b>0 \\ -1 &\text{if } w \centerdot x+b<0 \end{cases} yi={+11if wx+b>0if wx+b<0

损失函数也叫代价函数,是指模型分类错误时的代价值。这里的模型的损失函数可以是误分类点的比例,但是该函数不是要求参数 w w w 和 b的连续可导函数,不利于求解最优模型。

另一种损失函数是考虑所有误分类点到该超平面 w ⋅ x w \centerdot x wx + + +b=0的总距离。特征空间任意一点 x 0 x_0 x0到超平面距离为: ∣ w ⋅ x 0 + b ∣ ∥ w ∥ \LARGE\frac{| w \centerdot x_0+b|}{\|w \|} wwx0+b, ∥ w ∥ \|w \| w为向量 w w w的模长。我们要计算的就是所有误分类的点到超平面的距离。若所有误分类点属于集合M,则
C o s t F u n c t i o n = ∑ x i ⊆ M ∣ w ⋅ x i + b ∣ ∥ w ∥ CostFunction = \sum\limits_{x_i \sube M}\LARGE\frac{| w \centerdot x_i+b|}{\|w \|} CostFunction=xiMwwxi+b
但是有绝对值,也不好对参数求导,于是想到若 ( x i , y i ) (x_i, y_i) (xi,yi) 误分类,即 − y i ( w ⋅ x i + b ) -y_i(w \centerdot x_i+b) yi(wxi+b)>0,所以绝对值可去,不过得加个负号。而且模长始终为正不用考虑,可以去掉,于是得到最终的损失函数:
L ( w , b ) = − ∑ x i ⊆ M y i ( w ⋅ x + b ) \Large L(w,b)=-\sum\limits_{x_i \sube M}y_i(w \centerdot x+b) L(w,b)=xiMyi(wx+b)
最优模型即是使以上损失函数达最小值时的模型。

3.感知机学习算法

3.1 原始形式

知道了什么样的模型是最优的,可是如何来求解最优模型模型呢?这就是学习算法要做的事。感知机利用 随机梯度下降法(stochastic gradient descent) 来求解出 w , b w,b w,b从而得出最优模型。
梯度我理解为偏导数组成的向量,损失函数 L ( w , b ) L(w,b) L(w,b)有两个参数,梯度也就有两个元素(有两个方向下降),L可以沿着这两个方向下降。

∂ L ( w , b ) ∂ w = − ∑ x i ⊆ M y i x i \LARGE\frac{\partial L(w,b)}{\partial w} =-\sum\limits_{x_i \sube M}y_ix_i wL(w,b)=xiMyixi
∂ L ( w , b ) ∂ b = − ∑ x i ⊆ M y i \LARGE\frac{\partial L(w,b)}{\partial b} =-\sum\limits_{x_i \sube M}y_i bL(w,b)=xiMyi

随机梯度下降法随机选取一个误分类点 ( x j , y j ) (x_j,y_j) (xj,yj)(而不是像在线性回归的梯度下降法中使用所有点)对 w , b w,b w,b进行更新,然而随机一误分类点并不能保证 w , b w,b w,b是一直是朝着正确的方向改进。
w ← w + η y j x j \LARGE w \leftarrow w + \eta y_jx_j ww+ηyjxj
b ← b + η y j \LARGE b \leftarrow b +\eta y_j bb+ηyj
η \Large\eta η 为步长,或叫学习率,决定L下降的快慢。那么我有疑问,这样更新L一定会逐渐下降吗?
更新前:
L 0 ( w , b ) = − ∑ x i ⊆ M y i ( w 0 ⋅ x i + b 0 ) \Large L_0(w,b)= \Large-\sum\limits_{x_i \sube M}y_i(w_0 \centerdot x_i+b_0) L0(w,b)=xiMyi(w0xi+b0)
更新 w 1 = w 0 + η y j x j ; b 1 = b 0 + η y j \LARGE w_1= w_0 + \eta y_jx_j ; \LARGE b_1 = b_0 +\eta y_j w1=w0+ηyjxj;b1=b0+ηyj
更新后 L 1 ( w , b ) = − ∑ x i ⊆ M y i ( w 1 ⋅ x i + b 1 ) = − ∑ x i ⊆ M y i ( ( w 0 + η y j x j ) ⋅ x i + b 0 + η y j ) = − ∑ x i ⊆ M y i ( w 0 ⋅ x i + b 0 ) − ∑ x i ⊆ M y i ( η y j x j ⋅ x i + η y j ) = L 0 ( w , b ) − ∑ x i ⊆ M y i ( η y j x j ⋅ x i + η y j ) \Large \begin{aligned} L_1(w,b) &=-\sum\limits_{x_i \sube M}y_i(w_1 \centerdot x_i+b_1)\\ &=-\sum\limits_{x_i \sube M}y_i( (w_0 + \eta y_jx_j)\centerdot x_i+b_0+\eta y_j)\\ &=-\sum\limits_{x_i \sube M}y_i(w_0\centerdot x_i+b_0) -\sum\limits_{x_i \sube M}y_i (\eta y_jx_j \centerdot x_i +\eta y_j)\\ &=L_0(w,b)-\sum\limits_{x_i \sube M}y_i (\eta y_jx_j \centerdot x_i +\eta y_j) \end{aligned} L1(w,b)=xiMyi(w1xi+b1)=xiMyi((w0+ηyjxj)xi+b0+ηyj)=xiMyi(w0xi+b0)xiMyi(ηyjxjxi+ηyj)=L0(w,b)xiMyi(ηyjxjxi+ηyj)
问题是,减号后面这项真的大于0吗,这个式子里面就一项一定是大于0的(即 i = j i=j i=j时),但是其他项就无法判断了吧。如果不是大于0就说明L不一定是一直下降的。然而感知机算法好像整个过程并没有计算过这个损失函数,它是所有误分类点导致的误差,这个算法的思想不是直接改进 w , b w,b w,b使它减小,而是逐步地改进它的每一个部分(也就是每一个误分类点),从而达到整体的改进。也就是说随机梯度下降算法计算的并不是真正的梯度,真正的梯度应该像批梯度计算那样需要所有样本才能计算。随机梯度只是用一个样本进行对真正的梯度进行了一次预估,因此可能会有偏差。即随机梯度下降算法不一定保证每次前进方向都是正确的,所以会带来波动。 在更新 ( w , b ) (w,b) (w,b)这个过程中,可能会出现总损失函数 L ( w , b ) L(w,b) L(w,b)上升的情况, 因为你只针对一个点更新 ( w , b ) (w,b) (w,b),有可能会使原先分类正确的点又误分类,也有可能使误分类的点距离更远,但是总体是螺旋式下降,所以如果数据集是线性可分,那么损失函数迭代下去就会向0靠近,下面证明为什么该算法虽然有波动,但最终还是会收敛到0。

3.2.1算法收敛性的证明

对于线性可分的数据集,利用批量梯度下降的话, ( w , b ) (w,b) (w,b)每一次的更新都必然使损失函数   L ( w , b ) \ L(w,b)  L(w,b)下降,最终必然会收敛到0,没必要进行收敛性的证明。但是因为使用随机了随机梯度下降,每一次 ( w , b ) (w,b) (w,b)的更新不是使总的损失函数   L ( w , b ) \ L(w,b)  L(w,b)下降,而是仅仅使随机误分类点 ( x j , y j ) (x_j,y_j) (xj,yj)的损失——即 − y i ( w ⋅ x + b ) -y_i(w \centerdot x+b) yi(wx+b) 下降。那么每一次更新都可能使得总损失函数   L ( w , b ) \ L(w,b)  L(w,b)上升,产生波动。那是否这个算法就是失效了呢?下面证明,即使   L ( w , b ) \ L(w,b)  L(w,b)不是一直下降,会产生波动,但是经过有限次迭代后,最终还是会收敛到0,可以找到一个将训练数据集完全正确划分的分离超平面。
证明的出发点是,证明误分类的次数k是有上限的,每一次误分类后 ( w , b ) (w,b) (w,b)都会更新,最多经过有限次(k的上限)次更新后,可以找到训练数据集完全正确划分的分离超平面。证明过程不写了,看其他博主的吧。

感知机算法的收敛证明

3.2对偶形式

对偶形式与原始形式的输入一样,但是输出略有差异,它是将 w , b w,b w,b表示为 x i x_i xi y i y_i yi的线性组合,输出的是 α \alpha α和b,而不是直接输出 w w w的最终值:
和上面更新相同 w 1 = w 0 + η y j x j \LARGE w_1= w_0 + \eta y_jx_j w1=w0+ηyjxj ; b 1 = b 0 + η y j \LARGE b_1 = b_0 +\eta y_j b1=b0+ηyj
那么如果整个迭代过程对误分类点 ( x i , y i ) (x_i,y_i) (xi,yi)更新了 n i n_i ni次(如果一直分类正确就是0次),这个点对 w 和 b w和b wb的贡献分别为 n i ∗ η x i y i n_i* \eta x_iy_i niηxiyi n i ∗ η y i n_i* \eta y_i niηyi,记 α i = n i ∗ η \alpha_i=n_i*\eta αi=niη, 最终 w , b w,b w,b的结果为:
w = ∑ i = 1 N n i ∗ η y i x i = ∑ i = 1 N α i y i x i \Large w =\displaystyle\sum_{i=1}^Nn_i*\eta y_ix_i=\displaystyle\sum_{i=1}^N\alpha _iy_ix_i w=i=1Nniηyixi=i=1Nαiyixi
b = ∑ i = 1 N n i ∗ η y i = ∑ i = 1 N α i y i \Large b =\displaystyle\sum_{i=1}^Nn_i*\eta y_i=\displaystyle\sum_{i=1}^N\alpha_i y_i b=i=1Nniηyi=i=1Nαiyi
所以最终输出的模型为: f ( x ) = s i g n ( ∑ i = 1 N α i y i x i ⋅ x + b ) f(x)=sign(\displaystyle\sum_{i=1}^N\alpha _iy_ix_i \centerdot x+b) f(x)=sign(i=1Nαiyixix+b)

4.python实现感知机算法

感知机的基本原理就写完了,但是对于一个机器学习算法,如果只停留在理解但是不会运用这个阶段,那是远远不够的,必须要通过一些实例,多敲代码,在这个过程加深对算法的理解。这里选取sklearn内置数据库的iris(鸢尾属植物)数据集进行实战演练。

import pandas as pd
import numpy as np
from sklearn.datasets import load_iris 
import matplotlib.pyplot as plt
%matplotlib notebook

将数据存储到dataframe中:

iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['label'] = iris.target
df

结果如下:
iris 数据表格

总共有4个特征——sepal花萼的长宽以及petal花瓣的长宽,但是为了更直观的观察数据,这里只取花萼的长宽这两个特征进行这次练习,并且感知机是二分类模型,所以只选取前100行(只包含0、1两类)。

df = df.iloc[:100, [0, 1, -1]]
df.columns = ['sepal_length', 'sepal_width', 'label']
df

结果如下:
在这里插入图片描述
画出图形:

plt.figure()
plt.scatter(df.iloc[:50, 0], df.iloc[:50,1], label='0')
plt.scatter(df.iloc[50:100, 0], df.iloc[50:100,1], label='1')
plt.xlabel('sepal_length')
plt.ylabel('sepal_width')
plt.legend()

在这里插入图片描述
直观上感觉是线性可分的。提取出特征与输出值,将标签0改为-1。

data = np.array(df)
X, y = data[:,:-1], data[:,-1]
y[y==0] = -1

4.1手写感知机算法

def perceptron(X,y,eta):
    w = np.zeros(X.shape[1])   # 初始化w,b
    b = 0
    def sign_y(x,w,b):              # 计算w*x+b的值,以便于乘以真实标记y判断是否误分类
        y1 = np.dot(x,w)+b
        return y1
    while True:   # 下面定义了循环结束条件,只要还有误分类点,就会一直循环下去
        wrong_count = 0    
        for i in range(len(X)): # 100个样本
            x = X[i]
            yi_lable = y[i]
            if yi_lable * sign_y(x,w,b)<=0: # 说明误分类了,更新w,b
                w = w + eta*yi_lable*x
                b = b + eta*yi_lable
                wrong_count += 1
        if wrong_count == 0:    # 定义函数结束条件,一次循环下来,误分类点为0,即所有点都正确分类了
            return w,b
w,b=perceptron(X,y,eta=0.6) # 解包

计算得到w=[ 31.6 , -40.28],b=-49.6,画出图形观察是否正确分类:

fig = plt.figure()
x_ticks = np.linspace(4.3,7,10)
ax = plt.subplot(1,1,1)
ax.set_xticks(x_ticks)
ax.set_xlim(4.2,7.1)
ax.set_ylim(1.9,4.5)
ax.set_xlabel('sepal_length')
ax.set_ylabel('sepal_width')
plt.scatter(df.iloc[:50, 0], df.iloc[:50,1], label='0')
plt.scatter(df.iloc[50:100, 0], df.iloc[50:100,1], label='1')
plt.plot(df['sepal_length'], (w[0]*df['sepal_length'] + b)/(-w[1]))
plt.legend(loc = 'best')

得到下图:
在这里插入图片描述
可以看到分类正确,那两个看起来在线上的点其实也是分列于两侧,在jupyter notebook图形操作界面用放大镜放大后就能看出来。

4.2 scikit-learn实操

手写只是为了加深对算法的理解,但是一般实操还是调包更快更方便。感知机官方参数介绍可以点击这里

4.2.1sklearn.linear_model.Perceptron参数解释

用于创建感知机模型时传递的参数。

参数名称参数取值参数解释
penalty默认=None,即不加惩罚项,‘l2’(L2正则) or ‘l1’(L1正则) or ‘elasticnet’(混合正则)惩罚项,加上惩罚项主要为了避免模型过拟合风险
alpha默认=0.0001,取值为浮点数如果penalty不为None,则正则化项需要乘上这个数
l1_ratio默认=0.15,取值在[0,1]一般只在penalty=elasticnet时用,当l1_ratio =0就是L2正则,当l1_ratio =1就是L1正则,当在(0,1)之间就是混合正则
fit_interceptbool值,默认=True是否对参数 截距项b进行估计,若为False则数据应是中心化的
max_iterint整数,默认=1000最大迭代次数,哪怕损失函数依旧大于0
tolfloat or None,默认=10^(-3)迭代停止的标准。如果不为None,那么当loss-pre-loss<tol的时候,就会停止迭代。因为当前迭代造成的损失函数下降太小了,迭代下去对loss影响不大了。
shufflebool值,默认=True每轮训练后是否打乱数据
verbose取值为整数,默认=0verbose = 0 为不在标准输出流输出日志信息,verbose = 1 为输出进度条记录;verbose = 2 为每个epoch输出一行记录
eta0取值双精度浮点型double,默认=1学习率,决定梯度下降时每次参数变化的幅度
n_jobs取值为 int or None,默认=None在多分类时使用的CPU数量,默认为None(或1),若为-1则使用所有CPU
random_state取值为int, RandomState instance or None,默认=None当 shuffle =True时,用于打乱训练数据
n_iter_no_change取值int,默认=5在提前停止之前等待验证分数无改进的迭代次数,用于提前停止迭代
early_stopping取值bool值,默认=False当验证得分不再提高时是否设置提前停止来终止训练。若设置此项,当验证得分在n_iter_no_change轮内没有提升时提前停止训练
class_weight取值为dict, {class_label: weight} 或者 “balanced”或者None,默认=None用于拟合参数时,每一类的权重是多少。当为None时,所有类的权重为1,等权重;当为balanced时,某类的权重为该类频数的反比,当为字典时,则key为类的标签,值为对应的权重
warm_start取值为bool,默认=False若为True则调用前一次设置的参数,使用新设置的参数

4.2.2sklearn.linear_model.Perceptron属性解释

属性名称属性的类型属性解释
classes_array 一维数组,shape=(k,) ,k为y的类别数量放着y所有分类的数组,如感知机是array([-1., 1.])
coef_array 二维数组输出训练后的模型参数w的数组,不包含截距项b。当为二分类时,该数组shape=(1,n),n为特征数量。当为多分类时shape=(k, n)
intercept_array 一维数组输出训练后的模型截距b的数组。当为二分类时,该数组shape=(1,)。当为多分类时shape=(k, )
loss_function_损失函数的类别即用的哪种损失函数来定义模型输出值与真实值之间的差异
n_iter_整数即模型停止时共迭代的次数
t_整数模型训练时,权重w更新的总次数,等于n_iter_*样本数量

4.2.3sklearn.linear_model.Perceptron实战

from sklearn.linear_model import Perceptron
perceptron = Perceptron(fit_intercept=True, max_iter=1000, shuffle=True)
perceptron.fit(X, y)  # 默认学习率为1
w = perceptron.coef_[0]  # ,注意输出的是二维数组,加上[0]后, w=[ 23.2 -38.7]
b = perceptron.intercept_  # b=-5

用拟合好的模型画出图形

在这里插入图片描述
参考:李航博士《统计学习方法》
二分类线性分类模型感知机从原理到实例就讲完了,请大家多多指教,谢谢阅读。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值