感知机(perceptron)是二类分类的线性分类模型,其输入时实例的特征向量,输出为实例的类别。取值为+1和-1。
感知机模型
定义:假设输入空间(特征空间)是Χ∈Rn(n维欧式空间),输出空间是Y={1,-1}。输入x∈X表示实例的特征向量,对应于输入空间(特征空间)的点;输出y∈Y表示实例的类别。由输入空间到输出空间的映射如下:称为感知机。其中w∈Rn叫做权值,b∈R(实数)表示偏置。
感知机的学习策略
数据集
感知机训练的前提是训练集中的实例点都是线性可分的。
定义:给定一个数据集T={(x1,y1),(x2,y2),(x3,y3),……,(xn,yn)} 其中xi∈X=Rn (xi为向量不能理解为实数)yi∈Y={+1,-1}。
感知机训练的目的是找到一个能将训练集中的正实例点和负实例点完全分开的超平面。即确定感知机参数w,b的值。感知机所采用的学习策略是将经验损失极小化。将误分类点到超平面S的距离之和极小化。
Rn空间中x0点到超平面的距离: (类比点到直线距离)。因此对于误分类点(xi,yi)来说所以xi点到超平面的距离s为:。
假设误分类点的集合为M,那么所有的误分类点到超平面s的总的距离为:
其中xi∈M。
感知机损失函数定义:
给定训练数据集T={(x1,y1),(x2,y2),(x3,y3),……,(xn,yn)},xi∈X=Rn,yi∈Y={+1,-1},i=1,2,3……,N 感知机损失函数定义为:(w,b)= 其中xi∈M。(说明1/||w||是常数,定义损失函数时不考虑)M是误分类点的集合,这个损失函数就是感知机学习的经验分险函数。显然如果没有误分类点,损失函数的数值是0,误分类点越少,误分类函数的值就越小。损失函数(w,b)是连续可导函数。
感知机学习算法
感知机学习的目是求参数w,b使其为以下损失函数极小化问题的解: 其中xi∈M,并且损失函数时关于w,b的函数。在极小化的过程中不是一次使用M中所有的误分类点的梯度下降,而是一次随机选取一个误分类点使其梯度下降。损失函数的梯度:由▽w,▽b 给出:w ←w+η ,b←b+η 其中η是学习率。
原始感知机的代码实现:(github链接:https://github.com/sparrowljq/StatisticalLearningMethod.git)
#原始感知机代码 | |
import numpy as np | |
import matplotlib.pyplot as plt | |
# 输入三个点x1(3,3)、x2(4,3)、x3(1,1)其中x1,x2为正实例点,x3为负实例点 | |
p_x = np.array([[3, 3], [4, 3], [1, 1]]) | |
y = np.array([1, 1, -1]) | |
# 绘制初始点 | |
for i in range(len(p_x)): | |
if y[i] == 1: | |
# r表示红色 o表示圆点 | |
plt.plot(p_x[i][0], p_x[i][1], 'ro') | |
else: | |
# b表示蓝色 o表示圆点 | |
plt.plot(p_x[i][0], p_x[i][1], 'bo') | |
# w超平面的法向量,b是超平面的截距,delta是指的是学习率 | |
w = np.array([0, 0]) | |
b = 0 | |
delta = 1 | |
for i in range(100): | |
choice = -1 | |
for j in range(len(p_x)): | |
# dot函数表示向量点积 | |
if y[j] != np.sign(np.dot(w, p_x[j]) + b): | |
choice = j | |
break | |
# 表示三个点都被正确分类 | |
if choice == -1: | |
break | |
w = w + delta * y[choice] * p_x[choice] | |
b = b + delta * y[choice] | |
line_x = [0, 10] | |
line_y = [0, 0] | |
print(w[1]) | |
# 超平面表达式为wx+b=0 其中w为向量 w1x1+w2x2+b=0 x2=(-w1x1-b)/w2 | |
for i in range(len(line_x)): | |
line_y[i] = (-w[0] * line_x[i] - b) / w[1] | |
print(line_y) | |
plt.plot(line_x, line_y) | |
plt.savefig("picture.png") | |
plt.show() |
对偶感知机:
对偶形式
对偶形式的基本想法是,将 w 和 b 表示为是咧 xi 和标记 yi的线性组合的形式,通过求解其系数而得到 w 和 b。
w←w+ηyixi
b←b+ηyi
逐步修改 w,b,设修改 n 次,则 w,b 关于(xi,yi)(的增量分别是 αiyixi和 αiyi, 这里 αi=niη。最后学习到的 w,b 可以分别表示为:
w=∑αiyixi(其中i=1,2,……,N)
b=∑αiyi(其中i=1,2,……,N)
这里, αi≥0,i=1,2,...,Nαi≥0,i=1,2,...,N,当 η=1时,表示第i个是实例点由于误分类而进行更新的次数,实例点更新次数越多,说明它距离分离超平面越近,也就越难区分,该点对学习结果的影响最大。
感知机模型对偶形式:
f(x)=sign(∑αjyjxj⋅x+b)(其中j=1,2,……,N)
其中
α=(α1,α2,...,αN)
学习时初始化 α←0,b←0在训练集中找分类错误的点,即:
yi(∑αjyjxj⋅xi+b)≤0(其中j=1,2,……,N)
然后更新:
αi←αi+η
b←b+ηyi
知道训练集中所有点正确分类
对偶形式中训练实例仅以内积的形式出现,为了方便,可以预先将训练集中实例间的内积计算出来以矩阵的形式存储,即 Gram 矩阵。
对偶感知机的代码实现:(github链接:https://github.com/sparrowljq/StatisticalLearningMethod.git)
# 对偶感知机的代码 | |
import numpy as np | |
import matplotlib.pyplot as plt | |
p_x = np.array([[3, 3], [4, 3], [1, 1]]) | |
y = np.array([1, 1, -1]) | |
# 绘制初始点 | |
for i in range(len(p_x)): | |
if y[i] == 1: | |
plt.plot(p_x[i][0], p_x[i][1], 'ro') | |
else: | |
plt.plot(p_x[i][0], p_x[i][1], 'bo') | |
# 计算Gram矩阵 | |
Gram = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0]]) | |
for i in range(len(p_x)): | |
for j in range(len(p_x)): | |
# dot函数表示矩阵的内积 | |
Gram[i][j] = np.dot(p_x[i], p_x[j]) | |
# 初始化α1,α2,α3的值 | |
afa = np.array([0, 0, 0]) | |
# 初始化法平面截距 | |
b = 0 | |
# 初始化学习率η的值 | |
delta = 1 | |
while 1: | |
# 用来表示所有的点是否被正分类 | |
choice = 1 | |
for i in range(len(p_x)): | |
if y[i] == np.sign(np.dot(afa * y, Gram[i]) + b): | |
continue | |
while 1: | |
# 只要进入该循环表示有点未正确分类 | |
choice = -1 | |
if y[i] == np.sign(np.dot(afa*y, Gram[i])+b): | |
break | |
else: | |
afa[i] = afa[i]+delta | |
b = b+delta*y[i] | |
if choice == 1: | |
break | |
print(afa) | |
w = np.dot(afa*y, p_x) | |
print(b) | |
line_x = [0, 10] | |
line_y = [0, 0] | |
for i in range(len(line_x)): | |
line_y[i] = (-w[0]*line_x[i]-b)/w[1] | |
plt.plot(line_x, line_y) | |
plt.savefig("a.png") | |
plt.show() |
程序执行结果: