感知机是用于解决二类分类的线性分类模型,旨在求出将训练数据进行线性划分的分离超平面(对于线性不可分的训练集是无法进行学习和分类的)。感知机是支持向量机以及神经网络的基础。文章主要从感知机模型、感知机的学习策略以及感知机的学习算法三个方面介绍。
1 感知机模型
感知机的模型:
其中, x是输入向量, w是权值向量,两者都是n维的, b是实数偏置bias, sign是符号函数, w·x是向量内积。学习感知机模型,就是确定权值向量 w以及偏置 b的过程。
线性方程 w·x+b=0,对应于n维向量空间中的一个分离超平面 S, w是超平面的法向量, b是超平面的截距,超平面把n维向量空间划分为正负两类。经过对训练数据进行学习之后,训练数据中的两类数据,正好被分到了分离超平面的正负两部分。
2 感知机学习策略
训练数据集必须是线性可分的,也就是说在n维的向量空间中存在一个分类超平面S,可以将数据集中的正负实例点完全正确地划分到超平面的两侧。为了确定感知机模型中的参数:w以及b,需要定义损失函数并将损失函数极小化。
损失函数的定义:
其中, M是超平面 S的误分类点集。
最自然的损失函数是误分类点的总和,但是它不是参数w以及b的连续可导函数。其次,就是误分类点集到超平面的总距离,也就是上述损失函数。
首先,n维向量空间中的任意一点到分离超平面S的距离:
其次,对于误分类点(x i, y i),以下公式一定成立:
于是,误分类点到超平面的距离公式为:
最后,假设超平面的误分类点集合为M,那么所有误分类点到超平面的总距离为:
不考虑
给定训练数据集T,损失函数L(w, b)是关于w,b的连续可导函数。只需求是损失函数最小的w和b即可。
3 感知机学习算法
感知机学习问题转换成求解损失函数的最优化问题。感知机的学习算法是由误分类点驱动的,具体使用随机梯度下降算法。首先,随机选取超平面,而后用梯度下降不断地极小化损失函数。极小化过程中,每一次都是使用从误分类点中随机的选取一个误分类点进行梯度下降。
损失函数的梯度以下公式给出:
随机选取误分类点(x i, y i),对 w, b进行更新:
其中
感知器学习算法:
这种算法的直观解释:当实例点被误分类,即位于超平面错误的一侧,则调整 w和 b,是分离超平面向误分类点一侧移动,以减少该误分类点与超平面的距离,直至超平面越过该误分类点,使其可以被正确分类。
关于该学习算法的收敛性,请详见原书证明。
4 感知机学习算法的对偶形式
考虑感知机学习算法的对偶形式,假设上述学习算法中w与b的初始值都是0。对于误分点(xi, yi),使用如下方式更新:
逐步修改 w和 b,修改n次之后, w和 b关于误分点(x i, y i)的增量分别为
其中,
感知机学习算法的对偶形式:
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()
运行之后,在不断修改参数的过程中,分割直线如下图所示:
最后,感知机分割直线如下图,可以很好的将训练数据区分出来: