注意:本文所提的神经网络在这里特质多层前馈神经网络
神经网络的numpy实现代码:Neural Network with Numpy
1. 神经网络的基础结构
一个简单的神经网络基础结构包括三个,线性映射,激活层,隐藏层。
如图,输入层的输入向量经过一个线性映射,在经过一个激活层,到达了第一个隐藏层,随着网络的加深,重复线性映射,激活层,隐藏层的过程直至到达输出层。流程很简单,但是需要理解几个问题。
- 线性映射是什么?
假设如图所示,输入向量为四维向量,则经过图中的权重矩阵便是一次线性映射,即 - 2. 为什么要有激活层?
如果不添加激活层,则一个多层的网络则可以看做是多个线性映射的复合,即上一层网络的输出作为下一层的输入,假设第一层线性映射函数为,则第二层, 则最后的输出为。可以看到,多层的线性映射可以用单层的线性映射表示,失去了拟合非线性的能力。所以我们需要对每层线性映射的输出加入非线性的激活函数。
我们以矩阵的运算形式表示,对于 层:
经过线性映射:
经过激活层:
2. 优化
神经网络并没有解析解,我们需要借助梯度下降的方式进行优化。那么,我们如何求每层的梯度呢?
2.1 反向传播算法(Backpropagation)(觉得太麻烦可以直接看2.1.3)
2.1.1 传统方法
反向传播的基础是链式法则,这里就不多介绍。
现在有一个多层的神经网络,我们第 层为例,看一下他的前向传播。
来自上一层的输入经过线性映射的第i个输出:
然后经过激活函数f(x),然后得到l+1层的第i个输出:
根据链式法则,便可以很容易求得:
其中很明显与激活函数的导数有关。
所以呢,反向传播算法可以理解为利用第l+1层传递过来的梯度,求解Loss对第l层层参数(W,b)的梯度,并传递给l-1层。
2.1.2 矩阵运算
由上面的计算不难看出,实际上神经网络的计算都是矩阵运算,我们从矩阵的角度来理解反向传播的计算会更加容易一些。
不同于标量运算,矩阵运算不满足交换律(), 且矩阵必须形状匹配。
维数相容原则:若一个矩阵x经过f(x)变换为标量y,则必定也是一个大小矩阵。
所以我们的对矩阵求导时,可以先把矩阵看做标量正常求导,然后调整结果使得结果满足维数相容原则和矩阵相乘配型。
每一层的前向传播为:
设
2.1.3 以Loss对具体某个w的梯度为例
损失函数为均方误差:
Net:的输入输出为sigmoid:
可以看到,梯度跟中间的权重 (w5),激活函数() ,还有损失函数()有关
激活函数的导数最大为0.25,又因为通常我们的w初始化通常小于1,所以
2.2 梯度消失与激活函数
由2.1我们知道,实际上反向传播算法便是利用前一层的梯度求本层的梯度,并传递给下一层。当网络层数很深,且每层梯度都小于1的时候,反向传播的越深,随着梯度的累乘,浅层的网络梯度会变得非常小,这边是梯度消失;同理当梯度大于1,便会出现梯度爆炸的问题。
由2.1.1可以知道,每一层的梯度实际上跟激活函数有着很大的关系,所以我们可以通过改变激活函数,控制激活函数的导数大小,从而一定程度上解决梯度消失的问题。
2.2.1 常见的激活函数
- Sigmoid函数
函数表达式为
导数为
特点:
作为以前很常用的激活函数之一,sigmoid的特点便是可以将输入值统一压缩在[0,1]的值域内,对于极大的正值,经过sigmoid后值为1,极小的的负值,经过sigmoid后为0,这一性质使得它在“门”结构中得到了广泛使用(例如LSTM中的门),同样,也使得它很容易与概率对应起来,在二分类问题在也很常用。
缺点:
- 由sigmoid的导数也可以看到,每过一层,梯度就会变成原来的0.25倍甚至更小,这很容易导致梯度消失问题
- 经过sigmoid的输出并不是0均值的输出。意思是单样本训练中,经过sigmoid到下一层的输入x>0或者x<0,这使得f(x)=wx+b求w局部梯度要么都为正,要么都为负,也就意味着此次所有的w要么全往正向更新,要么全往负向更新,降低了训练速度。当然,按batch进行训练一定程度上可以解决这个问题。
- sigmoid的解析解中的幂运算对于计算机来说求解比较麻烦。
- tanh函数
函数表达式为
导数为
特点几乎与sigmoid相同,不同点在于将sigmoid的值域拉伸到了[-1,1],解决了sigmoid的输出不以0均值问题,但幂运算和梯度消失的问题仍未解决。
- ReLu
- Relu特点很明显, 对于负值梯度一律取0,对于正值梯度一律为1,所以不存在梯度消失的问题,但是同时会带来梯度死亡问题,即一旦某个神经元的想x<0, 梯度便为0,从此该神经元参数便永远不会被更新。当然,我们可以通过合理的初始化和学习率减少这样的概率。
同时,Relu的计算简单,只需要判断阈值即可,计算速度很快。并且x<0时的输出为0,达到了类似于dropout的产生稀疏性的效果。
唯一的问题与sigmoid类似,输出不是以为0为均值中心,但是它的优点实在太明显,所以至今为止仍然被广泛使用。 - 其他激活函数
还有一些激活函数是针对relu的改进,比如PReLu,ELU, 理论上效果好于Relu,但实际应用中并未发现如此。如果感兴趣的朋友可以自行查阅。
2.3 其他解决梯度消失与梯度爆炸的方法
梯度爆炸
- 梯度剪切/截断: 设置梯度最大值的阈值
- 正则化
梯度消失
- Batch Normolization(BN):将每一层的输出规范化为0均值1方差的正态分布,使得激活函数的输入值落在更敏感的区域,从而使得损失函数变化更大,梯度更大,一定程度上避免梯度消失,而且加快训练收敛速度。
- 残差结构:
- LSTM