感知机的原始形式
对于输入样本特征数据,感知机通过以下函数将其映射至{+1,-1}的输出空间
对于所有的错分类点 i∈Mi∈M ,都有 −yi(w⋅xi+b)>0−yi(w⋅xi+b)>0 ,因此我们可以定义如下的损失函数作为优化准则:
通过求解损失函数(2)的梯度(3),
我们很容易就可以得到感知机学习算法的原始形式.
整个算法流程如下:
- 选取初值w0,b0
- 在训练集中任意选取点(xi,yi)
- 如果−yi(w⋅xi+b)>0−yi(w⋅xi+b)>0则按照(4)式更新w,b4
- 重复2直到没有被误分的点
感知机的对偶形式
相比于原始形式,其对偶形式在理解上没有前者那么直观,网上关于其实现代码的例子也比较少。
在《统计学习方法》一书中,关于对偶形式有如下的描述:
对偶形式的基本想法是,将w和b表示为实例xixi 和标记 yiyi 的线性组合的形式,通过求解其系数而求得w和b.
假设w0=0,b=0,那么从(4)式可以看出,当所有的点均不发生误判时,最后的w,b一定有如下的形式:
(5)
其中 αi=niηαi=niη 中 nini 代表对第i个样本的学习次数,感知机对偶形式的完整形式即为(6)式:
注:经过对偶形式变换,原始形式中 w 和 b的迭代转换成 α 和 b 的迭代。且此时训练数据中有多少个样本,α 值就有多少个值。
由于,表示第i个样本被用来调整参数的次数,表示学习率或者步长。也就说第i个样本被用来调整参数,则
累加一个,因此的更新方式就是累加。
- 初始化α=0α=0,b=0b=0.
- 任意选取(xi,yi)
- 如果yi(∑j=1Nαjyjxj⋅xi+b)≤0yi(∑j=1Nαjyjxj⋅xi+b)≤0,即发生误判,则对αi,bαi,b进行更新:
αi←αi+ηbi←bi+ηyiαi←αi+ηbi←bi+ηyi - 重复2直到所有点都被正确分类
简而言之,感知机的对偶形式就是把对w,bw,b的学习变成了对α,bα,b的学习,原始形式中,ww在每一轮迭代错分时都需要更新,而采用对偶形式时,对于某一点(xi,yi)发生错分时,我们只需要更新其对应的αiαi即可,最后按照(5)式即可一次计算出ww.
同时我们上述步骤3中的yi(∑j=1Nαjyjxj⋅xi+b)≤0yi(∑j=1Nαjyjxj⋅xi+b)≤0可以看出,xj⋅xixj⋅xi仅以内积的形式出现,因此我们可以是先计算出x的gram矩阵存储起来,这样正式训练时只需要查表就可以得到xj⋅xixj⋅xi的值,这样做可以方便程序的优化,提高运算的速度。
原始形式和对偶形式对参数b的处理是相同的。
例:训练数据集,其正实例点是 x1=(3,3)^T, x2=(4,3)^T ,负实例点是x3=(1,1)^T
python实现:
原始形式实现:
from __future__ import division
import random
import numpy as np
import matplotlib.pyplot as plt
def sign(v):
if v>=0:
return 1
else:
return -1
def train(train_num,train_datas,lr):
w=[0,0]
b=0
for i in range(train_num):
x=random.choice(train_datas)
x1,x2,y=x
if(y*sign((w[0]*x1+w[1]*x2+b))<=0):
w[0]+=lr*y*x1
w[1]+=lr*y*x2
b+=lr*y
return w,b
def plot_points(train_datas,w,b):
plt.figure()
x1 = np.linspace(0, 8, 100)
x2 = (-b-w[0]*x1)/w[1]
plt.plot(x1, x2, color='r', label='y1 data')
datas_len=len(train_datas)
for i in range(datas_len):
if(train_datas[i][-1]==1):
plt.scatter(train_datas[i][0],train_datas[i][1],s=50)
else:
plt.scatter(train_datas[i][0],train_datas[i][1],marker='x',s=50)
plt.show()
if __name__=='__main__':
train_data1 = [[1, 3, 1], [2, 2, 1], [3, 8, 1], [2, 6, 1]] # 正样本
train_data2 = [[2, 1, -1], [4, 1, -1], [6, 2, -1], [7, 3, -1]] # 负样本
train_datas = train_data1 + train_data2 # 样本集
w,b=train(train_num=50,train_datas=train_datas,lr=0.01)
plot_points(train_datas,w,b)
对偶形式实现:
from __future__ import division
import random
import numpy as np
import matplotlib.pyplot as plt
def sign(v):
if v>=0:
return 1
else:
return -1
def train(train_num,train_datas,lr):
w=0.0
b=0
datas_len = len(train_datas)
alpha = [0 for i in range(datas_len)]
train_array = np.array(train_datas)
gram = np.matmul(train_array[:,0:-1] , train_array[:,0:-1].T)
for idx in range(train_num):
tmp=0
i = random.randint(0,datas_len-1)
yi=train_array[i,-1]
for j in range(datas_len):
tmp+=alpha[j]*train_array[j,-1]*gram[i,j]
tmp+=b
if(yi*tmp<=0):
alpha[i]=alpha[i]+lr
b=b+lr*yi
for i in range(datas_len):
w+=alpha[i]*train_array[i,0:-1]*train_array[i,-1]
return w,b,alpha,gram
def plot_points(train_datas,w,b):
plt.figure()
x1 = np.linspace(0, 8, 100)
x2 = (-b-w[0]*x1)/(w[1]+1e-10)
plt.plot(x1, x2, color='r', label='y1 data')
datas_len=len(train_datas)
for i in range(datas_len):
if(train_datas[i][-1]==1):
plt.scatter(train_datas[i][0],train_datas[i][1],s=50)
else:
plt.scatter(train_datas[i][0],train_datas[i][1],marker='x',s=50)
plt.show()
if __name__=='__main__':
train_data1 = [[1, 3, 1], [2, 2, 1], [3, 8, 1], [2, 6, 1]] # 正样本
train_data2 = [[2, 1, -1], [4, 1, -1], [6, 2, -1], [7, 3, -1]] # 负样本
train_datas = train_data1 + train_data2 # 样本集
w,b,alpha,gram=train(train_num=500,train_datas=train_datas,lr=0.01)
plot_points(train_datas,w,b)