深度学习初步,全连接神经网络,MLP从原理到实现(一)原理部分,反向传播详细解释和实际计算例子
其他两篇:
深度学习初步,全连接神经网络,MLP从原理到实现(二)原理部分,过拟合,**函数,batchsize和epochs,训练DL模型的建议
深度学习初步,全连接神经网络,MLP从原理到实现(三)实现部分,用java实现MLP
下面的内容主要是李宏毅老师的机器学习课程,加上自己的整理和补充,文章有点长,有一定的ml基础,认真读就能搞懂,数学部分建议自己手写推导。如果对反向传播部分没有理解,强烈建议多听几遍李宏毅老师机器学习中的bp部分。b站就有资源。
1.多层全连接神经网络
如图是一个多层全连接神经网络,input层是输入层,Layer[1]到Layer[L-1]是隐层,Layer[L]是输出层,层与层之前每两两个单元(神经元或者输出/输入单元)之间存在连接,代表着一个权重,单个神经元干什么事呢?
前一层的输出作为当前层的输入,用权重与输入之间的乘积和+偏置的值Z作为到**函数的输入,**函数的值输出到下一层,这里Z的计算和回归中没有区别,如果**函数是sigmoid就成了logistic回归。
变量命名约定和矩阵化运算:
所有变量上标代表所处的神经网络的第几层
表示L-1层神经元j到l层中神经元i连接的权重
将l-1和l之间的权重排列成矩阵:
这样看矩阵中行的大小就是第L层的神经元个数,矩阵的列就是第L-1层的神经元个数,矩阵中的一行表示了l层中神经元i所连接的l-1层的所有权重。
考虑偏置:
表示第l层第i个神经元和上一层l-1的偏置,这样l层的所有偏置值构成一个向量。
前面提到Z值是输入和权重的乘积和加上偏置。
这里用表示这个值,这也是**函数的输入值
那么l层第i个神经元的z值可以表示为:
这样,l层所有神经元Z值可以矩阵化运算
将输入到**函数,其输出值将作为l+1层的输入
矩阵运算可以并行,所以可以使用gpu加速。
问题:
(1)将多个线性层叠在一起意味着什么?为什么效果好?
单层线性模型表达不够强,我认为一定程度上参数越多对数据拟合越好,越能深层次挖掘数据的特征,这就好像CRF和HMM的对比,CRF参数更多,效果往往也更好,而CRF这些特征组合都是人为定义的。多层神经网络中可以把中间的隐层认为是一个Feature extractor(特征提取器),不必像CRF那样去自己定义特征,实际上要定义好的有效的特征是很难的,比如面对音频,图像这种数据定义特征就很麻烦,使用深度学习的方法比传统机器学习方法就好得多。自然语言人是能够理解的,所以在nlp中人为定义的特征就可以达到比较好的效果。
(2)深度学习的“深”
举例:ImgNet比赛的神经网络深度
可以看到随着层数越多错误率不断下降,一定是这样吗?为什么往宽度走,也就是降低层次,提升单层的神经元的数量
(3).仅仅是多层全连接的神经网络就需要调整很多参数。多少层?每层神经元多少个?使用什么**函数?优化算法?代价函数?
2.多层全连接神经网络中的反向传播
损失函数有很多均方误差,交叉熵损失函数等等。
把所有样本的损失值加起来为总的损失,参数即为神经网络中的权重,将总的损失优化到最小即为最终的权重参数,优化使用梯度下降,只不过可能是各种梯度下降的变形。
如何计算用于更新参数的偏微分 是一个重要的问题。
补充:链式求导法则
要计算 这里L是总的损失,这里求导举例只举例单个样本对参数求导,总的直接所有样本加起来就行,所以以l代表单个样本的损失值。
反向传播:
为了使用梯度下降,需要求出梯度,也就是损失函数L关于权重w的一阶导数。设有N个样本,那么:
因为多个样本和单个样本求梯度没什么差别,所以下面的例子以求单个样本的损失函数(以l表示)对w的导数为例。类似的,专注损失l对某个特定的w的导数而不是所有w,因为其他w的求法都可以类似得到。
对于这样一个简单的nn结构:
关注左上角的局部部分:
损失函数l是在NN的输出端形成的,要求出l对w1的导数,就要利用开始提到的链式求导法则。
Z的值直接影响到了 ,所以链式求导从这里开始。
因为:
很容易直接得到就是x1,因为这里是输入层后的隐层,所以是x1,如果不是,直观的后面任意层的
就是前一层的输入a
没有
这么容易计算,因为z还影响了下一层的输入a,而a又进一步影响后面层的w,这种影响一直延续下去直到输出层。
仔细想想这里的Z就类似上图种的s,只不过NN结构中,Z影响到的变量要比图中的s更多。
接着,继续对 展开,Z紧接着送到了**函数σ中,产生了a。所以:
就是**函数的导数,写成
。
继续写出 。
根据链式法则:
所以:
如果z'和z''不是输出层,那么这个过程还有继续进行下去。我们把这个过程看作逆向的:
我们将求 这个过程反过来看:
比如:
我们要求 ,那么可以分为2个步骤,
前向传播求出
反向传播求
就是上一层的输出,本层的输入
而 需要从输出层开始计算。
有了 那么前面的
就可以反向回去。
比如:
这样从后往前计算就可以算出所有的 ,最后将
和
乘起来就可以得到
。(注意这里的思维要想成所有的w)
下面举一个实际的例子:
NN结构如图,规定**函数使用sigmoid,损失函数使用交叉熵,输出层[y1,y2]使用softmax。输入单个样本x1,x2=1,2,y=[1,0](one-hot编码表示类别是y1)。
补充:softmax和交叉熵输出层的求导。
参考:https://blog.****.net/qian99/article/details/78046329
最后一层softmax做**函数,那么输出a的表达式为:
z就是上一层的输入和权重的乘积求和再加上b
**函数σ(z):
损失函数:
这里l是单个样本,求和是因为多个类别,比如总共有3个类别,如果某个样本的真实类别是2,那么标签是一个向量[0 1 0],遍历就是要遍历这个向量, 就是指不同类别的真实值(这里就只有y2=1,其他为0),
就是经过softmax后不同类别的概率分布。
要求的是:
、
这里 代表单个样本的损失,如果是多个样本只需要求和:
因为 直接影响了a的值,而因为softmax的特效,所有a的分母部分都包含了
。
影响了所有a的值:
根据链式法则:
先来计算 ,因为
,只对
求偏导,所以其他
均当作常数项。那么:
再计算 ,需要分情况讨论,因为j=i时,a的分子部分含有
。
当i=j:
当i≠j时:
将上面2种情况组合起来:
所以最后:
有了上面的例子开始计算:
输如样本x=[1,2],y=[1,0],权重初始如图,偏置b都为0.1
(1)先进行一次前向传播,计算所有的神经元输出输出a:
(2)反向传播计算梯度
符号说明:
表示第l(不包含输入层)层第i(从上往下数)个单元产生的z值
表示第l层第x个单元与第l-1层第y个单元之间连接的权重。
计算 :
第3层:
第2层:
第1层:
有了 ,计算
就很简单了:比如:
(3)向量化/矩阵化运算
但是这样串行的思想不利用计算,需要把上面的计算过程向量化矩阵化。
1)a的计算
其他层的计算类似,输入x变为输入上一层的输出a
2)
其他情况类似推广。
3)
其他情况类似推广。
可以计算梯度后就可以更新参数了。