统计学习方法——感知机(perceptron)

感知机是用于解决二类分类的线性分类模型,旨在求出将训练数据进行线性划分的分离超平面(对于线性不可分的训练集是无法进行学习和分类的)。感知机是支持向量机以及神经网络的基础。文章主要从感知机模型、感知机的学习策略以及感知机的学习算法三个方面介绍。

1 感知机模型

感知机的模型:

感知机模型

其中, x是输入向量, w是权值向量,两者都是n维的, b是实数偏置bias, sign是符号函数, w·x是向量内积。学习感知机模型,就是确定权值向量 w以及偏置 b的过程。
线性方程 w·x+b=0,对应于n维向量空间中的一个分离超平面 Sw是超平面的法向量, b是超平面的截距,超平面把n维向量空间划分为正负两类。经过对训练数据进行学习之后,训练数据中的两类数据,正好被分到了分离超平面的正负两部分。

2 感知机学习策略

训练数据集必须是线性可分的,也就是说在n维的向量空间中存在一个分类超平面S,可以将数据集中的正负实例点完全正确地划分到超平面的两侧。为了确定感知机模型中的参数:w以及b,需要定义损失函数并将损失函数极小化。

损失函数的定义:

损失函数

其中, M是超平面 S的误分类点集。

最自然的损失函数是误分类点的总和,但是它不是参数w以及b的连续可导函数。其次,就是误分类点集到超平面的总距离,也就是上述损失函数。
首先,n维向量空间中的任意一点到分离超平面S的距离:

距离

其次,对于误分类点(x i, y i),以下公式一定成立:
距离

于是,误分类点到超平面的距离公式为:
距离

最后,假设超平面的误分类点集合为M,那么所有误分类点到超平面的总距离为:
总距离

不考虑 分母,就得到了上面提到的损失函数。

给定训练数据集T,损失函数L(w, b)是关于wb的连续可导函数。只需求是损失函数最小的wb即可。

3 感知机学习算法

感知机学习问题转换成求解损失函数的最优化问题。感知机的学习算法是由误分类点驱动的,具体使用随机梯度下降算法。首先,随机选取超平面,而后用梯度下降不断地极小化损失函数。极小化过程中,每一次都是使用从误分类点中随机的选取一个误分类点进行梯度下降。
损失函数的梯度以下公式给出:

梯度

随机选取误分类点(x i, y i),对 wb进行更新:
更新

其中 学习速率是学习速率,通过迭代可以期待损失函数不断减少,直到为0。
感知器学习算法:
学习算法

这种算法的直观解释:当实例点被误分类,即位于超平面错误的一侧,则调整 wb,是分离超平面向误分类点一侧移动,以减少该误分类点与超平面的距离,直至超平面越过该误分类点,使其可以被正确分类。
关于该学习算法的收敛性,请详见原书证明。

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

考虑感知机学习算法的对偶形式,假设上述学习算法中wb的初始值都是0。对于误分点(xi, yi),使用如下方式更新:

更新

逐步修改 wb,修改n次之后, wb关于误分点(x i, y i)的增量分别为 1213,这里 14。这样最后学习到的 wb可以分别表示为:
对偶形式

其中, 16,当 17时,表示第i个实例点因为误分而对参数进行更新的次数。

感知机学习算法的对偶形式:
对偶学习算法1
对偶学习算法2

5 python代码实现

只有这部分完完全全是自己的!!!
代码github地址:github

#!/usr/bin/python                                                                                                                                            
#coding=utf-8
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation

def read_data(data_path = "./data"):
    """
    读取数据
    """
    data = list()
    for line in open(data_path):
        cols = line.strip().split()
        point = [float(val) for val in cols[:-1]]
        #用1扩充输入向量,因为程序中将bias并入权重向量w中了
        point.append(1.0)   #add the bias into point
        value = int(cols[-1])
        data.append([np.array(point), value])

    return data

data = read_data("data_basic")

def pla():
    global data
    data_len = len(data)
    w = np.array([1.0, 1.0, 0.0])   #最后一维为bias
    ret_w = [w]
    alpha = 0.5
    flag = 0
    while(True):
        for item in data:
            if np.sign(np.dot(item[0], w)) != item[1]:
                flag = 0
                #更新权值w
                w = w + item[1]*alpha*item[0]
                #将每次调整后的权值w保存到列表中,用于绘制算法的动态调整图
                ret_w.append(w)
                break
            flag += 1
        if flag == data_len:
            break
    return ret_w


data_n = [item[0] for item in data if item[1] == -1]
data_p = [item[0] for item in data if item[1] == 1]
data_n_x = [val[0] for val in data_n]
data_n_y = [val[1] for val in data_n]
data_p_x = [val[0] for val in data_p]
data_p_y = [val[1] for val in data_p]

fig = plt.figure()
ax = fig.add_subplot(111)

w_lst = pla()
#绘制输入数据的散点图,红色为负,蓝色为正
plt.scatter(data_n_x, data_n_y, c = "red")
plt.scatter(data_p_x, data_p_y, c = "blue")
plt.axis([0, 3.0, 0, 3])
line, = ax.plot([], [], c = "g", lw = 1)

def animate(data):
    x = [0, -data[2]/data[0]]
    y = [-data[2]/data[1], 0]
    line.set_data(x, y)
    return line,
#动态绘制感知器算法在调整权值w时,划分直线所在的位置
ani = animation.FuncAnimation(fig, animate, w_lst, interval = 1500, repeat=False)
plt.show()

运行之后,在不断修改参数的过程中,分割直线如下图所示:

效果图

最后,感知机分割直线如下图,可以很好的将训练数据区分出来:
结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值