神经元
先来认识下真正的神经元。
图 1: 典型神经元的结构(来自维基百科 “树突” 词条)
神经元大致可以分为树突、突触、细胞体和轴突。树突为神经元的输入通道,其功能是将其他神经元的动作电位传递至细胞体。其他神经元的动作电位借由位于树突分支上的多个突触传递至树突上。神经细胞可以视为有两种状态的机器,激活时为 “是”,不激活时为 “否”。神经细胞的状态取决于从其他神经细胞接收到的信号量,以及突触的性质(抑制或加强)。当信号量超过某个阈值时,细胞体就会被激活,产生电脉冲。电脉冲沿着轴突并通过突触传递到其它神经元。(内容来自维基百科 “感知机” 词条,稍作整理)
我们的视觉系统非常复杂。大致来讲,当光线投射到视网膜后,视觉膜上的光感受细胞的突触直接与双极细胞相连,双极细胞突触则与最外层的节细胞相连,节细胞将动作电位传递到大脑。我们的视网膜有大约 1 亿 3 千万个光感受细胞接收光信号,然后通过大约 120 万个节细胞轴突将信息从视网膜传递到大脑。(内容来自维基百科 “视觉系统” 词条)
视觉系统使我们有了视知觉能力,将外部世界的二维投射重构为三维世界。
人类的视觉系统可以毫不费力的识别出下面图片中的数字(当然还有文字)。但如果让计算机来识别,就会明显感觉到视觉识别的困难。
图 2: 街道地址(来源于维基百科 “Handwriting recognition” 词条)
我们不能简单地把 “数字 2 像一只水面上的天鹅,数字 7 像一把锄头” 这样的规则教给计算机。因此,我们需要把手写识别的问题表述成计算机可以理解的方式。
图 3: 水面上的天鹅像极了数字 2
假设我们有 5x5 像素的图片来显示每个数字,例如数字 2 表示如下:
@@@@@
. . . . . @
@@@@@
@. . . . .
@@@@@
由于我们的神经网络是以数字组成向量作为输入的,所以我们将每个图片转换成长度为 25 的向量,其元素的值是 1(“这个像素位于该图像中”)或 0(“这个像素不在该图像中”)
例如,数字 2 可以表示为:
two_digit=[1,1,1,1,1
0,0,0,0,1
1,1,1,1,1
1,0,0,0,0
1,1,1,1,1]
我们希望神经网络最终的输出是一个具体的数字,因此我们需要 10 个不同的输出结果。例如,对于数字 2,正确的输出结果将是:
[0,0,1,0,0,0,0,0,0,0]
图 4: 使用 python 列表解析式建立一个代表 0-9 不同数字的列表
其中 targets[2] 就是输出结果数字 2。
那么,神经网络是如何把信息的输入转换成我们想要的输出的呢?
感知器(Perceptron)
感知器是一种二元分类器,它是神经网络的基石。感知器是对神经细胞的模拟,如权量(突触)、偏置(阈值)及激活函数(细胞体)。
输入以向量的形式表示 x=(x_0, x_1, x_2),你可以把它们理解为不同的特征维度,其中的 x_0 是偏置单元(bias unit), 相当于线性回归中的常数项。在经过 “神经元”(激活函数)的计算后,感知器会输出一个大于 0 或小于 0 的数。箭头上的数字代表每个特征的权量(weights),相当于线性回归模型的参数,是收集信息的神经突触。
例如,上图列出了每个特征的权量,于是我们有这样的模型:
hθ(x)=-3+2*x_1+2*x_2
当 x_1 和 x_2 同时为 1 时,输出为 1,而不同时都将得到负数。这实际上就是逻辑与或称为与门。
我们的感知器将根据输出大于或小于 0 来对输入进行分类:
这是一个单层的感知器。通过修改权量,我们也可以建立或门(weights=[2,2], bias=-1)、非门(weights=[-2],bias=1,只有一个输入端)。但是,无论如何,你都无法通过一个单层的感知器来建立 “异或门”(XOR),即两个输入相同时输出 1,否则输出 0。这种情况下,我们就需要更复杂的神经网络了,即多层神经网络,也被称为前馈神经网络(feedforward neural network)。
前馈神经网络
前馈神经网络是多个感知器的组合,这些感知器以不同的方式产生连接,并由不同的激活函数控制激活。
图 6: 前馈神经网络示意图
我们来认识下前馈神经网络:
它包括输入层(input layer)、输出层(output layer)和一个或多个隐藏层(hidden layers)。上图的神经网络由 3 个单元的输入层,4 个单元的隐藏层和 2 个单元的输出层组成。单元等于感知器。
输入层的单元是隐藏层单元的输入,隐藏层单元的输出是输出层单元的输入。
两个感知器之间的连接有一个权量。
第 t 层的每个感知器与第 t-1 层的每个感知器相互关联。当然,你也可以设置权量为 0,从而在实质上取消连接。
在加工输入数据时,你将输入数据赋予输入层的每个单元,而隐藏层的每个单元是输入层每个单元的加权求和。也就是说,输入层的数据会被前向传播到隐藏层的每个单元。同理,隐藏层的输出作为输入会前向传播到输入层,计算得到最后的输出,即神经网络的输出。
多个隐藏层的神经网络同理。
超越线性
在 “感知器” 中我们谈到,单层感知器虽然可以建立与门、或门、非门等,但无法建立更为复杂的异或门(XOR),即两个输入相同时输出 1,否则输出 0。
为了更为直观地理解这个问题,我们可以看下图:
图 7: 双输入感知器的决策空间
模型有两个输入(input1 和 input2),我们可以线性地区分或门的两类情况:即同时为 0 时在左下角,其它情况在右上角;与门的情况也可以线性地区分,即输出同时为 1 时在右上角,其它情况在左下角。但异或门呢?这种情况是无法作出线性区分的,也就是说,单层感知器无法实现异或门。
多层感知器吗?
先来分析输入和输出的情况。最左边两列列出了输入的 4 种情况。异或门的输出是最右边一列的情况,即两个输入相同时输出 1,否则为 0。我们在输入层和输出层之间加入两个单元的隐藏层,那么,它给输出层的输入应该是什么呢?答案如下图。你可以发现,对于隐藏层的 a_1 单元(上标 2 代表这是第 2 层)来说,它实际上是且门(都为 1 时才输出 1);对 a_2 单元来说,它的逻辑是 (not x_1) and (not x_2),即同时为 0 时才输出 1。而从隐藏层到输出层,是逻辑或。前馈神经网络可以实现异或门!
图 8: 异或门输入输出下推导隐藏层
于是我们建立如下的神经网络,但是其输出并非我们想要。为什么?
图 9: 线性激活函数下的前馈神经网络
这是因为上面感知器的激活函数是线性函数。这种情况下,神经网络的输出也只是输入的某种线性函数,只不过是通过网络的形式来进行加权。线性函数的线性组合仍然是线性函数。也就是说,即便是多层的感知器,激活函数为线性时也无法实现输入 00 和输入 11 时的输出比输入 01 和 10 时大,即非线性。
如果激活函数是线性函数,前馈神经网络将不会比单层感知器强大多少,不管它有多少层。
我们需要 logistic 函数的帮助。大部分神经网络会使用非线性激活函数,比如 logistic 函数(也被称为 Sigmoid 函数,这是就其形状而言的)。此外,由于训练神经网络要使用微积分,而要使用微积分,就得使用光滑函数(在反向传播时我们会详细讲解)。logistic 函数也满足这一要求。
在 logistic 函数的作用下,线性函数的输出值将被转化到 0 到 1 之间。从下图右上角的 logistc 曲线可以看到,输入值越大,输出越接近 1,输入为 4 时输出为 0.99;输入值越小,输出越接近 0,输入值为 - 4 时,输出为 0.01。
下图是单层感知器的激活函数为 logitic 函数时与门的示例。
图 10: 激活函数为 logitic 函数时的与门单层感知器
综上,我们可以通过前馈神经网络和 logistic 函数解决异或门的问题:
图 11: 非线性神经网络实现异或门
反向传播(backpropagation):训练神经网络
反向传播是使用数据来训练神经网络的算法,它是神经网络的梯度下降算法。
假设我们有一个训练集,其中含有输入向量和相应的目标输出向量。同时,假定我们的网络已经拥有一组权量(相当于我们知道每个神经元的激活函数),那么接下来,我们就需要使用以下算法来调整这些权量。
1、利用初始权量,在输入向量上运行前向传播,从而得到所有网络所有神经元的输出。
2、这样,每个输出层神经元都会得到一个误差,即输出值与实际值之差。
3、计算作为神经元权量的函数的误差的梯度,然后根据误差降低最快的方向调整权量。
4、将这些输出误差反向传播给隐藏层以便计算相应误差。
5、计算这些误差的梯度,并利用同样的方式调整隐藏层的权量。
不断迭代,直到网络收敛。
要理解上述过程,梯度下降算法是关键。
图 12: 单个参数的损失函数示例
在梯度下降算法中,我们需要损失函数(即上面提到的作为神经元权量的函数的误差)。损失函数衡量了模型的误差,即神经网络的输出值与实际值之间的差异,它的参数是模型的参数,即连接的权量。当损失函数达到全局最低时,得到最优的权权量。在训练阶段,权量不断被更新,慢慢接近全局最低值。例如,权量是 0.6 时,它就需要向 0.4 变化,这样才能接近全局最低值。
上图描述的是只有一个参数的情况,即模型的误差取决于一个参数。然而,神经网络的误差取决于每一个权量,它的损失函数也会复杂得多。
其中,i 代表第几层,k 代表第 i 层的第几个单元。可见,损失函数把整个神经网络的误差都加和了。后面的第二部分是正则化项,暂时不用理睬,不影响后面的理解。
有人可能会问,为什么神经网络的损失函数是上面的形式?这个问题等价于为什么 logistic 函数采用上面的形式?
在回归模型中,损失函数是平方误差函数(最小二乘法):
损失函数衡量了模型的输出与实际值之间的误差:
但对 logistic 函数而言,平方误差函数是一个非凸函数,利用梯度下降算法,它将无法保证找到损失函数的全局最优解。
图 13: 非凸函数
对 logistic 函数,我们需要其他的损失函数:
图 14:
图 15:
如果 y=1,而 h(x)=1 的话,则 cost=0,即图 14 曲线上(1,0)的位置;如果 h(x)=0,则 cost 函数输出值趋向于无穷大,这意味着当我们认为某种情况不会发生,例如用户不会成为付费用户,而事实上是可能的时,这种损失是不可估量的。同理,如果 y=0,而 h(x)=0 的话,则 cost=0,即图 15 曲线上(0,0)的位置;如果 h(x)=1,则 cost 趋向于无穷大。
可以把两种情况写在一个表达式中:
也即:
神经网络衡量整个网络的误差,所以在形式上看起来会复杂些,但实质是一样的。
那么,接下来我们要如何更新权量才能使得损失函数接近全局最低值呢?
在神经网络中,我们使用反向传播算法来更新神经元之间连接的权量。反向传播的意思是指,我们从输出层开始计算,计算输出层与实际值的误差,然后返回到前面的隐藏层计算其误差,以此类推,不断迭代,直到网络收敛。
在完成第一步的前向传播后,我们会得到神经网络的输出值,利用损失函数可以计算出输出值与实际值之间的误差。损失函数的偏导数可以告诉我们参数往哪个方向更新可以得到更小的损失函数值。导数的几何意义为曲线的斜率,例如,对于图 12 简单的损失函数而言,参数在 0.5 时斜率为正,参数值需要减少才能得到更小的损失函数值;而如果参数值为 0.1,斜率为负,参数值需要增加才能得到更小的损失函数值。导数可以告诉我们全局最低的方向!
为了得到最小的 J(θ),我们需要不断更新参数θ,即神经网络连接的权量。每次更新的变化值由下面的公式决定:
其中, 为损失函数的偏导数,前面为负是因为斜率为正时,我们需要减少参数的值才能 “下山”,而斜率为负时,我们需要增加参数的值才能 “下山”, 代表学习速率,代表下山的步子有多大,如果步子太小,要走很远才能走到山底,如果步子太大,容易越过谷底,导致无法收敛;J(θ) 为神经元输出的误差。
从隐藏层到输出层的两个神经元之间的权量一旦更新,就可以更新隐藏层神经元的输出,从而得到隐藏层神经元新的误差值(更新后的输出值和之前前向传播时得到的输出值),进而根据上述公式更新隐藏层和上一层两个神经元之间的权量。依此类推,直到更新完整个神经网络的权量。
利用损失函数继续更新整个网络的权量,直到神经网络输出值的误差达到最小。
所有神经元之间的权量确定后,神经网络就完成训练了。
综上,让机器人看到 2 绝非易事。
参考资料:
Machine Learning from Coursera by Andrew Ng.
Data Science from Scratch by Joel Grus.
A Deep Learning Tutorial: From Perceptrons to Deep Networks BY IVAN VASILEV.
维基百科
====================================分割线================================
本文作者:AI研习社
本文转自雷锋网禁止二次转载,原文链接
相关资源:七夕情人节表白HTML源码(两款)