感知机(perceptron)是二分类的线性分类模型,输入为实例的特征向量,输出为实例的类别(取+1和-1)。感知机对应于输入空间中将实例划分为两类的分离超平面。感知机旨在求出该超平面,为求得超平面导入了基于误分类的损失函数,利用梯度下降法 对损失函数进行最优化(最优化)。感知机的学习算法具有简单而易于实现的优点,分为原始形式和对偶形式。感知机预测是用学习得到的感知机模型对新的实例进行预测的,因此属于判别模型。感知机由Rosenblatt于1957年提出的,是神经网络和支持向量机的基础。
1. 感知机模型
定义
假设输入空间(特征向量)为X⊆Rn,输出空间为Y={-1, +1}。输入x∈X表示实例的特征向量,对应于输入空间的点;输出y∈Y表示示例的类别。由输入空间到输出空间的函数为
f(x)=sign(w·x + b) (1)
称为感知机。其中,参数w叫做权值向量,b称为偏置。w·x表示w和x的内积。sign为符号函数,即
(2)
几何解释
感知机模型是线性分类模型,感知机模型的假设空间是定义在特征空间中的所有线性分类模型,即函数集合{f|f(x)=w·x+b}。线性方程 w·x+b=0对应于特征空间Rn中的一个超平面S,其中w是超平面的法向量,b是超平面的截踞。这个超平面把特征空间划分为两部分。位于两侧的点分别为正负两类。超平面S称为分离超平面,如下图:
学习与预测
感知机学习即由训练数据集T={(x1,y1),(x2,y2)...(xN,yN)}(其中xi∈X=Rn,yi∈Y={-1, +1},i=1,2...N)求得感知机模型(1),即求得参数w,b;感知机预测即根据得到的感知机模型(1),对新的输入实例给出对应的类型。
2. 感知机学习策略
假设训练数据集是线性可分的,感知机学习的目标是求得一个能够将训练数据的正负实例点完全分开的分离超平面,即最终求得参数w、b。这需要一个学习策略,即定义(经验)损失函数并将损失函数最小化。
损失函数的一个自然的选择是误分类的点的总数。但是这样得到的损失函数不是参数w、b的连续可导函数,不宜优化。损失函数的另一个选择是误分类点到分里面的距离之和。
首先,对于任意一点xo到超平面的距离为
(3)
其次,对于误分类点(xi,yi)来说 -yi(w·xi+b)>0
这样,假设超平面S的总的误分类点集合为M,那么所有误分类点到S的距离之和为
(4)
不考虑1/||w||,就得到了感知机学习的损失函数。
经验风险函数
给定数据集T={(x1,y1),(x2,y2)...(xN,yN)}(其中xi∈X=Rn,yi∈Y={-1, +1},i=1,2...N),感知机sign(w·x+b)学习的损失函数定义为
(5)
其中M为误分类点的集合,这个损失函数就是感知机学习的经验风险函数。
显然,损失函数L(w,b)是非负的。如果没有误分类点,那么L(w,b)为0,误分类点数越少,L(w,b)值越小。一个特定的损失函数:在误分类时是参数w,b的线性函数,在正确分类时,是0.因此,给定训练数据集T,损失函数L(w,b)是w,b的连续可导函数。
3. 感知机学习算法
最优化问题:给定数据集T={(x1,y1),(x2,y2)...(xN,yN)}(其中xi∈X=Rn,yi∈Y={-1, +1},i=1,2...N),求参数w,b,使其成为损失函数的解(M为误分类的集合):
(6)
3.1 感知机学习的原始形式
感知机学习是误分类驱动的,具体采用随机梯度下降法。首先,任意选定w0、b0,然后用梯度下降法不断极小化目标函数(6),极小化的过程不知一次性的把M中的所有误分类点梯度下降,而是一次随机选取一个误分类点使其梯度下降。
假设误分类集合M是固定的,那么损失函数L(w,b)的梯度由(7)(8)给出
(7)
(8)
随机选取一个误分类点(xi,yi),对w,b进行更新:
(9)
(10)
式中η(0≤η≤1)是步长,在统计学是中成为学习速率。步长越大,梯度下降的速度越快,更能接近极小点。如果步长过大,有可能导致跨过极小点,导致函数发散;如果步长过小,有可能会耗很长时间才能达到极小点。
算法(感知机学习算法的原始形式)
输入:T={(x1,y1),(x2,y2)...(xN,yN)}(其中xi∈X=Rn,yi∈Y={-1, +1},i=1,2...N,学习速率为η) 输出:w, b;感知机模型f(x)=sign(w·x+b) (1) 初始化w0,b0 (2) 在训练数据集中选取(xi, yi) (3) 如果yi(w xi+b)≤0 w = w + ηyixi b = b + ηyi (4) 转至(2)
直观解释:当一个实例点被误分类时,调整w,b,使分离超平面向该误分类点的一侧移动,以减少该误分类点与超平面的距离,直至超越该点被正确分类。
实现代码
#!/usr/bin/python import sys import getopt def usage(): print '''Help Information: -h, --help: show help information; -t, --train: train file; -r, --ratio: training ratio; -b, --bias: initial bias; -x, --slopex: initial slopex; -y, --slopey: initial slopey; ''' def getErrNum(errFlag): errnum = 0 for i in range(0,len(errFlag),1): errnum += errFlag[i] return errnum def getResult(data,slopex,slopey,bias): res = data[0]*slopex + data[1]*slopey + bias return res if __name__=="__main__": #set parameter try: opts, args = getopt.getopt(sys.argv[1:], "ht:r:b:x:y:", ["help", "train=","ratio=","bias=","slopex=","slopey="]) except getopt.GetoptError, err: print str(err) usage() sys.exit(1) sys.stderr.write("\ntrain.py : a python script for perception training.\n") sys.stderr.write("Copyright 2016 sxron, search, Sogou. \n") sys.stderr.write("Email: shixiang08abc@gmail.com \n\n") train = '' ratio = 0.1 bias = 0.0 slopex = 1.0 slopey = 1.0 for i, f in opts: if i in ("-h", "--help"): usage() sys.exit(1) elif i in ("-t", "--train"): train = f elif i in ("-r", "--ratio"): ratio = float(f) elif i in ("-b", "--bias"): bias = float(f) elif i in ("-x", "--slopex"): slopex = float(f) elif i in ("-y", "--slopey"): slopey = float(f) else: assert False, "unknown option" print "start trian parameter \ttrain:%s\tratio:%f\tbias:%f\tslopex:%f\tslopey:%f" % (train,ratio,bias,slopex,slopey) #read train file orgdata = [] label = [] fin = open(train,'r') while 1: line = fin.readline() if not line: break ts = line.strip().split('\t') if len(ts)==3: try: lbx = int(ts[0]) lby = int(ts[1]) lb = int(ts[2]) except: continue data = [] data.append(lbx) data.append(lby) orgdata.append(data) label.append(lb) fin.close() for i in range(0,len(label),1): print "%d\t%d\t%d" % (orgdata[i][0],orgdata[i][1],label[i]) errFlag = [] for i in range(0,len(label),1): errFlag.append(0) while 1: for i in range(0,len(label),1): errFlag[i] = 0 result = getResult(orgdata[i],slopex,slopey,bias) if result*label[i]<0: slopex = slopex + orgdata[i][0]*label[i]*ratio slopey = slopey + orgdata[i][1]*label[i]*ratio bias = bias + label[i]*ratio errFlag[i] = 1 errnum = getErrNum(errFlag) if errnum==0: break print "slopex:%f\tslopey:%f\tbias:%f\t" % (slopex,slopey,bias)