如今深度学习如火如荼,各种工具和平台都已经非常完善。各大训练平台比如 TensorFlow 让我们可以更多的聚焦在网络定义部分,而不需要纠结求导和 Layer 的内部组成。本系列我们来回顾一下深度学习的各个基础环节,包括线性回归,BP 算法的推导,卷积核和 Pooling,循环神经网络,LSTM 的 Memory Block 组成等,全文五篇,前三篇是斯坦福深度学习 wiki 整理,第四第五两篇是 Alex Graves 的论文读书笔记,图片来自网络和论文,有版权问题请联系我们。
多层神经网络之前我们讨论的线性回归和逻辑回归都是对 进行线性关系建模的,如果我们希望对其进行非线性关系建模,神经网络是一个方法,而且它可以对比较复杂的非线性关系进行建模。
我们先来看神经网络最基本的单元“神经单元”的定义:
上面的例子中,有一个激活函数,我们一般选择一个 sigmod 函数作为f的实现,比如 logistic 函数:,当然我们也可以使用tanh函数,或者 Rectifier 激活函数(使用 Rectifier 作为激活函数的 Unit 叫做 Rectified Linear Units, ReLu ):,或者其他一些 Rectifier 的变种。三种函数的输入对比如下图所示:
tanh(z) 可以看做是 sigmoid 的拉伸版本,他的值域是 (-1,1),而 logistic 是 (0,1) 。ReLU 则是当 Z 大于 0 时斜率为 1 的线性函数,小于 0 时始终输出 0 。
三种不同的激活函数在进行梯度求导的时候也是各不相同。Logistic 梯度是,tanh函数的梯度是
Rectifier的梯度在 Z 大于 0 的时候是 1 ,其他为 0 。
理论上 Rectifier 在 0 点是不可导的,但是由于我们训练神经网络的时候用很大量的数据,梯度更新的时候是所有 instance 的梯度求和,一处 z=0 不至于影响整个梯度下降的过程,所以我们把 z=0 时的梯度也定义为 0 。
另外一个需要注意的是,我们把 bias(也叫截距)这一项单独写出来了,定义为公式中的 b ,而不是同一使用来定义。
神经网络模型我们把很多的神经元串联起来,下一层的输入是上一层的输出,从而组成一个小型的网络,比如下图:
最左边一层我们叫输入层(input layer),最右边一层叫做输出层(output layer),中间部分都叫做隐藏层(hidden layer)。定义一些变量:
是神经网络的层数,比如这里是3
是第 l 层网络,比如是输入层,
是输出层是神经网络的参数,表示连接第l层的节点 j 与连接第 l+1 层的节点 i 之间的边的权重(注意下标和节点对应关系,因为 W 与 x 相乘时是转置过的)。
是连接到l+1层第i个节点的 bias
是第l层网络的节点个数(不包含 bias 节点)
是第l层第i个节点的输出值(activation),对于l=1的输入层
对于输入层,我们也统一到了这个参数上,如果引入一个新的定义 z ,上式可以简化为:
我们把这个 l -> l+1 逐层计算输出的过程叫做前馈(forward propagation)。通过 blas 等向量和矩阵运算库,我们可以很方便的把前馈过程表达为向量矩阵的线性代数运算,从而提高计算速度。
神经网络可以定义很多中间层,从而使得网络很 deep(这也是 DNN 的来源),同时输出层还可以定义多个输出节点来预测多个值,比如下面的网络示意图:
上面这个网络中,。这种网络可以专门用来对多分类问题进行建模。
可以看到,如果不存在输入层,直接输入对输出,如果输出层节点为 1,且激活函数为 logistic ,其实就是逻辑回归。如果输出层节点多于 1 ,且激活函数为 softmax ,就是 softmax regression 。
BP算法 backpropagation Algorithm我们使用最小均方误差来定义代价函数,对于单条记录有:
对于所有的m条训练数据合并计算代价函数,可以得到如下的代价函数:
其中第一部分定义的是误差平方和的平均值,第二项是正则化因子,也叫权值衰减 (weight decay),专门用来惩罚数值太大的权值,防止训练过程中发生过拟合。正则化因子控制前后两项的相对权重。
上面的损失函数既可以用于分类问题,也可以用于回归问题。分类问题的时候认为 y=0 或者 1 ,回归问题的时候需要先把 y 值归一化到 [0,1] 然后进行训练。
现在目标转化为求一组值使得 的值最小化。虽然 是一个非凸函数,使用梯度下降的最优化求解可能会收敛到局部最优,而非全局最优,但是实际应用中,我们这么做并没用太大的问题。
需要注意的是, 一定要使用随机值来进行初始化,而不能统一设置为 0 或者其他的一模一样的数,因为这样的话会使得隐藏层每个节点的连接参数都是一样的,这样计算出来后隐藏层节点的结果也都是一样的,然后逐层向前计算,最后的结果就是 x 原封不动的传输到了最后一层。这会让训练程序直接停止,因为无法计算梯度。这个随机初始化的过程,目的就是消除对称性。
根据之前使用梯度下降法更新参数的经验,我们知道现在我们需要用 对分别求偏导,用于更新参数。按照严格的数学求解应该是这样的:
为了能更方便的求解,我们都使用 BP 算法。关于 BP 算法先说一个不太好理解的版本:首先对每一个训练样例 (x, y) 先进行前向计算得到最后一层的激活输出 (activations) ,然后对隐藏层的每一层 l 的每个节点 i ,依次计算一个误差项,用于评估最后一层的输出误差中有多少是这个节点产生的。再说一个我自己理解的版本:如果网络并没有隐藏层,输入直接对接到输出,那么就是普通的逻辑回归或者 softmax 回归的求偏导更新过程;现在网络有很多隐藏层,前向计算过程中,是输入一层层传递过来的,而传递过程中与连接权重矩阵相乘的过程,本质上是一次次的空间变换(矩阵乘法就是两侧空间的 transfer 过程,可降维,可升维,可旋转缩放,可非线性变换等等各种),那么现在最后一层的误差来了,我们应该干什么呢?应该通过矩阵的另外一端把误差重新进行空间变换映射回去,并层层传递到第一个隐藏层。后面我们对照公式再具体讲一下。
对于输出层的 可以通过计算输出与真实值之间的最小二乘的导数来得到,而对于隐藏层,我们可以认为是所有使用 作为输入的下一层的节点的误差项进行加权求和(反向的矩阵空间变换)的结果。
BP 算法描述如下:
1. 先使用前向计算,一直计算得到输出层的结果;
2. 对于输出层的每个节点 i ,计算 :
3. 对所有的隐藏层,l= nl-1, nl-2, …, 3, 2,给每个节点 i 计算:
4. 使用 计算偏导项:
注意以上的乘 操作都是按位操作的,而且 W 和我们同样可以使用矩阵和向量运算来加速。假设 是 sigmoid 函数,根据前面的推导我们可以直接得到,这样可以直接代入上面的公式中使用。
使用 mini-batch 来进行数据的批量计算,我们可以把上面过程使用矩阵形式来表达,梯度下降的算法过程如下:
1. 对于所有层 l ,设置梯度
注意:是矩阵,是向量,他们是用来存储梯度值的。与用来存储权重的 W 和 b 维度是一样的。
2. 对于所有训练样例 i=1,2,…,m
a. 使用 BP 算法计算梯度
,。
注意:这里的 也是矩阵方式的表达,比如输出层,隐藏层 。这里的 就是把下一层误差反向映射回来的过程,那为什么还有 这一部分呢?因为我们要计算的实际上参数的梯度下降方向。
b. 更新W和b的梯度
3. 更新参数 W 和 b :
如此一直迭代训练,便可以不断减小代价函数 ,直到模型收敛或者达到一定的训练迭代次数。
softmax regression输出层使用 softmax regression 作为输出层时,代价函数求导得到的误差项表表达应该如下式,后面的误差传递过程保持不变:
常见的激活函数求导
通过上面的内容,我们知道多层神经网络的 BP 算法求解,大体的公式都是固定的模式化的,有一个可变的地方就是激活函数求导 。一般常见的三种激活函数: logistic , tanh , Rectifier ,我们可以简单的把它们记录下来以便直接使用。
注意:以下部分都用进行了简化,实际上应该是对 W 求偏导,需要多补充一部分函数内求导的部分。
Rectifier:for z<=0; for z>0
logistic:
tanh: