第一章感知器模型
闫涛北京动维康科技有限公司
北京{yt7589}@qq.com
Abstract
在本章中我们将向大家介绍感知器模型,感知器模型是一种最简单的神经网络,但是却是现代深度学习技术的基础。在本章中,我们先介绍感知器模型的数学原理,然后我们用 python来实现感知器模型,并以 iris数据集来介绍感知器模型的实战应用。
1 概述
神经网络的发展最初是受大脑的启发而产生的,感知器模型就直接来源于神经细胞的结构。典型的神经细胞如下图所:
大脑中的神经元由细胞体和突触组成,如上图所示,粗的部分为轴突,细的分枝称为树突,相近的神经细胞之间,树突之间的相互接近,之前的缝隙称为突触缝隙,在其中间通过神经递质来传递生物电信号。受生物神经元启发,上世纪四、五十年代,出现了感知器模型,其结构如下所示:
如上图所示,左侧的 {x1, x2, ..., xn}为输入信号,分别以权重 {w1, w2, ..., wn}接入到感知器中,感知器模型首先将输入信号与对应权重乘积进行叠加,然后再加上一个偏置值,最后经
过一个非线性的激活函数,得到最终的输出值,具体步聚如下所示:
感知器模型最常用于二分类问题,一个典型的激活函数如下所示:
下面我们设输入信号为二维信号,因为这样的话比较直观,可以通过图形的方式来直观显示,假设我们研究的是一个二分类问题,也就相当于对平面上的数据点,将其分配到不同的分类中。这里就是要找到一个超平面(这里是直线)把数据点进行正确的划分,使位于超平面一边的所有数据点属于第一个类别,位于超平面另一边的所有数据点属于第二个类别。也就是要找到下面的直线方程:
使所有第一个类别的数据点,上式的值大于零,所有第二个类别的数据点,上式的值小于零。对于某个样本点,假设其 y = 1,则若其 x1 ·w1 + x2 ·w2 + b > 0,则该点可被正确分类,若x1 ·w1 + x2 ·w2 + b ≤ 0,则该点会被错误分类;如果 y = −1,则若 x1 ·w1 + x2 ·w2 + b ≤ 0则该点可以被正确分类,若 x1 ·w1 + x2 ·w2 + b > 0则将被错误分类。由上面的分析可以看出错误分类数据点满足下式:
我们将错误分类点到超平面的距离定义为代价函数,感知器模型的任务就是使代价函数的值最小。
2 点到直线距离
由于感知器模型需要点到直线(超平面)的距离,所以在这一节中,我们将带领大家一起推导点到直线距离公式。点到直线距离公式推导方法有很多种,我们在这里采用向量法进行推导。因为深度学习中最常用的是向量和矩阵运算,所以大家要熟悉相关运算。根据鼎新教育 [2018]所述,我们假设任一点 P为 (x01, x02),直线方程为:
则该直线的方向可以用向量 v来表示:
其法向量为:
假设直线上有一点 Q(x1, x2),则向量 PQ表示为:
则 PQ向量在法向量上的投影即为点 P到直线的距离,而向量 PQ到法向量的投影为:
3 感知器模型代价函数
在y小川 [2017]中,我们将感知器模型的代价函数定义为所有错误分类的数据点到超平面的距离,根据上一节点到距离的公式,可以得到如下公式:
因为根据前面的分析,上式分子永远为正,所以可以省略绝对值符号。我们可以将上式写为向量形式:
第于第 j 个样本点,其代价函数为:
而我们要求的代价函数是所有数据点的代价函数,如下所示:
通常我们会忽略 ∥w|V ert项,所以感知器模型的代价函数如下所示:
我们的感知器模型学习算法就是要求出代价函数的极小值,根据高等数学知识,求一个多元函数的极小值,就是将函数对每个自变量求偏导数,然后另偏导数等于零,应可以求出使函数得到最小值时的极值点。在这里我们的参数为w和 b,因此我们可以得到极小值。因为在实际应用中,我们很难得到解析解,因此直接找到极值点比较困难,我们通常用梯度下降算法。代价函数对 wi 的偏导数可以表求为:
将上式改为用向量形式表示为:
同理可以推导出代价函数对 b的偏导数:
根据梯度下降算法,我们假设学习率为 α,则我们按如下公式来调整 w和 b:
以上的算法为批量学习算法,即运行完所有数据点后,统一调整权重w和偏置值 b。如果采用一个样本点调整一次的在线学习方法的话,上面的公式就变为:
如果大家可以跟着我们的推导步聚来到这里,恭喜大家,大家基本上就掌握了感知器模型基本的数学原理。在这里核心要点是记住感知器模型的代价函数是错误分类的样本点到超平面的距离,其他的都是数学推导过程,而找到一个高效的代价函数表示方法,是解决深度学习问题的关键。
4 Iris数据集实战
在理解了感知器模型的数学原理之后,我们来看感知器模型怎么应用于实战,在这里我们采用 Iris数据集来进行演示。根据百度百科 [2000]所述,Iris数据集是常用的分类实验数据集,由 Fisher于 1936年收集整理。Iris也称鸢尾花卉数据集,是一类多重变量分析的数据集。数据集包含 150个数据样本,分为 3类,每类 50个数据,每个数据包含 4个属性。可通过花萼长度,花萼宽度,花瓣长度,花瓣宽度 4个属性预测鸢尾花卉属于(Setosa,Versicolour,Virginica)三个种类中的哪一类。在这里我们为了更加直观,对这个数据集进行适当的简化,只取第 3、4个特征,而且只识别是否为 Setosa类别,我们首先来研究一下数据集的特性。
4.1 数据集解读
我们首先需要取出数据集,并且只取花瓣长度、花瓣宽度两个特征来尝试识别样本是否是Setosa类别,程序如下所示:
import numpy as npimport matplotlib.pyplot as pltimport sklearn.datasets as skdimport sklearn.linear_model as sklmclass Perceptron(object): def __init__(self): self.name = 'Perceptron' def startup(self): print('感知器类启动中...') X, y = self.load_dataset() print('X:{0}; {1}'.format(X.shape, X)) print('y:{0}; {1}'.format(y.shape, y)) self.draw_dataset(X, y) def load_dataset(self): iris = skd.load_iris() print('iris.data:{0}; {1}'.format(iris.data.shape, iris.data)) print('iris.target:{0}; {1}'.format(iris.target.shape, iris.target)) X = iris.data[:, (2, 3)] y = (iris.target == 0).astype(np.int) return X, y def draw_dataset(self, X, y): fig, ax = plt.subplots() # 绘制Setosa类别样本点 ax.scatter(X[:50, 0], X[:50, 1], marker='o', label='one: blue') # 绘制非Setosa类别样本点 ax.scatter(X[50:, 0], X[50:, 1], marker='v', label='two: green') ax.legend() plt.show()
Listing 1: 载入 IRIS数据集
• 第 18行:我们通过 sklearn.datasets.load_iris函数获取 IRIS数据集;• 第 21行:我们取数据集中所有的样本数据,但是只取第 3、4个特征,即样本集变为 150行 2列;
• 第 22行:对于标签集如果标签的类别为 0时,其取值为 True,否则为 False,即首先为由 True和 False组成具有 150个元素的一维数组,然后将数组的类型变为整数,就是将 True变为 1,将 False变为 0;
• 第 28行:以圆形标志绘制 Setosa类别;• 第 30行:以三角形标志绘制非 Setosa类别;
运行上面的程序首先打印出 iris数据集样本数据:
如上可以看出,共有 150条记录,每条记录有 4个特征,与百度百科 [2000]中描述的一致。接下来我们来看 IRIS数据集的标签集:
如上所示,IRIS标签集为有 150个元素的一维数组,由 0 2代表三个类别。接下来,我们来看我们只取花瓣长度、花瓣宽度两个特征来尝试识别样本是否是 Setosa类别所形成的数据集。样本集如下所示:
可以看出,样本集已经变为有 150条记录,每条记录有两个特征。接下来我们来看标签集:
我们将经过处理的数集绘制为散点图,两个特征分别对应两个坐标轴坐标,如下图所:
由上图可以看出,我们还是非常容易找出区分出这两个类别的超平面的。在这里我们虽然得到了数据集,但是数据集中前 50个样本都是 Setosa类,而后面 100个都是非 Setosa类,而我们希望的数据集,Setosa类和非 Setosa类应该是随机分布的,这样才便于学习算法来学习。另外,为了估计算法对未遇见的新数据的处理效果,我们需将一部分数据集的数据设置为测试数据集,这部分数据集不参与训练过程,当模型训练结束后,拿测试数据进行测试,通过这样的方法,可以较为合理的估计出算法的实际性能。最后,由于我们的算法模型通常复杂度上高于解决问题所需的复杂度,因此会出现 Overfiffing现象,就是我们的算法在训练数据集上有非常高的精度,但是在遇到新的数据点时,精度却非常低。这是因为模型只是记住了当前数据集中的内容,也没有学到背后的原理。为了避免这种情况发生,我们会在训练数据集中再取出 10∼20%的数据,作为验证数据集,当我们在训练过程中,会阶段性的求出在验证数据集上的精度,如果精度随着训练过程出现下降趋势,这时虽然在训练数据集上精度还可以提高,我们也需要终止训练过程,这就是经常所说的Early-Stopping技术。由于训练数据集、验证数据集、测试数据集的概念在今后的深度学习模型中会经常遇到,所以在这里先给大家一个简单的介绍,在后面的具体算法中,还会反复使用,所以这里只要有一个概要性认识就可以了。我们首先将数据集划分为训练数据集和测试数据集,在训练过程中,只使用训练数据集,只有最后做性能评估时才使用测试数据集,代码如下所示:
def load_dataset(self): iris = skd.load_iris() print('iris.data:{0}; {1}'.format(iris.data.shape, iris.data)) print('iris.target:{0}; {1}'.format(iris.target.shape, iris.target)) random_state = 58 X = iris.data[:, (2, 3)] y = (iris.target == 0).astype(np.int) X_df = pd.DataFrame(X) y_df = pd.DataFrame(y) X_train, X_test = skms.train_test_split(X_df, test_size=0.1, random_state=random_state) y_train, y_test = skms.train_test_split(y_df, test_size=0.1, random_state=random_state) return X_train.values, y_train.values, X_test.values, y_test.values
Listing 2: 划分训练数据集和测试数据集
运行上面程序,打印出来的训练样本集为:
如上所示,训练样本集共有 135条记录,有两个特征。训练数据集标签集如下所示:
如上所示,训练标签集共有 135条记录,值为 1时代表为 Setosa类,0为非 Setosa类。如果我们还像原来一样绘制训练样本集散点图时,会得到如下所示图形:
原来我们先画前 50个样本,这些样本均属于 Setosa类,所以点的形状一样,然后再画后 100个样本,而这些样本均属于非 Setosa类,所以其点的形状也相同。而我们经过训练样本集和测试样本集划分后,在这一过程中,有一个打散重新排序的过程,因此无论前 50个样本和后 100个样本,都会随机的包含 Setosa类和非 Setosa类的样本,所以绘制出的图形会两种形状的点混杂在一起。这正好说明我们进行了打散排序。在 IRIS数据集中,没有没有值的记录,如果有特征值缺失的记录,我们通常有以下三处处理方式:
1. 删除特征值缺失的记录;
2. 去掉该特征列;
3. 用该特征的平均值填充缺失值;
我们通常采用第三种方式,代码如下所示:
def process_missing_value(self, X): imputer = skimpute.SimpleImputer(strategy="median") imputer.fit(X) print(imputer.statistics_) return imputer.transform(X)
Listing 3: 处理缺失值
在 imputer.statistics_中保存着所有特征的平均值。
4.2 sklearn实现
下面我们来看怎么通过 sklearn来实现感知器模型,其实在实际的深度学习项目中,真正到应用算法解决问题时,就比较简单了,真正复杂的是根据数据的统计特征进行预处理、选择代价函数及合适的深度学习算法。因此这部分代码比较简单。我们首先来看训练过程:
def train(self, X, y): self.perceptron = sklm.Perceptron() self.perceptron.fit(X, y)
Listing 4: 感知器模型训练
如上所示,我们只需初始化 sklearn.linear_model.Perceptron,然后调用其 fit方法即可。在训练完成之后,接下来就是预测过程,我们以测试样本集为例来介绍,大家也可以随意构造自己的数据集。代码如下所示:
def startup(self): print('感知器类启动中...') X_train, y_train, X_test, y_test = self.load_dataset() self.draw_dataset(X_train, y_train) self.train(X_train, y_train) X_test = self.imputer.transform(X_test) X_test = self.scaler.transform(X_test) y_pred = self.perceptron.predict(X_test) print('predict:{0}'.format(y_pred)) print('target:{0}'.format(np.reshape(y_test, (y_test.shape[0]))))
Listing 5: 预测过程
• 第 6行:将样本集中缺失的数据用该特征的平均值填充,注意这里的平均值是我们在预处理过程在训练样本集中求出的平均值,千万不要用测试样本集中的平均值;
• 第 7行:将测试样本集中的数据进行标准化,公式为:
式中的 µ为均值,σ为标准差;
• 第 8行:调用 predict方法进行预测;
预测结果和实际结果如下所示:
5 结论
在本章中,我们系统学习了感知器模型,感知器模型虽然是最简单的一种神经网络,其只有一个神经元组成,但是其是当代深度学习中最常用的多层感知器模型的基础,学习这一章对后续的学习具有基础性作用。在这一章中,除了感知器模型之外,大家应该重点关注为什么及怎样进行数据预处理,怎样选择代价函数,这些所有深度学习甚至机器学习的基础概念,也是初学者最容易忽略的部分,但是在实际项目中,这部分工作的重要性绝不低于具体的深度学习算法。
参考资料
y 小川. 闲谈:感知器学习算法 (the perceptron learning algorithm). blog.csdn.net, 70210888,
2017. URL https://blog.csdn.net/blackyuanc/article/details/70210888.
百度百科. Iris 数据集. baike.baidu.com, 70210888, 2000. URL https://baike.baidu.com/item/IRIS/4061453?fr=aladdin.
鼎新教育. 点到直线距离公式的向量推导方法. 360doc.com, abs/1508.01983, 2018. URLhttp://www.360doc.com/content/18/0811/10/47813312_777396011.shtml.