您的位置:首页 > 其它

统计机器学习笔记——感知机(1)

2015-06-27 14:25 381 查看
感知机由Fank Rosenblatt于1957年提出,是一个二元线性分类器模型,其输入若干训练样本的特征向量,输出为新测试样本的类别判断。从几何图形角度的解释,感知机可以理解为将输入空间样例划分为两类的超平面。



在使用感知机进行分类时,首先需要提供训练数据对感知机进行学习。感知机的学习目标是使分类的错误程度尽可能的小。通过几何解释我们可以发现,感知集的学习也就是要学习超平面表达式中的系数。

学习这个系数主要遵循的思路如下:

在没有任何先验知识的情况下,我们首先给感知机赋上随机的参数,即在空间中随机地确定一个超平面。

利用训练数据,考察利用当前超平面所得的分类结果与实际结果之间的差异。

以朝着减少差异的方向,调整超平面的参数,得到新的超平面。(这种调整是逐渐调整的)

重复步骤2、3直至超平面满足应用需求时停止。这样,最终的超平面的系数就确定了。从而感知机的学习完成。

学习思路中的步骤3如何实现呢?我们还是回到感知机的几何解释。设当前超平面的表达式为:

w1⋅x1+w2⋅x2+⋯+wm⋅xm+b=0

向量化描述为:

w⋅x+b=0

那么对于
20000
一个训练样本(即空间中的一个点)a=(a1,a2,⋯,am)

若w⋅a+b>0,则在当前超平面下的分类结果记为p=+1

若w⋅a+b<0,则在当前超平面下的分类结果记为p=−1

a的真实类别用y表示,可取值为{+1,−1}

而评价一个超平面的好坏可以通过分类错误的样例个数来实现,这样的表达为:

∑nisign(pi,yi)

其中sign(pi,yi)表示如果pi≠yi时,sign(pi,yi)=1,否则为0

n表示训练样本的个数

但是我们发现上面的误差度量与超平面的参数没有任何的相关性我们无法找到途径更新参数w,b。

由此我们考虑使用误分类点到超平面的距离作为超平面的评价超平面好坏的指标,也就是损失函数.损失函数的表达式定义为:

d(ai)=1||w|||w⋅ai+b|;其中ai表示一个训练样例, ||w||是w的L2范式。

进一步,在整个训练样本下的经验风险函数为:

S=∑nisign(Pi≠Ri)⋅d(ai)

S=1||w||∑nisign(Pi≠Ri)⋅|w⋅ai+b|

设误分类点有k个,对于误分类点必然有Rj⋅(w⋅aj+b)<0,Rj为第j个错误分类样本的真实类,则等式变为:

S=−1||w||∑kjRj⋅(w⋅aj+b)

用于w决定超平面的角度,||w||本身不能决定超平面,可以认为是与确定超平面的无关项。由此得到经验风险函数的表达式:

L(w,b)=−∑kjRj⋅(w⋅aj+b)

可以发现L(w,b)的特点是:L(w,b)非负且是连续可导函数(每个样本的损失函数是一个线性函数)

那么学习的目标即为求损失函数L(w,b)的极小值问题:minw,bL(w,b),优化方法采用的是随机梯度下降法(stochastic gradient descent,SGD)。极小化过程不是一次使用所有的误分类点的梯度下降,而是随机选择一个误分类点使其梯度下降。

假设误分类点集合M是固定的,那么损失函数L(w,b)的梯度由:

∇wL(w,b)=−∑xi∈Myixi

∇bL(w,b)=−∑xi∈Myi

从上式我们可以得到每个错误样本的参数梯度值:

∇wL(w,b)xi,yi=yixi

∇bL(w,b)xi,yi=yi

那么,参数更新方法为:

w←w+ηyixi

b←b+ηyi

需要指出的是,采用不同的初值或选取不同的误分类点,感知学习的解可以不同。

原始形式算法描述

选取初值w(0),b(0)。

在训练集中选取数据(xi,yi), xi为第i个样本的特征,yi为第i个样本的类别。

如果yi(w(l)⋅xi+b(l))≤0, 则w(l+1)←w(l)+ηyixi,b(l+1)←b(l)+ηyi;η为学习率。

转至2,直至训练集中没有误分类点。

原始形式算法的收敛性

为了便于叙述与推导,将偏置b并于权重向量w,记作w^=(wT,b)T,同样也将输入向量加以扩充,加进常数1,记为x^=(xT,1)T。这样,x^∈Rn+1,w^∈Rn+1。显然,w^⋅x^=w⋅x+b。

定理(Novikoff):假设训练数据集完全正确分开的,则

(1)存在满足条件||w^opt||=1的超平面w^opt⋅x^=wopt⋅x+bopt=0将训练数据集完全正确分开;且存在γ>0,对所有i=1,2,⋯,N

yi(w^opt⋅x^i)=yi(wopt⋅xi+bopt)≥γ

(2)令R=max1≤i≤N||x^i||,则感知机算法在训练数据集上的误分类次数k满足不等式

k≤(Rγ)2

证明:

(1)由于训练数据集线性可分,即存在超平面

w^opt⋅x^=wopt⋅x+bopt=0,且使||w^opt||=1

可将训练数据集完全正确分开。由于对有限的i=1,2,⋯,N,均有

yi(w^opt⋅x^i)>0

所以存在

γ=miniyi(wopt⋅xi+bopt)

使

yi(w^opt⋅x^i)=yi(wopt⋅xi+bopt)≥γ

(2)感知机算法从w^0=0开始,如果实例被误分类,则有

yi(w^k−1⋅x^i)≤0

则参数更新为

1.w^k=w^k−1+ηyix^i

