版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/shankezh/article/details/78906271
简单介绍:
感知机是二分类模型,和其它二分类比起来,最大区别是输出类别是{1,-1}。算法特点:
1、感知机属于有监督学习的一种。2、感知机利用具有正负类别的示例数据训练出判别模型。
3、比起逻辑回归,简单许多。
4、具有原始形式和对偶形式。
5、单纯使用感知机,需要对数据做认知处理,因为选择的超平面方程会直接影响到训练结果,例如不能使用直线方程去拟合曲线方程更适合的数据。
6、最优化问题,常见三板斧,这里用梯度下降。
7、是神经网络和支持向量机的基础,其实就是个激活函数。
学习感知机的基本流程:
1、了解基础知识。2、了解使用了什么样的损失函数
3、推导(如果你推过LR,NN,SVM之类的,这个就显得太简单了)
4、写代码看效果
基础知识:
感知机模型:
定义假设输入空间(特征向量)为X⊆Rn,输出空间为Y={-1, +1}。输入x∈X表示实例的特征向量,对应于输入空间的点;输出y∈Y表示示例的类别。由输入空间到输出空间的函数为:f(x)=sign(w·x + b)称为感知机。其中,参数w叫做权值向量,b称为偏置。w·x表示w和x的内积。sign为符号函数,即:
几何解释
感知机模型是线性分类模型,感知机模型的假设空间是定义在特征空间中的所有线性分类模型,即函数集合{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),感知机sign(w·x+b)学习的损失函数定义为
其中M为误分类点的集合,这个损失函数就是感知机学习的经验风险函数。
显然,损失函数L(w,b)是非负的。如果没有误分类点,那么L(w,b)为0,误分类点数越少,L(w,b)值越小。一个特定的损失函数:在误分类时是参数w,b的线性函数,在正确分类时,是0.因此,给定训练数据集T,损失函数L(w,b)是w,b的连续可导函数。
如果你有心了解感知机的学习策略,你可能会看到L2范数
显然,损失函数L(w,b)是非负的。如果没有误分类点,那么L(w,b)为0,误分类点数越少,L(w,b)值越小。一个特定的损失函数:在误分类时是参数w,b的线性函数,在正确分类时,是0.因此,给定训练数据集T,损失函数L(w,b)是w,b的连续可导函数。
如果你有心了解感知机的学习策略,你可能会看到L2范数
L2范数 :
L2范数是我们最常见最常用的范数了,我们用的最多的度量距离欧氏距离就是一种L2范数,它的定义如下:
表示向量元素的平方和再开平方。
像L1范数一样,L2也可以度量两个向量间的差异,如平方差和(Sum of Squared Difference):
对于L2范数,它的优化问题如下:
L2范数通常会被用来做优化目标函数的正则化项,防止模型为了迎合训练集而过于复杂造成过拟合的情况,从而提高模型的泛化能力.
推导:
由于误差函数:
分别对w和b求导,得出结果:
所以,根据梯度下降,我们的w,b的求解方法就是随机选取一个误分类点(xi,yi)进行更新,其中η是学习速率:
程序部署步骤:
1、获取数据2、初始化参数3、定义预测函数4、定义误差函数5、模型训练函数6、画图
数据源:
本次使用到的数据是之前做逻辑回归用的,请存储为logistic_d1_s.txt即可:
-0.017612 14.053064 0
-1.395634 4.662541 1
-0.752157 6.538620 0
-1.322371 7.152853 0
0.423363 11.054677 0
0.406704 7.067335 1
0.667394 12.741452 0
-2.460150 6.866805 1
0.569411 9.548755 0
-0.026632 10.427743 0
0.850433 6.920334 1
1.347183 13.175500 0
1.176813 3.167020 1
-1.781871 9.097953 0
-0.566606 5.749003 1
0.931635 1.589505 1
-0.024205 6.151823 1
-0.036453 2.690988 1
-0.196949 0.444165 1
1.014459 5.754399 1
1.985298 3.230619 1
-1.693453 -0.557540 1
-0.576525 11.778922 0
-0.346811 -1.678730 1
-2.124484 2.672471 1
1.217916 9.597015 0
-0.733928 9.098687 0
-3.642001 -1.618087 1
0.315985 3.523953 1
1.416614 9.619232 0
-0.386323 3.989286 1
0.556921 8.294984 1
1.224863 11.587360 0
-1.347803 -2.406051 1
1.196604 4.951851 1
0.275221 9.543647 0
0.470575 9.332488 0
-1.889567 9.542662 0
-1.527893 12.150579 0
-1.185247 11.309318 0
-0.445678 3.297303 1
1.042222 6.105155 1
-0.618787 10.320986 0
1.152083 0.548467 1
0.828534 2.676045 1
-1.237728 10.549033 0
-0.683565 -2.166125 1
0.229456 5.921938 1
-0.959885 11.555336 0
0.492911 10.993324 0
0.184992 8.721488 0
-0.355715 10.325976 0
-0.397822 8.058397 0
0.824839 13.730343 0
1.507278 5.027866 1
0.099671 6.835839 1
-0.344008 10.717485 0
1.785928 7.718645 1
-0.918801 11.560217 0
-0.364009 4.747300 1
-0.841722 4.119083 1
0.490426 1.960539 1
-0.007194 9.075792 0
0.356107 12.447863 0
0.342578 12.281162 0
-0.810823 -1.466018 1
2.530777 6.476801 1
1.296683 11.607559 0
0.475487 12.040035 0
-0.783277 11.009725 0
0.074798 11.023650 0
-1.337472 0.468339 1
-0.102781 13.763651 0
-0.147324 2.874846 1
0.518389 9.887035 0
1.015399 7.571882 0
-1.658086 -0.027255 1
1.319944 2.171228 1
2.056216 5.019981 1
-0.851633 4.375691 1
-1.510047 6.061992 0
-1.076637 -3.181888 1
1.821096 10.283990 0
3.010150 8.401766 1
-1.099458 1.688274 1
-0.834872 -1.733869 1
-0.846637 3.849075 1
1.400102 12.628781 0
1.752842 5.468166 1
0.078557 0.059736 1
0.089392 -0.715300 1
1.825662 12.693808 0
0.197445 9.744638 0
0.126117 0.922311 1
-0.679797 1.220530 1
0.677983 2.556666 1
0.761349 10.693862 0
-2.168791 0.143632 1
1.388610 9.341997 0
0.317029 14.739025 0
代码部分:
由于感知机太过简单,而且不小心写成了一个类,就一次粘贴全部代码:
#!/usr/bin/env python
# encoding: utf-8
"""
@version: v1.0
@author: Jason Zhu
@license: Apache Licence
@file: 感知机.py
@time: 2017-12-19 18:57
"""
import numpy as np
from matplotlib import pyplot as plt
class Perceptron():
#权重
weight = []
#截距
b = []
#损失值
loss = []
#样本集合和结果
train_x = []
train_y = []
#学习速率
learning_rate = 0.1
#错误分类点
err_classify_point = []
def get_data(self):
x = []
y = []
fileIn = open('logistic_d1_s.txt')
# print(fileIn.readline().strip().split(' | | | |\r\n'))
for line in fileIn.readlines():
linearr = line.strip().split()
x.append([float(linearr[0]), float(linearr[1])])
if 0. == float(linearr[2]):
y.append(-1)
else:
y.append(float(linearr[2]))
self.train_x = np.array(x)
self.train_y = np.mat(y).T
# 定义符号函数,z = wT * xT + b
def sign(self,z):
npy = []
for i in range(len(z)):
if z[i][0] >= 0:
npy.append([1])
else:
npy.append([-1])
return npy
def set_param(self):
m, n = np.shape(self.train_x)
self.weight = np.zeros((n,1)) #初始化权重矩阵
self.b = 0
def predict(self,x):
return self.sign(x)
def loss_func(self):
# z = np.dot(self.train_x, self.weight) + self.b
# self.loss = -self.train_y.T * (z)
loss_sum = 0.0
for i in self.err_classify_point:
z = self.weight[0,0] * self.train_x[i,0] + self.weight[1,0] * self.train_x[i,1] + self.b[0,0]
loss_sum = loss_sum + self.train_y[i] * z
self.loss.append(- loss_sum)
# print( "损失为:" + str( loss_sum) )
def train(self,iterMax = 100):
# print("go")
# print(np.shape(self.weight))
for i in range(iterMax):
#z = wx + b
z = np.dot(self.train_x,self.weight) + self.b
predict_y = self.sign(z)
err_predict_index = []
self.err_classify_point = []
#找到所有误分类点的序号
for m in range(np.size(self.train_y)):
if self.train_y[m] != predict_y[m]:
err_predict_index.append(m)
self.err_classify_point.append(m)
#随机选择一个误分类点
err_point = np.random.randint(0,len(err_predict_index))
# print((self.train_y[err_predict_index[err_point]]),predict_y[err_predict_index[err_point]])
#对误分类点进行梯度下降,np.mat(self.train_x[err_predict_index[err_point]])需要将其格式化成1x2
aLw = -np.dot(np.mat(self.train_x[err_predict_index[err_point]]).T,self.train_y[err_predict_index[err_point]])
aLb = -self.train_y[err_predict_index[err_point]]
self.weight = self.weight - self.learning_rate * aLw
self.b = self.b - self.learning_rate * aLb
# print("weight:" + str(self.weight) + "\n bias:" + str(self.b))
self.loss_func()
def show_pic(self):
#根据结果绘制点集
plt.figure(1)
for i in range(len(self.train_x)):
if self.train_y[i][0] == 1:
plt.plot(self.train_x[i,0], self.train_x[i,1], color='red', marker='*')
else:
plt.plot(self.train_x[i, 0], self.train_x[i, 1], color='blue', marker='*')
x1 = np.arange(-3,3,0.1).tolist()
print(self.err_classify_point)
#查看上一次误分类点
# for t in self.err_classify_point:
# plt.plot(self.train_x[t,0],self.train_x[t,1],color='brown',marker='o')
x2 = []
for m in range(len(x1)):
xx2 = -(self.weight[0,0] *x1[m] + self.b[0,0] ) / self.weight[1,0]
x2.append(xx2)
plt.plot(x1, x2, color='black')
plt.show()
if __name__ == "__main__":
np.set_printoptions(suppress=True) # 不使用科学计数法显示结果
m = Perceptron()
m.get_data()
m.set_param()
m.train(2000)
print("初次损失为:" + str(m.loss[0]))
print("最终损失为:" + str(m.loss[-1]))
print("权重w" + str(m.weight) + "\n"
+"截距b" + str(m.b) + "\n"
)
print("上一次误分类点数量:" + str( len(m.err_classify_point)))
m.show_pic()
运行之后,看一下结果:
结果如图:
写在最后:
写代码的时候也参考了很多blog,如果细心的人也会发现,梯度下降的代码会有区别,原因大多是选择的损失函数不一样,就我目前已经见到过用平方差误差函数,和最大似然误差函数两种了,我这里选用经验风险函数,原因是因为参考了<统计学习方法>李航这本书,关于误差函数的选择,有网上有专门的讲解,不过一般选择最推荐的,毕竟都是前辈推出来的经验结论。
参考链接:
【2】<<统计学习方法>> 作者:李航