-
-
【原创】Liu_LongPo
转载请注明出处【CSDN】http://blog.csdn.net/llp1992感知机
感知机是二分类的线性分类模型,输入为实例的特征向量,输出为实例的类别(取+1和-1)。感知机对应于输入空间中将实例划分为两类的分离超平面。感知机旨在求出该超平面,为求得超平面导入了基于误分类的损失函数,利用梯度下降法对损失函数进行最优化(最优化)。感知机的学习算法具有简单而易于实现的优点,分为原始形式 和 对偶形式。感知机预测是用学习得到的感知机模型对新的实例进行预测的,因此属于判别模型。感知机是一种线性分类模型,只适应于线性可分的数据,对于线性不可分的数据模型训练是不会收敛的。
感知机模型
假设输入空间(特征向量)是 x∈Rnx∈Rn 输出空间为 Y={−1,+1}Y={−1,+1},输入 x∈Xx∈X表示表示实例的特征向量,对应于输入空间的点,输出 y∈Yy∈Y表示实例的类别,则由输入空间到输出空间的表达形式为:
f(x)=sign(w⋅x+b)f(x)=sign(w⋅x+b)
该函数称为感知机,其中w和bw和b 称为模型的参数,w∈Rnw∈Rn称为权值,bb 称为偏置,w⋅xw⋅x 表示 w和xw和x 的内积这里
sign(x)={+1,x>0−1,x<0sign(x)={+1,x>0−1,x<0如果我们将sign称之为激活函数的话,感知机与logistic regression的差别就是感知机激活函数是sign,logistic regression的激活函数是sigmoid。sign(x)将大于0的分为1,小于0的分为-1;sigmoid将大于0.5的分为1,小于0.5的分为0。因此sign又被称为单位阶跃函数,logistic regression也被看作是一种概率估计。
在神经网络以及DeepLearning中会使用其他如tanh,relu等其他激活函数。
感知机的学习策略
如下图: 线性可分数据集
对于一个给定的线性可分的数据集,如上面的数据点,红色代表正类,蓝色代表负类,感知机的主要工作就是寻找一个线性可分的超平面 S:{w⋅x+b=0}S:{w⋅x+b=0} , 该超平面能够将所有的正类和负类完全划分到超平面的两侧。而这就是上图中的红色直线,也就是说,感知机算法实际上就是找一条直线将数据集进行正确划分。
如果数据集可以被一个超平面完全划分,则称该数据集是线性可分的数据集,否则称为线性不可分的数据集,如下图:非线性可分数据集
感知机分类效果:
logistic regression分类效果:
为了寻找上面那条能够将数据集完全划分的超平面 S:w⋅x+b=0S:w⋅x+b=0 我们需要确定一个学习策略,也就是常说的loss function 以及梯度下降法迭代求解。
为了使用梯度下降法求解 w和bw和b ,我们的loss function必须是 w和bw和b 的连续可导函数,因此可以选择loss function为:误分类点到超平面 SS 的总距离。令输入 x0x0 到超平面 SS 的距离为 1||w|||w⋅x0+b|1||w|||w⋅x0+b| ,其中 ||w||||w|| 为 ww 的L2L2 范数。
对误分类点(xi,yi)(xi,yi) 来说,有 −yi(w⋅xi+b)>0−yi(w⋅xi+b)>0 ,则可得loss function为:
L(w,b)=−1||w||∑xi∈Myi(w⋅xi+b)L(w,b)=−1||w||∑xi∈Myi(w⋅xi+b)感知机的原始形式
伪代码
输入:训练数据集 T=(x1,y1),(x2,y2),⋯,(xN,yN),T=(x1,y1),(x2,y2),⋯,(xN,yN), 其中 xi∈X=Rn,yi∈Y=−1,+1.i=1,2,⋯,N;xi∈X=Rn,yi∈Y=−1,+1.i=1,2,⋯,N; 学习率 η(0<η≤1)η(0<η≤1)
输出:w,bw,b ;感知机模型 f(x)=sign(w⋅x+b)f(x)=sign(w⋅x+b)
1、选取初值 w0,b0w0,b0
2、在训练集中选取数据 (xi,yi)(xi,yi)
3、yi(w⋅xi+b)≤0yi(w⋅xi+b)≤0w=w+ηyixib=b+ηyiw=w+ηyixib=b+ηyi
4、转至2直到没有误分类点直观解释:当一个实例点被误分的时候,即调整 w,bw,b , 也就是调整 超平面所在的直线,让误分类的点划分到正确的一侧。
实现代码
""" @Description : perceptron by python @Author: Liu_Longpo @Time: Sun Dec 20 12:57:00 2015 """ import matplotlib.pyplot as plt import numpy as np import time trainSet = [] w = [] b = 0 lens = 0 alpha = 0 # learn rate , default 1 trainLoss = [] def updateParm(sample): global w,b,lens,alpha for i in range(lens): w[i] = w[i] + alpha*sample[1]*sample[0][i] b = b + alpha*sample[1] def calDistance(sample): global w,b res = 0 for i in range(len(sample[0])): res += sample[0][i] * w[i] res += b res *= int(sample[1]) return res def trainMLP(Iter): print "training MLP..." print "-"*40 epoch = 0 for i in range(Iter): train_loss = 0 update = False print "epoch",epoch, " w: ",w,"b:",b, for sample in trainSet: res = calDistance(sample) if res <= 0: train_loss += -res update = True updateParm(sample) print 'train loss:',train_loss trainLoss.append(train_loss) if update: epoch = epoch+1 else: print "The training have convergenced,stop trianing " print "Optimum W:",w," Optimum b:",b #os._exit(0) break # early stop update = False if __name__=="__main__": ''' if len(sys.argv)!=4: print "Usage: python MLP.py trainFile modelFile" exit(0) alpha = float(sys.argv[1]) trainFile = open(sys.argv[2]) modelPath = sys.argv[3] ''' alpha = float(0.1) trainFile = open('E://ML&DL//MachineLearning//python//testSet.txt') #modelPath = 'model' lens = 0 # load data trainSet[i][0]:data,trainSet[i][1]:label for line in trainFile: data = line.strip().split('\t') # train ' ' ,testSet '/t' lens = len(data) - 1 sample_all = [] sample_data = [] for i in range(0,lens): sample_data.append(float(data[i])) sample_all.append(sample_data) # add data if int(data[lens]) == 1: sample_all.append(int(data[lens])) # add label else: sample_all.append(-1) # add label trainSet.append(sample_all) trainFile.close() # initialize w by 0 for i in range(lens): w.append(0) # train model for max 100 Iteration start = time.clock() trainMLP(500) end = time.clock() print 'train time is %f s.' % (end - start) x = np.linspace(-5,5,10) plt.figure() for i in range(len(trainSet)): if trainSet[i][1] == 1: plt.scatter(trainSet[i][0][0],trainSet[i][0][1],c=u'b') else: plt.scatter(trainSet[i][0][0],trainSet[i][0][1],c=u'r') plt.plot(x,-(w[0]*x+b)/w[1],c=u'r') plt.show() trainIter = range(len(trainLoss)) plt.figure() plt.scatter(trainIter,trainLoss,c=u'r') plt.plot(trainIter,trainLoss) plt.xlabel('Epoch') plt.ylabel('trainLoss') plt.show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
对线性可分数据集
训练过程
分类结果
迭代误差
对线性不可分数据集
训练过程
分类结果
迭代误差
感知机算法的训练过程与logistic regression大体一致,梯度下降时的每一次更新都需要将整个数据集代入训练,所以训练完的分类效果也基本一致。
感知机的对偶形式
基本思想:
将 ww 和 bb 表示为实例 xixi 和标记 yiyi线性组合的形式,通过求解其系数而求得 w、bw、b 。假设 w0,b0w0,b0为0,对误分类点(xi,yi)(xi,yi) 通过w=w+ηyixib=b+ηyiw=w+ηyixib=b+ηyi逐步修改 w,bw,b ,设修改 nn 次,则 w,bw,b 关于(xi,yi)(xi,yi) 的增量分别为 αiyixiαiyixi 和 αiyiαiyi, 其中αi=niηαi=niη ,则最后学习到的 w,bw,b可以分别表示为
w=∑i=1Nαiyixib=∑i=1Nαiyiw=∑i=1Nαiyixib=∑i=1Nαiyi这里,αi≥0,=1,2,⋯Nαi≥0,=1,2,⋯N, αi=niηαi=niη ,nini 的意义是样本 ii 被误分的次数,所以当 η=1η=1 时,表示第 ii 个样本由于被误分而更新的次数,实例点更新次数越多,表明它距离分离超平面越近,也就越难正确分类。
伪代码
输入:线性可分的数据集 T={(x1,y1),(x2,y2),⋯,(xN,yN)}T={(x1,y1),(x2,y2),⋯,(xN,yN)} ,其中 xi∈Rn,yi∈−1,+1,i=1,2,⋯,Nxi∈Rn,yi∈−1,+1,i=1,2,⋯,N,学习率 η(0<η<1)η(0<η<1);
输出:a,ba,b;感知机模型 f(x)=sign(∑Nj=1αiyjxj⋅x+b)f(x)=sign(∑j=1Nαiyjxj⋅x+b)
其中, α=(α1,α2,⋯,αN)Tα=(α1,α2,⋯,αN)T
(1) α=0,b=0α=0,b=0
(2) 在训练集中选取数据(xi,yi)(xi,yi)
(3) 如果yi(∑Nj=1αjyjxj⋅xi+b)≤0yi(∑j=1Nαjyjxj⋅xi+b)≤0αi=αi+ηb=b+ηyiαi=αi+ηb=b+ηyi
(4) 转至(2)知道没有误分类数据代码
# -*- coding: utf-8 -*- ''' @Description : dualperceptron by python @Author: Liu_Longpo @Time: Sun Dec 20 12:57:00 2015 ''' import matplotlib.pyplot as plt import numpy as np import time trainSet = [] w = [] a = [] b = 0 lens = 0 alpha = 0 Gram = [] trainLoss = [] def calInnerProduct(i, j): global lens res = 0 for p in range(lens): res += trainSet[i][0][p] * trainSet[j][0][p] return res def AddVector(vec1, vec2): retvec = [] for i in range(len(vec1)): retvec.append(vec1[i] + vec2[i]) return retvec def NumProduct(num, vec): retvec = [] for i in range(len(vec)): retvec.append(num * vec[i]) return retvec def createGram(): global lens for i in range(len(trainSet)): tmp = [] for j in range(0, len(trainSet)): tmp.append(calInnerProduct(i, j)) Gram.append(tmp) # update parameters using stochastic gradient descent def updateParm(k): global a, b, alpha a[k] += alpha b = b + alpha * trainSet[k][1] def calDistance(k): global a, b res = 0 for i in range(len(trainSet)): res += a[i] * int(trainSet[i][1]) * Gram[i][k] res += b res *= trainSet[k][1] return res def trainModel(Iter): print "training MLP..." print "-"*40 epoch = 0 for i in range(Iter): train_loss = 0 global w, a update = False print "epoch",epoch, " w: ",w,"b:",b, for j in range(len(trainSet)): res = calDistance(j) if res <= 0: train_loss += -res update = True updateParm(j) print 'train loss:',train_loss trainLoss.append(train_loss) if update: epoch = epoch+1 else: for k in range(len(trainSet)): w = AddVector(w, NumProduct(a[k] * int(trainSet[k][1]), trainSet[k][0])) print "result: w: ", w, " b: ", b update = False if epoch==Iter: print 'reach max trian epoch' for j in range(len(trainSet)): w = AddVector(w, NumProduct(a[j] * int(trainSet[j][1]), trainSet[j][0])) print "RESULT: w: ", w, " b: ", b if __name__=="__main__": ''' if len(sys.argv)!=4: print "Usage: python MLP.py trainFile modelFile" exit(0) alpha = float(sys.argv[1]) trainFile = open(sys.argv[2]) modelPath = sys.argv[3] ''' alpha = float(0.1) trainFile = open('./testSet.txt') #modelPath = 'model' lens = 0 # load data trainSet[i][0]:data,trainSet[i][1]:label for line in trainFile: data = line.strip().split('\t') lens = len(data) - 1 sample_all = [] sample_data = [] for i in range(0,lens): sample_data.append(float(data[i])) sample_all.append(sample_data) # add data if int(data[lens]) == 1: sample_all.append(int(data[lens])) # add label else: sample_all.append(-1) # add label trainSet.append(sample_all) trainFile.close() createGram() for i in range(len(trainSet)): a.append(0) for i in range(lens): w.append(0) start = time.clock() trainModel(500) end = time.clock() print 'train time is %f s' % (end - start) x = np.linspace(-5,5,10) plt.figure() for i in range(len(trainSet)): if trainSet[i][1] == 1: plt.scatter(trainSet[i][0][0],trainSet[i][0][1],c=u'b') else: plt.scatter(trainSet[i][0][0],trainSet[i][0][1],c=u'r') plt.plot(x,-(w[0]*x+b)/w[1],c=u'r') plt.show() trainIter = range(len(trainLoss)) plt.figure() plt.scatter(trainIter,trainLoss,c=u'r') plt.plot(trainIter,trainLoss) plt.xlabel('Epoch') plt.ylabel('trainLoss') plt.show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
训练过程
对比起原始形式的感知机,同样的数据集,同样的迭代次数其训练过程只需要0.08824s,而对偶形式却需要1.7484s,可见原始形式的感知机在速度上占优势,且分类性能与对偶形式基本一致。
分类效果
训练误差
对于非线性可分的数据集,感知机的算法迭代过程都会发生震荡,如上图的train loss.
代码数数据集可在 Github 上下载
-
机器学习算法原理与实践(1)、感知机算法
猜你喜欢
转载自blog.csdn.net/weixin_42029738/article/details/80415004
今日推荐
周排行