2.w^k⋅w^opt=(w^k−1+ηyix^i)⋅w^opt

≥w^k−1⋅w^opt+ηγ

≥kηγ

3.||w^k||2=||w^k−1+ηyix^i||2

=||w^k−1||2+2ηyiw^k−1⋅x^i+η2||yi||2||x^i||2

≤||w^k−1||2+η2||x^i||2

≤||w^k−1||2+η2R2

≤||w^k−2||2+2η2R2≤⋯

≤kη2R2

4.kηγ≤w^k⋅w^opt≤||w^k||||w^opt||≤k√ηR

k2γ2≤kR2

k≤(Rγ)2

定理概括为:

1、在训练集可分的情况下,存在||w^opt||=1的超平面。所有的样本的yi(w^opt⋅x^i)>0,且存在最小的正数值γ

2、误分类的次数k是有上界,经过有限次搜索可以找到将训练数据完全正确分开的分离平面,也就是说,当训练数据集线性可分时,感知机学习算法原始形式迭代是收敛的。

而对于感知机的学习有两种学习形式:原始形式和对偶形式

对偶形式算法描述

α=(α1,α2,...,αN),α←0,b←0。

在训练集中选取数据(xi,yi), xi为第i个样本的特征,yi为第i个样本的类别。

如果yi((∑Nj=1αjyjxj)⋅xi+b(l))≤0, 则αi←αi+η,b←b+ηyi;η为学习率。

转至2,直至训练集中没有误分类点。

对偶形式中训练实例以内积形式出现。为了计算方便,可以预先将训练集中实例间的内积计算出来并以矩阵的形式存储,这个矩阵就是所谓的Gram矩阵。

对偶形式实际就是原始形式参数更新的和式表达形式:

w=∑Ni=1αiyixi

b=∑Ni=1αiyi

αi=niη (n_i是第i个样本的更新次数,样例点更新次数越多,则意味着它距分离超平面越近,也越难正确分类,这样的实例对学习结果影响最大)

代码实现

# -*- coding:utf8 -*-
import numpy as np
import matplotlib.pyplot as plt
import pdb

def generateData():

def createcolor(labels):
cs = []
for label in labels:
if label > 0:
cs.append("or")
else:
cs.append("oy")
return cs

data = np.random.random_sample((200, 2))
features = np.array([item for item in data if sum(item) - 1 != 0])
labels = np.array([-pow(-1, (sum(item) - 1) > 0) for item in features], dtype = np.float)
labels.resize(labels.shape[0], 1)
Data = np.hstack((features, labels))
with open("Perceptron.dat", "w") as filew:
np.save(filew, Data)

def loadData():
with open("Perceptron.dat", "r") as filer:
return np.load(filer)

def train_original(data):
w = np.array([0] * (data.shape[1] - 1))
b = 0
learning_rate = 0.1
max_iter_num = 10000
for iter_num in range(1, max_iter_num + 1):
flag = True
print "iter:", iter_num
for item in data:
if item[-1]*(sum(w * item[:-1]) + b) <= 0:
w = w + learning_rate * item[-1] * item[:-1]
b = b + learning_rate * item[-1]
flag = False
if flag == True:
break
rD = {"w": w, "b": b}
with open("perceptron_original.model", "w") as filew:
np.save(filew, rD)
return w, b

def draw_original(data, w, b):
color_list = []
for label in data[:,-1]:
if label == -1.0:
color_list.append("y")
else:
color_list.append("b")
plt.scatter(data[:,0], data[:,1], c = color_list)
X = np.array([min(data[:,0]), max(data[:,0])])
Y = [-(b+w[0]*num)/w[1] for num in X]
plt.plot(X, Y)
plt.show()

def original():
data = loadData()
w, b = train_original(data)
draw_original(data, w, b)

def train_Dual(data):

def calGram(data):
GramMatrix = np.zeros((data.shape[0], data.shape[0]))
for i in range(GramMatrix.shape[0]):
for j in range(i, GramMatrix.shape[0]):
GramMatrix[i][j] = sum(data[i,:-1] * data[j, :-1])
GramMatrix[j][i] = GramMatrix[i][j]
return GramMatrix

GramMatrix = calGram(data)

alpha = np.zeros((data.shape[0],))
b = 0
learning_rate = 0.3
max_iter_num = 1000

for iter_num in range(1, max_iter_num + 1):
flag = True
print "iter:", iter_num
for i in range(data.shape[0]):
if data[i][-1]*(np.sum(alpha * data[:,-1] * GramMatrix[:,i]) + b) <= 0:
alpha[i] = alpha[i] + learning_rate
b = b + learning_rate * data[i][-1]
flag = False
if flag == True:
break

rD = {"alpha": alpha, "b": b}
with open("perceptron_dual.model", "w") as filew:
np.save(filew, rD)

return alpha, b

def draw_Dual(data, alpha, b):
alpha_y = alpha.reshape((data.shape[0], 1)) * data[:,-1].reshape((data.shape[0], 1))
w = (alpha_y * data[:,:-1]).sum(axis = 0)
color_list = []
for label in data[:,-1]:
if label == -1.0:
color_list.append("y")
else:
color_list.append("b")
plt.scatter(data[:,0], data[:,1], c = color_list)
X = np.array([min(data[:,0]), max(data[:,0])])
Y = [-(b+w[0]*num)/w[1] for num in X]
plt.plot(X, Y)
plt.show()

def dual():
data = loadData()
alpha, b = train_Dual(data)
draw_Dual(data, alpha, b)

if __name__ == "__main__":
generateData()
original()
dual()
pass


参考文献:

1. http://en.wikipedia.org/wiki/Perceptron

2. 《统计学习方法》 李航
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  感知机