Python实现感知机(Perceptron)

1.感知机回归概念

感知机(Perceptron)是二分类的线性分类模型,其输入是实例的特征向量,输出是实例的类别,取+1及-1二值。感知机是在1957年由Rosenblatt提出,今天看来它的分类模型在大多数时候泛化能力不强,但是它的原理却值得好好研究。因为研究透了感知机模型,学习支持向量机的话会降低不少难度。同时如果研究透了感知机模型,再学习神经网络,深度学习,也是一个很好的起点。

假设输入空间是 χ R n χ⊆R^n ,输出空间是 γ = ( + 1 , 1 ) γ=(+1,−1) 。输入 χ X χ∈X 表示实例的特征向量,对应于输入空间的点;输出 y γ y∈γ 表示实例的类别。由输入空间到输出空间的如下函数:

f ( x ) = s i g n ( w x + b ) f(x)=sign(wx+b)

其中, w w (权值)和 b b (偏置)为感知机模型的参数, w x wx 表示 w w x x 的内积,sign是符号函数,即:
s i g n ( x ) = { + 1 x 0 1 x < 0 sign\left( x\right) =\begin{cases}+1 &x\geq 0\\-1 &x<0 \end{cases}

感知机学习策略

假设训练数据集是线性可分的,感知机学习的目标就是求得一个能够将训练集正实例点和负实例点完成正确分开的分离超平面。为了找出这样的超平面,即确定感知机模型参数 w w b b ,需定义损失函数并将损失函数极小化。损失函数使用误分类点到超平面 S S 的总距离,因此输入空间 R n R^n ​中任何一点 x 0 x_0 ​到超平面 S S 的距离为:

1 w w x + b \frac{1}{∥w∥}|wx+b|

在这里, w ∥w∥ w w 的L2范数。对于误分类的数据 ( x i , y i ) (x_i,y_i) 来说, y i ( w x i + b ) > 0 −y_i(wx_i+b)>0
成立。因为当 w x i + b > 0 wx_i+b>0 时, y i = 1 y_i=−1 ,而当 w x i + b < 0 wx_i+b<0 时, y i = + 1 y_i=+1 ,因此,误分类点 x i x_i 到超平面 S S 的距离是:

1 w y i ( w x i + b ) −\frac{1}{∥w∥}y_i(wx_i+b)

这样,假设超平面 S S 的误分类点集合为 M M ,那么所以误分类点到超平面 S S 的总距离为:

1 w x i M y i ( w x i + b ) −\frac{1}{∥w∥}∑_{x_i∈M}y_i(wx_i+b)

不考虑 1 w \frac{1}{∥w∥} ,就得到感知机学习的损失函数:

L ( w , b ) = x i ε n y i ( w x i + b ) L(w,b)=−∑_{x_iεn}y_i(wx_i+b)

其中M为误分类点的集合。

2.感知机学习算法的原始形态

感知机算法是使得损失函数 L ( w , b ) L(w,b) 极小化的最优化问题,可以使用随机梯度下降法来进行最优化。假设误分类点集合M是固定的,那么损失函数 L ( w , b ) L(w,b) 的梯度由

w L ( ω , b ) = x i ε n y i x i \nabla _{w}L\left( \omega ,b\right) =-\sum _{x_{i\varepsilon }n}y_{i}x_{i}

b L ( ω , b ) = x i ε n y i \nabla _{b}L\left( \omega ,b\right) =-\sum _{x_{i\varepsilon }n}y_{i}

给出,随机选取一个误分类点 ( x i , y i ) (x_i,y_i) ,对 w b w,b 进行更新:

w w + η y i x i w\leftarrow w+\eta y_ix_i

b b + η y i b\leftarrow b+\eta y_i

其中 η ( 0 < η 1 ) η(0<η≤1) 称为学习率(learning rate),这样通过迭代可以使得损失函数 L ( w , b ) L(w,b) 不断减小,直到为0。

感知机算法原始形式的主要训练过程,《统计学习方法》李航,P29页:
在这里插入图片描述

Python代码实现主要函数:

# 感知机学习算法的原始形态
def perceptron(dataSet,loop_max=100):
    eta=1 # 学习率
    features=dataSet.shape[1]-1   # x特征列数量
    w=np.array([x*0 for x in range(0,features)])
    b=0
    for times in range(loop_max):
        for d in dataSet:
            x=d[:-1]
            y=d[-1]
            if y*([email protected]+b)<=0: # @符号作用同np.dot
                w=w+eta*y*x
                b=b+eta*y
    return w,b

3.感知机学习算法的对偶形式

假设 w 0 w_0 b 0 b_0 均初始化为0,对误分类点通过

w w + η y i x i w←w+ηy_ix_i

b b + η y i b←b+ηy_i

逐步修改 w b w,b ,设修改n次,则 w b w,b 关于 ( w i , y i ) (w_i,y_i) 的增量分为是 α i y i x i α_iy_ix_i α i y i α_iy_i ,这里的 α i = n i η α_i=n_iη ,其中ni表示第i个点误分类的次数,这样最后学习到的 w b w,b 可以分别表示为

w = i = 1 N α i y i x i w=∑_{i=1}^{N}α_iy_ix_i

b = i = 1 N α i y i b=∑_{i=1}^{N}α_iy_i

实例点更新的次数越多,意味着它距离分离超平面越近,也就越难正确分类。

训练过程,《统计学习方法》李航,P33-34页:

输出 α , b α,b ,其中 α = ( α 1 , α 2 , . . . , α N ) T α=(α_1,α_2,...,α_N)^T
(1) α 0 , b 0 α←0,b←0

(2)在训练集中选取数据 ( x i , y i ) (x_i,y_i)

(3)如果 y i ( j = 1 n α j y j x j x i + b ) 0 y_i(∑_{j=1}^nα_jy_jx_j⋅x_i+b)≤0 ,则

α i α i + η α_i←α_i+η

b b + η y i b←b+ηy_i

(4)转到(2),直到没有错误。

最后通过 w = i = 1 N α i y i x i w=∑_{i=1}^Nα_iy_ix_i 计算出 w w ,使用上述过程求出的 b b ,即计算出模型参数。

Python代码实现主要函数:

# 感知机算法对偶形式
def perceptron_dual(dataSet,loop_max=100):
    m, n = dataSet.shape
    a=np.zeros((m, 1))
    b=0
    x_fea=dataSet[:,:-1]
    [email protected]_fea.T          # @符号作用同np.dot
    #G=np.dot(x_fea,x_fea.T) # 两种写法均可
    x=dataSet[:,:-1]
    y=dataSet[-1].reshape((-1,1))
    
    for times in range(loop_max):
        for i in range(0,m):
            yi=y[i]
            err=0
            err=sum(a*y*G[i].reshape((m,1)))+b
            if yi*err<=0:
                a[i]=a[i]+1
                b=b+yi
                break
    print('a:',a.T,'b:',b)
    w=(a*y)[email protected]
    return w,b

程序输出结果:

在这里插入图片描述

以上算法完整源代码:

# -*- coding: utf-8 -*-
"""
 @Time    : 2018/11/21 09:22
 @Author  : hanzi5
 @Email   : [email protected]
 @File    : perceptron.py
 @Software: PyCharm
"""

import numpy as np
#import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D

# 感知机学习算法的原始形态
def perceptron(dataSet,loop_max=100):
    eta=1 # 学习率
    features=dataSet.shape[1]-1   # x特征列数量
    w=np.array([x*0 for x in range(0,features)])
    b=0
    for times in range(loop_max):
        for d in dataSet:
            x=d[:-1]
            y=d[-1]
            if y*([email protected]+b)<=0: # @符号作用同np.dot
                w=w+eta*y*x
                b=b+eta*y
    return w,b

# 感知机算法对偶形式
def perceptron_dual(dataSet,loop_max=100):
    m, n = dataSet.shape
    a=np.zeros((m, 1))
    b=0
    x_fea=dataSet[:,:-1]
    [email protected]_fea.T          # @符号作用同np.dot
    #G=np.dot(x_fea,x_fea.T) # 两种写法均可
    x=dataSet[:,:-1]
    y=dataSet[-1].reshape((-1,1))
    
    for times in range(loop_max):
        for i in range(0,m):
            yi=y[i]
            err=0
            err=sum(a*y*G[i].reshape((m,1)))+b
            if yi*err<=0:
                a[i]=a[i]+1
                b=b+yi
                break
    print('a:',a.T,'b:',b)
    w=(a*y)[email protected]
    return w,b

# 可视化结果
def plotResult2(dataSet, weight, bias):
    fig = plt.figure()
    axes = fig.add_subplot(111)

    # 取两类x1及x2值画图
    type1_x1=dataSet[dataSet[:,-1]==-1][:,:-1][:,0].tolist() 
    type1_x2=dataSet[dataSet[:,-1]==-1][:,:-1][:,1].tolist()
    type2_x1=dataSet[dataSet[:,-1]==1][:,:-1][:,0].tolist()
    type2_x2=dataSet[dataSet[:,-1]==1][:,:-1][:,1].tolist()
    
    # 画点,使用红蓝两色
    axes.scatter(type1_x1, type1_x2, marker='x', s=20, c='red')
    axes.scatter(type2_x1, type2_x2, marker='o', s=20, c='blue')
    
    # 画线
    y = (0.1 * -weight[0] / weight[1] + -bias / weight[1], 4.0 * -weight[0] / weight[1] + -bias / weight[1])
    axes.add_line(Line2D((0.1, 4.0), y, linewidth=1, color='blue'))

    # 画坐标轴标签
    plt.xlabel('X')
    plt.ylabel('Y')
    plt.show()

if __name__ == '__main__':
    # 生成数据,《统计学习方法》李航,P29,例2.1
    dataSet=np.array([[3,3,1],[4,3,1],[1,1,-1]])
    
    # 感知机学习算法的原始形态
    w,b=perceptron(dataSet)
    print('感知机学习算法的原始形态,计算结果如下:')
    print('w:',w,'b:',b)
    print()
    
    # 感知机学习算法的对偶形式
    print('感知机学习算法的对偶形式,计算结果如下:')
    w2,b2=perceptron_dual(dataSet)
    print('w2:',w,'b2:',b)
    
    # 画图
    plotResult2(dataSet, w2.reshape((-1,1)), b2)

参考资料:
1、《机器学习实战》Peter Harrington著
2、《机器学习》西瓜书,周志华著
3、 斯坦福大学公开课 :机器学习课程
4、机器学习视频,邹博
5、《统计学习方法》李航
6、感知机原理小结
7、机器学习算法–Perceptron(感知机)算法