TensorFlow深度学习实战(37)——深度学习的数学原理
TensorFlow深度学习实战(37)——深度学习的数学原理
- 0. 前言
- 1. 反向传播历史
- 2. 微积分相关概念
- 2.1 向量
- 2.2 导数和梯度
- 2.3 梯度下降
- 2.4 链式法则
- 2.5 常用求导公式
- 2.6 矩阵运算
- 3. 激活函数
- 4. 反向传播
- 4.1 前向计算
- 4.2 反向传播
- 5. 交叉熵及其导数
- 6. 批量梯度下降、随机梯度下降和小批量梯度下降
- 6.1 批量梯度下降
- 6.2 随机梯度下降
- 6.3 小批量梯度下降
- 7. 反向传播和卷积神经网络
- 8. 反向传播和循环神经网络
- 9. TensorFlow 和自动微分
- 小结
- 系列链接
0. 前言
在本节中,我们将探讨深度学习背后的数学原理,以了解神经网络的内部工作机制。
1. 反向传播历史
连续反向传播的由 Henry J. Kelley
于 1960
年提出,使用动态规划实现。Stuart Dreyfus
于 1962
年提出了链式法则。Paul Werbos
首次使用反向传播进行神经网络研究,但直到 1986
年,反向传播才获得成功。1987
年,Yann LeCun
描述了当前用于训练神经网络的反向传播。
随机梯度下降 (Stochastic Gradient Descent
, SGD
) 的基本原理由 Robbins
和 Monro
于 1951
年提出,但并非针对神经网络。反向传播提出的 52
年后,AlexNet
才在 ImageNet 2012
挑战中实现了优异的深度学习模型,至此深度学习才开始受到广泛的关注。神经网络的创新并非一蹴而就,而是经历了超过 50
年的漫长历程。
2. 微积分相关概念
在介绍反向传播之前,首先回顾微积分的相关概念。
2.1 向量
向量实际上是一个数字列表。给定一个向量,可以将其解释为空间中的具有大小和方向的量。通常将向量写作列向量 xxx 或行向量 xTx^TxT。给定两个列向量 uuu 和 vvv,可以计算它们的点积 u⋅v=uTv=∑iuiviu\cdot v =u^Tv=\sum_iu_iv_iu⋅v=uTv=∑iuivi,可以证明 u⋅v=∣∣u∣∣⋅∣∣v∣∣cos(θ)u\cdot v=||u||\cdot||v||cos(\theta)u⋅v=∣∣u∣∣⋅∣∣v∣∣cos(θ),其中 θ\thetaθ 是两个向量之间的角度。
2.2 导数和梯度
导数是一个强大的数学工具,使用导数和梯度能够优化神经网络。函数 y=f(x)y = f(x)y=f(x) 的导数是衡量变量 xxx 变化时,函数 yyy 值变化速率的度量。
如果 xxx 和y是实数,将 fff 的图形绘制在坐标轴上,则导数是图形在每一点的斜率 (slope
)。
如果函数是线性函数 y=ax+by=ax+by=ax+b,斜率是 a=ΔyΔxa=\frac{\Delta y}{\Delta x}a=ΔxΔy,使用微积分进行推导:
y+Δy=f(x+Δx)=a(x+Δx)+b=ax+aΔx+b=y+aΔxΔy=aΔxa=ΔyΔxy+\Delta y=f(x+\Delta x)=a(x+\Delta x)+b=ax+a\Delta x+b=y+a\Delta x\\ \Delta y=a\Delta x\\ a=\frac{\Delta y}{\Delta x} y+Δy=f(x+Δx)=a(x+Δx)+b=ax+aΔx+b=y+aΔxΔy=aΔxa=ΔxΔy
下图展示了 Δx\Delta xΔx、Δy\Delta yΔy 和线性函数与 xxx 轴之间的角度 θ\thetaθ 的几何意义:
如果函数不是线性的,那么计算变化速率就是数学上差分比值 ΔyΔx\frac{\Delta y}{\Delta x}ΔxΔy 的极限,当 Δx\Delta xΔx 变得无限小时,斜率就是切线,如下图所示:
例如,函数 f(x)=x2f(x) = x^2f(x)=x2 和其导数 f′(x)=2xf'(x) = 2xf′(x)=2x,在给定点 x=2x = 2x=2 时,导数为 f′(2)=4f'(2) = 4f′(2)=4。
梯度是对多变量的导数的推广,单变量函数的导数是标量值函数,而多个变量函数的梯度是向量值函数。梯度用符号 ∇\nabla∇ 表示,假设 x∈Rmx \in \mathbb{R}^mx∈Rm (例如,具有 mmm 维的实数空间),且 fff 从 Rn\mathbb{R}^nRn 映射到 R\mathbb{R}R,梯度定义如下:
∇(f)=(∂f∂x1,…,∂f∂xm)\nabla(f)=(\frac{\partial f}{\partial x_1},\dots,\frac{\partial f}{\partial x_m}) ∇(f)=(∂x1∂f,…,∂xm∂f)
在数学中,多变量函数的偏导数 ∂f∂xi\frac{\partial f}{\partial x_i}∂xi∂f 是对其中一个变量的导数,而其他变量保持不变。需要注意的是,梯度是一个向量:
- 梯度指向函数增加最快的方向
- 在局部最大值或局部最小值处,梯度为
0
,这是因为如果梯度为0
,函数就无法进一步增加或减少
2.3 梯度下降
梯度下降算法的关键思想是,如果梯度指向函数增加最快的方向,那么通过沿着梯度的相反方向移动,如下图所示:
2.4 链式法则
链式法则用于求一个复合函数的导数,如果有一个函数 y=g(x)y = g(x)y=g(x),且 z=f(y)z = f(y)z=f(y),那么函数 zzz 对 xxx 的导数可以定义为:
dzdx=dzdydydx\frac {dz}{dx}=\frac{dz}{dy}\frac{dy}{dx} dxdz=dydzdxdy
链式法则可以推广到标量以外的情况。假设 x∈Rmx \in \mathbb{R}^mx∈Rm 和 y∈Rny \in \mathbb{R}^ny∈Rn 以及 ggg 是一个从 Rm\mathbb{R}^mRm 映射到 Rn\mathbb{R}^nRn 的函数,fff 是一个从 Rn\mathbb{R}^nRn 映射到 R\mathbb{R}R 的函数。设 y=g(x)y = g(x)y=g(x),z=f(y)z = f(y)z=f(y),可以推导出:
∂z∂xi=∑j∂z∂yj∂yj∂xi\frac {\partial z}{\partial x_i}=\sum_j\frac {\partial z}{\partial y_j}\frac {\partial y_j}{\partial x_i} ∂xi∂z=j∑∂yj∂z∂xi∂yj
偏导数的广义链式法则是处理多变量函数的反向传播算法的基本工具。
2.5 常用求导公式
常数求导:c′=0c' = 0c′=0,其中 ccc 是常数。
变量求导:当对变量求导时,x′=1x'=1x′=1。
线性求导:[af(x)+bg(x)]′=a⋅f′(x)+bg′(x)[af(x) + bg(x)]' = a \cdot f'(x)+bg'(x)[af(x)+bg(x)]′=a⋅f′(x)+bg′(x)。
倒数求导:[1f(x)]′=−f′(x)f(x)2\left[\frac{1}{f(x)}\right]' = -\frac{f'(x)}{f(x)^2}[f(x)1]′=−f(x)2f′(x)。
指数求导:[f(x)n]′=n⋅f(x)n−1[f(x)^n]'= n \cdot f(x)^{n-1}[f(x)n]′=n⋅f(x)n−1。
2.6 矩阵运算
大小为 m×nm\times nm×n 的矩阵 WWW 可以用来表示网络权重 wijw_{ij}wij,其中 0≤i≤m0 \leq i \leq m0≤i≤m 和 0≤j≤n0 \leq j \leq n0≤j≤n,这些权重用于表示与两个相邻网络层之间的连接。通过调整权重,可以控制网络的学习过程,并且特定权重 wijw_{ij}wij 发生微小变化时,会根据网络的拓扑结构在整个网络中传播。
我们已经回顾了微积分的基本概念,接下来,将其应用于深度学习,首先从激活函数开始。
3. 激活函数
常用的激活函数包括 sigmoid
、tanh
和 ReLU
,接下来,我们计算这些激活函数的导数。
sigmoid
函数定义为:σ(z)=11+e−z\sigma(z) = \frac{1}{1 + e^{-z}}σ(z)=1+e−z1。导数计算过程如下:
σ′(z)=ddz(11+e−z)=1(1+e−z)−2ddz(e−z)=e−z1+e−z11+e−z=e−z+1−11+e−z11+e−z=(1+e−z1+e−z−11+e−z)11+e−z=(1−σ(z))σ(z)\begin{align*} \sigma'(z)&=\frac d{dz}(\frac 1{1 + e^{-z}})=\frac 1{(1 + e^{-z})^{-2}}\frac d{dz}(e^{-z})=\frac {e^{-z}}{1 + e^{-z}}\frac 1{1 + e^{-z}}\\ &=\frac {e^{-z}+1-1}{1 + e^{-z}}\frac 1{1 + e^{-z}}=(\frac {1 + e^{-z}}{1 + e^{-z}}-\frac 1{1 + e^{-z}})\frac 1{1 + e^{-z}}\\ &=(1-\sigma(z))\sigma(z) \end{align*} σ′(z)=dzd(1+e−z1)=(1+e−z)−21dzd(e−z)=1+e−ze−z1+e−z1=1+e−ze−z+1−11+e−z1=(1+e−z1+e−z−1+e−z1)1+e−z1=(1−σ(z))σ(z)
tanh
函数定义为:tanh(z)=ez−e−zez+e−z\tanh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}}tanh(z)=ez+e−zez−e−z。根据 ddzez=ez\frac{d}{dz} e^z = e^zdzdez=ez 和 ddze−z=−e−z\frac{d}{dz} e^{-z} = -e^{-z}dzde−z=−e−z,则导数计算如下:
tanh′(z)=(ez+e−z)(ez+e−z)−(ez−e−z)(ez−e−z)(ez+e−z)2=1−(ez−e−z)2(ez+e−z)2=1−tanh2(z)tanh'(z)=\frac{(e^z+e^{-z})(e^z+e^{-z})-(e^z-e^{-z})(e^z-e^{-z})}{(e^z+e^{-z})^2}=1-\frac{(e^z-e^{-z})^2}{(e^z+e^{-z})^2}=1-tanh^2(z) tanh′(z)=(ez+e−z)2(ez+e−z)(ez+e−z)−(ez−e−z)(ez−e−z)=1−(ez+e−z)2(ez−e−z)2=1−tanh2(z)
ReLU
函数定义为:f(x)=max(0,x)f(x) = \max(0, x)f(x)=max(0,x)。ReLU
的导数为:
f′(x)={1,ifx>00,otherwisef'(x)= \left\{ \begin{aligned} 1, & &if\ \ x>0 \\ 0, & &otherwise \end{aligned} \right. f′(x)={1,0,if x>0otherwise
需要注意的是,ReLU
在零点处不可导,在零点处的导数值可以任意选择为 0
或 1
。
4. 反向传播
反向传播 (Backpropagation
) 算法是深度学习的数学核心。一个神经网络可以有多个隐藏层,以及一个输入层和一个输出层。可以把反向传播理解为一种逐步纠正误差的方法,一旦检测到误差,就立即进行修正。为了减少神经网络的误差,必须对网络进行训练。训练网络(监督学习)需要一个包括输入值和相应真实输出值的数据集,我们希望网络预测尽可能接近真实输出值的结果。反向传播算法的关键是根据输出神经元的测量误差更新连接的权重。
网络开始训练时,所有权重都会被分配随机值。然后,网络对训练集中的每个输入进行计算,输入值从输入层传播到隐藏层,最后到输出层,得到预测结果:
由于训练集中包含对应的真实输出值,因此可以根据预测值和真实值计算预测的误差。反向传播的最简单方法是将误差向后传播,使用合适的优化算法(如梯度下降),调整神经网络的权重,目标是减少误差:
从输入到输出的前向计算和误差的反向传播过程会重复多次,直到误差低于预定义的阈值。整个过程如下图所示表示。神经网络根据数据集输入,生成预测结果,将预测值与(真实)标签进行比较,优化器通过更新模型的权重来最小化损失函数:
接下来,我们详细介绍前向计算和反向传播过程的实现。
4.1 前向计算
在前向计算过程中,输入值与权重相乘,然后将所有结果相加,接着应用激活函数。在每一网络层重复进行以上过程。第一层接收输入,并生成其输出,然后,每个后续层将前一层的输出作为输入:
在每一层中,通常有两个函数:
- 转换函数:z=∑wixi+bz = \sum w_i x_i + bz=∑wixi+b,其中 xix_ixi 是输入值,wiw_iwi 是权重,bbb 是偏置,用向量可以表示为 z=wTxz = w^T xz=wTx。偏置 bbb$ 可以通过设置 w0=bw_0 = bw0=b 和 x0=1x_0 = 1x0=1 隐含在求和步骤中
- 激活函数: y=σ(z)y = \sigma(z)y=σ(z),其中 σ\sigmaσ 是激活函数
人工神经网络由一个输入层、一个输出层以及位于输入层和输出层之间的一个或多个隐藏层组成。为了简单起见,假设只有一个隐藏层,结果可以很容易地推广到包含多个隐藏层的情况。
如上图所示,来自输入层的 xix_ixi 与连接输入层和隐藏层的一组全连接权重 wijw_{ij}wij 相乘,然后与偏置一起求和 zj=∑iwixi+bjz_j = \sum_i w_{i} x_i + b_jzj=∑iwixi+bj。结果通过激活函数 yj=σj(zj)y_j = \sigma_j(z_j)yj=σj(zj) 传递,然后从隐藏层传递到输出层。
简单总结,在前向计算过程中,需要执行以下操作:
- 对每一层的每个神经元,将每个输入值乘以其对应的权重
- 然后,将步骤1中得到的结果求和并加上偏置
- 最后,对每个神经元,应用激活函数计算输出值
在前向计算结束时,根据在输入层提供的输入向量 xxx,在输出层得到预测向量 yoy_oyo。接下来,利用反向传播调整模型参数使预测向量 yoy_oyo 与真实值向量 tot_oto 更接近。
4.2 反向传播
为了确定预测向量 yoy_oyo 与真实值向量 tot_oto 的接近程度,需要一个函数来衡量输出层的误差,即损失函数。损失函数的选择取决于实际问题。以均方差为例:
E=12∑(yo−to)2E=\frac 12\sum (y_o-t_o)^2 E=21∑(yo−to)2
需要注意的是,EEE 是一个二次方函数,因此,当 tot_oto 与 yoy_oyo 相差较远时,差值的二次方会更大,而与差值的符号无关。
在训练过程中,我们希望调整网络的权重以最小化最终误差。如前所述,我们可以通过沿梯度的相反方向 (−∇w-\nabla w−∇w) 移动来逼近局部最小值,沿梯度的相反方向移动的算法称为梯度下降 (gradient descent
)。因此,可以定义权重 wijw_{ij}wij 更新方程:
wij←wij−∇wijw_{ij}\leftarrow w_{ij}-\nabla w_{ij} wij←wij−∇wij
对于多变量函数,梯度是通过偏导数计算的。引入超参数 η\etaη ,在机器学习中称为学习率,来确定在与梯度相反的方向上移动的步长。
求损失对权重的导数:
∇w=−η∂E∂wij\nabla w=-\eta \frac{\partial E}{\partial w_{ij}} ∇w=−η∂wij∂E
上式表示一个微小的变化如何影响最终误差。
定义 zjz_jzj 是层 lll 中节点 jjj 的输入。δj\delta_jδj 是层 lll 中节点 jjj 的激活函数(应用于 zjz_jzj),y=δj(zj)y=\delta_j(z_j)y=δj(zj) 是层lll 中节点 jjj 的激活输出,wijw_{ij}wij 是将层 lll 中神经元 iii 连接到层 l−1l-1l−1 中神经元 jjj 的权重矩阵,bjb_jbj 是层 lll 中单元 jjj 的偏置,yoy_oyo 是输出层中节点的目标值。
接下来,需要计算在权重的变化量为 ∂wij\partial w_{ij}∂wij 时,输出层误差的偏导数 ∂E\partial E∂E,可以分为两种不同的情况:
- 情况
1
:从隐藏层(或输入层)到输出层的神经元的权重更新方程 - 情况
2
:从隐藏层(或输入层)到隐藏层的神经元的权重更新方程
4.2.1 情况 1:从隐藏层到输出层
在这种情况下,需要考虑从隐藏层 jjj 到输出层 ooo 的神经元的方程。应用误差 EEE 的定义并求导:
∂E∂wjo=∂12∑o(yo−to)2∂wjo=(yo−to)∂(yo−to)∂wjo\frac {\partial E}{\partial w_{jo}}=\frac{\partial\frac 12\sum_o (y_o-t_o)^2}{\partial w_{jo}}=(y_o-t_o)\frac {\partial(y_o-t_o)}{\partial w_{jo}} ∂wjo∂E=∂wjo∂21∑o(yo−to)2=(yo−to)∂wjo∂(yo−to)
这里,求和项消失是因为当我们对第 jjj 维度进行偏导数时,误差中唯一不为零的项是第 jjj 项。考虑到求导结果是线性运算,并且 ∂to∂wjo=0\frac{\partial t_o}{\partial w_{jo}} = 0∂wjo∂to=0,因为真实值 tot_oto 不依赖于 wjow_{jo}wjo,则有:
∂(yo−to)∂wjo=∂yo∂wjo−0\frac {\partial(y_o-t_o)}{\partial w_{jo}}=\frac {\partial y_o}{\partial w_{jo}}-0 ∂wjo∂(yo−to)=∂wjo∂yo−0
应用链式法则,可以得到:
∂E∂wjo=(yo−to)∂yo∂wjo=(yo−to)∂δo(zo)∂wjo=(yo−to)δo′(zo)∂zo∂wjo\frac {\partial E}{\partial w_{jo}}=(y_o-t_o)\frac {\partial y_o}{\partial w_{jo}}=(y_o-t_o)\frac {\partial \delta_o(z_o)}{\partial w_{jo}}=(y_o-t_o)\delta'_o(z_o)\frac {\partial z_o}{\partial w_{jo}} ∂wjo∂E=(yo−to)∂wjo∂yo=(yo−to)∂wjo∂δo(zo)=(yo−to)δo′(zo)∂wjo∂zo
其中,zo=∑jwjoδj(zj)z_o=\sum_jw_{jo}\delta_j(z_j)zo=∑jwjoδj(zj),∂zo∂wjo=δj(zj)\frac{\partial z_o}{\partial w_{jo}} = \delta_j(z_j)∂wjo∂zo=δj(zj),由于当我们对第 jjj 个维度进行偏导数时,误差中唯一不为零的项就是第 jjj 项。根据定义,δj(zj)=yj\delta_j({z_j})=y_jδj(zj)=yj,因此:
∂E∂wjo=(yo−to)δo′(zo)yj\frac {\partial E}{\partial w_{jo}}=(y_o-t_o)\delta'_o(z_o)y_j ∂wjo∂E=(yo−to)δo′(zo)yj
因此,误差 EEE 关于从隐藏层 jjj 到输出层 ooo 的权重 wjw_jwj 的梯度,简单来说是三个项的乘积,预测值 yoy_oyo 和实际值 tot_oto 之间的差异、输出层激活函数的导数 δo′(zo)\delta'_o(z_o)δo′(zo) 以及隐藏层节点 jjj 的激活输出 yjy_jyj。为了简化,可以定义 vo=(yo−to)δo′(zo)v_o=(y_o-t_o)\delta'_o(z_o)vo=(yo−to)δo′(zo),得到:
∂E∂wjo=voyj\frac {\partial E}{\partial w_{jo}}=v_oy_j ∂wjo∂E=voyj
简而言之,对于情况 1
,每个隐藏-输出连接的权重更新方程为:
wjo←wjo−η∂E∂wjow_{jo}\leftarrow w_{jo}-\eta\frac {\partial E}{\partial w_{jo}} wjo←wjo−η∂wjo∂E
如果想明确计算关于输出层偏置的梯度:
∂zo∂bo=∂∑jwjoδj(zj)+bo∂bo=1\frac {\partial z_o} {\partial b_o}=\frac{\partial\sum_jw_{jo}\delta_j(z_j)+b_o}{\partial b_o}=1 ∂bo∂zo=∂bo∂∑jwjoδj(zj)+bo=1
因此,在这种情况下,∂E∂bo=vo\frac {\partial E} {\partial b_o}=v_o∂bo∂E=vo。
4.2.2 情况 2:从隐藏层到隐藏层
在这种情况下,需要考虑从隐藏层(或输入层)到隐藏层的神经元方程。隐藏层权重变化与输出误差之间存在间接关系,使得梯度计算变得有些复杂。在这种情况下,我们需要考虑从隐藏层 iii 到隐藏层 jjj 的神经元方程。应用 EEE 的定义并求导,得到:
∂E∂wij=∂12∑o(yo−to)2∂wij=∑o(yo−to)∂(yo−to)∂wij=∑o(yo−to)∂yo∂wij\frac {\partial E} {\partial w_{ij}}=\frac{\partial\frac 12\sum_o (y_o-t_o)^2}{\partial w_{ij}}=\sum_o(y_o-t_o)\frac {\partial(y_o-t_o)}{\partial w_{ij}}=\sum_o(y_o-t_o)\frac {\partial y_o}{\partial w_{ij}} ∂wij∂E=∂wij∂21∑o(yo−to)2=o∑(yo−to)∂wij∂(yo−to)=o∑(yo−to)∂wij∂yo
在这种情况下,求和不会消失,因为隐藏层的权重变化直接影响输出。代入 yo=δo(zo)y_o = \delta_o(z_o)yo=δo(zo) 并应用链式法则,得到:
∂E∂wij=∑o(yo−to)∂δo(zo)∂wij=∑o(yo−to)δo′(zo)∂zo∂wij\frac {\partial E} {\partial w_{ij}}=\sum_o(y_o-t_o)\frac {\partial \delta_o(z_o)}{\partial w_{ij}}=\sum_o(y_o-t_o)\delta'_o(z_o)\frac {\partial z_o}{\partial w_{ij}} ∂wij∂E=o∑(yo−to)∂wij∂δo(zo)=o∑(yo−to)δo′(zo)∂wij∂zo
zoz_ozo 与内部权重 wijw_{ij}wij 之间的间接关系可以表示为:
zo=∑jwjoδj(zj)+bo=∑jwjoδj(∑iwijzi+bi)+boz_o=\sum_jw_{jo}\delta_j(z_j)+b_o=\sum_jw_{jo}\delta_j(\sum_iw_{ij}z_i+b_i)+b_o zo=j∑wjoδj(zj)+bo=j∑wjoδj(i∑wijzi+bi)+bo
由于 zj=∑wijzi+biz_j = \sum w_{ij} z_i + b_izj=∑wijzi+bi,需要再次应用链式法则:
∂zo∂wij=∂zo∂yi∂yi∂wij=∂yjwjo∂yi∂yi∂wij=wjo∂yi∂wij\frac {\partial z_o} {\partial w_{ij}}=\frac {\partial z_o} {\partial y_i}\frac {\partial y_i} {\partial w_{ij}}=\frac {\partial y_jw_{jo}} {\partial y_i}\frac {\partial y_i} {\partial w_{ij}}=w_{jo}\frac {\partial y_i} {\partial w_{ij}} ∂wij∂zo=∂yi∂zo∂wij∂yi=∂yi∂yjwjo∂wij∂yi=wjo∂wij∂yi
代入 yj=δj(zj)y_j = \delta_j(z_j)yj=δj(zj),应用链式法则:
wjo∂yi∂wij=wjo∂δj(zj)∂wij=wjoδj′(zj)∂zj∂wijw_{jo}\frac {\partial y_i} {\partial w_{ij}}=w_{jo}\frac {\partial \delta_j(z_j)} {\partial w_{ij}}=w_{jo}\delta'_j(z_j)\frac {\partial z_j} {\partial w_{ij}} wjo∂wij∂yi=wjo∂wij∂δj(zj)=wjoδj′(zj)∂wij∂zj
代入 zj=∑yiwij+biz_j = \sum y_i w_{ij}+b_izj=∑yiwij+bi:
wjoδj′(zj)∂zj∂wij=wjoδj′(zj)∂(∑iyiwij+bi)∂wij=wjoδj′(zj)yiw_{jo}\delta'_j(z_j)\frac {\partial z_j} {\partial w_{ij}}=w_{jo}\delta'_j(z_j)\frac {\partial (\sum_iy_iw_{ij}+b_i)} {\partial w_{ij}}=w_{jo}\delta'_j(z_j)y_i wjoδj′(zj)∂wij∂zj=wjoδj′(zj)∂wij∂(∑iyiwij+bi)=wjoδj′(zj)yi
联合以上结果:
∂E∂wij=∑o(yo−to)δo′(zo)wjoδj′(zj)yi=yiδj′(zj)∑o(yo−to)δo′(zo)wjo\frac {\partial E} {\partial w_{ij}}=\sum_o(y_o-t_o)\delta'_o(z_o)w_{jo}\delta'_j(z_j)y_i=y_i\delta'_j(z_j)\sum_o(y_o-t_o)\delta'_o(z_o)w_{jo} ∂wij∂E=o∑(yo−to)δo′(zo)wjoδj′(zj)yi=yiδj′(zj)o∑(yo−to)δo′(zo)wjo
由于 vo=(yo−to)δo′(zo)v_o = (y_o - t_o) \delta'_o(z_o)vo=(yo−to)δo′(zo),得到:
∂E∂wij=yiδj′(zj)∑ovowjo\frac {\partial E} {\partial w_{ij}}=y_i\delta'_j(z_j)\sum_o v_o w_{jo} ∂wij∂E=yiδj′(zj)o∑vowjo
代入 vov_ovo,用于将后续层计算出的信号 vov_ovo 进行反向传播。损失相对于权重 wijw_{ij}wij 的变化率 ∂E∂wij\frac{\partial E}{\partial w_{ij}}∂wij∂E 是三个因子的乘积,来自下层的输出激活 yiy_iyi、隐藏层激活函数的导数 δ′j\delta'jδ′j 和之前计算的后续层的反向传播信号 vov_ovo 乘以 wjow_{jo}wjo。可以通过定义 vj=δ′j(zj)∑ovowjov_j = \delta'j(z_j) \sum_o v_o w{jo}vj=δ′j(zj)∑ovowjo 来使用反向传播误差信号的思想,因此:∂E∂wij=yivj\frac{\partial E}{\partial w_{ij}} = y_i v_j∂wij∂E=yivj。这表明,为了计算深度神经网络中任意层 lll 的梯度,可以简单地将反向传播的误差信号 vjv_jvj 与到达层 lll 的前馈信号 yl−1y_{l-1}yl−1 相乘。虽然计算过程有些复杂,但结果非常简单。给定一个函数 z=f(x,y)z=f(x,y)z=f(x,y),局部计算到神经元的输入 xxx 和 yyy,梯度 ∂L∂z\frac{\partial L}{\partial z}∂z∂L 进行反向传播。然后,通过链式法则将这些梯度与局部梯度 ∂z∂x\frac{\partial z}{\partial x}∂x∂z 和 ∂z∂y\frac{\partial z}{\partial y}∂y∂z 结合进行进一步反向传播。其中,LLL 表示来自前一层的误差。
损失相对于输出层偏置的梯度为 ∂E∂bi=vj\frac{\partial E}{\partial b_i} = v_j∂bi∂E=vj。
简而言之,对于情况 2(隐藏到隐藏的连接),权重的增量是 Δw=ηvjyi\Delta w=\eta v_jy_iΔw=ηvjyi,每个隐藏层到隐藏层连接的权重更新方程为:
wij←wij−η∂E∂wijw_{ij}\leftarrow w_{ij}-\eta\frac{\partial E}{\partial w_{ij}} wij←wij−η∂wij∂E
反向传播的本质就是逐层应用权重更新规则,从最后的输出层开始,向前推进到第一个输入层。虽然推导起来比较困难,但应用起来就非常简单。深度学习核心的前向-反向算法如下:
- 计算从输入到输出的前向结果
- 基于预测 yoy_oyo 和真实值 tot_oto 计算输出误差 EEE
- 反向传播误差,将其与前一层的权重以及相关激活函数的梯度相乘
- 根据反向传播的误差和来自输入的前向结果计算所有参数 θ\thetaθ 的梯度 ∂E∂θ\frac{\partial E}{\partial \theta}∂θ∂E
- 使用计算出的梯度 θ←θ−η∂E∂θ\theta\leftarrow\theta-\eta\frac{\partial E}{\partial \theta}θ←θ−η∂θ∂E 更新参数
需要注意的是,以上算法要求误差函数 EEE 和激活函数 δl\delta_lδl 都必须是可微分的。
尽管梯度下降和反向传播不能保证找到损失函数的全局最小值,但通常能够找到局部最小值。
5. 交叉熵及其导数
采用交叉熵作为损失函数时,逻辑损失函数定义为:
E=L(c,p)=−∑i[ciln(pi)+(1−ci)ln(1−pi)]E=L(c,p)=-\sum_i[c_iln(p_i)+(1-c_i)ln(1-p_i)] E=L(c,p)=−i∑[ciln(pi)+(1−ci)ln(1−pi)]
其中 ccc 表示独热编码的类别(或标签),而 ppp 代表经过 softmax
处理的概率。由于交叉熵应用于经过 softmax
处理的概率和独热编码的类别,考虑使用链式法则来计算损失相对于最终权重 scoreiscore_iscorei 的梯度:
∂E∂scorei=∂E∂pi∂pi∂scorei\frac{\partial E}{\partial score_i}=\frac{\partial E}{\partial p_i}\frac{\partial p_i}{\partial score_i} ∂scorei∂E=∂pi∂E∂scorei∂pi
从 ∂E∂pi\frac{\partial E}{\partial p_i}∂pi∂E 开始,需要注意的是,对于固定的 ∂pi{\partial p_i}∂pi,求和中的所有项都是常数,只有选择的那一项是变化的:
∂E∂pi=∂(−∑i[ciln(pi)+(1−ci)ln(1−pi)])∂pi=∂(−[ciln(pi)+(1−ci)ln(1−pi)])∂pi\frac{\partial E}{\partial p_i}=\frac{\partial(-\sum_i[c_iln(p_i)+(1-c_i)ln(1-p_i)])}{\partial p_i}=\frac{\partial(-[c_iln(p_i)+(1-c_i)ln(1-p_i)])}{\partial p_i} ∂pi∂E=∂pi∂(−∑i[ciln(pi)+(1−ci)ln(1−pi)])=∂pi∂(−[ciln(pi)+(1−ci)ln(1−pi)])
对求和应用偏导数,且由于 ln′(x)=1x\ln'(x) = \frac{1}{x}ln′(x)=x1:
−∂ciln(pi)∂pi−∂(1−ci)ln(1−pi)∂pi=−cipi−(1−ci)(1−pi)∂(1−pi)∂pi-\frac{\partial c_iln(p_i)}{\partial p_i}-\frac {\partial(1-c_i)ln(1-p_i)}{\partial p_i}=-\frac{c_i}{p_i}-\frac {(1-c_i)}{(1-p_i)}\frac {\partial (1-p_i)}{\partial p_i} −∂pi∂ciln(pi)−∂pi∂(1−ci)ln(1−pi)=−pici−(1−pi)(1−ci)∂pi∂(1−pi)
可得:
∂E∂pi=−cipi+(1−ci)(1−pi)\frac{\partial E}{\partial p_i}=-\frac{c_i}{p_i}+\frac {(1-c_i)}{(1-p_i)} ∂pi∂E=−pici+(1−pi)(1−ci)
接下来,计算 ∂pi∂scorei\frac{\partial p_i}{\partial score_i}∂scorei∂pi,其中 pip_ipi 定义为:
σ(xj)=exj∑iexi\sigma(x_j)=\frac{e^{x_j}}{\sum_ie^{x_i}} σ(xj)=∑iexiexj
求导:
∂σ(xj)∂xk={σ(xj)(1−σ(xj)),ifj=k−σ(exj)σ(exk),ifj≠k\frac{\partial\sigma(x_j)}{\partial x_k}= \left\{ \begin{aligned} \sigma(x_j)(1-\sigma(x_j)), & &if\ \ j=k \\ -\sigma(e^{x_j})\sigma(e^{x_k}), & &if \ \ j\neq k \end{aligned} \right. ∂xk∂σ(xj)={σ(xj)(1−σ(xj)),−σ(exj)σ(exk),if j=kif j=k
使用 δij={1,ifj=k0,otherwise\delta_{ij}=\left\{\begin{aligned}1, & &if\ \ j=k \\0, & &otherwise\end{aligned}\right.δij={1,0,if j=kotherwise,可得:
∂σ(xj)∂xk=σ(xj)(δij−σ(xj))\frac{\partial\sigma(x_j)}{\partial x_k}=\sigma(x_j)(\delta_{ij}-\sigma(x_j)) ∂xk∂σ(xj)=σ(xj)(δij−σ(xj))
因此,可以得到:
∂pi∂scorei=pi(1−pi)\frac{\partial p_i}{\partial score_i}=p_i(1-p_i) ∂scorei∂pi=pi(1−pi)
综上,可得:
∂E∂scorei=∂E∂pi∂pi∂scorei=[−cipi+(1−ci)(1−pi)][pi(1−pi)]=−ci[pi(1−pi)]pi+(1−ci)[pi(1−pi)](1−pi)=pi−ci\frac{\partial E}{\partial score_i}=\frac{\partial E}{\partial p_i}\frac{\partial p_i}{\partial score_i}=[-\frac{c_i}{p_i}+\frac {(1-c_i)}{(1-p_i)}][p_i(1-p_i)]=-\frac{c_i[p_i(1-p_i)]}{p_i}+\frac {(1-c_i)[p_i(1-p_i)]}{(1-p_i)}=p_i-c_i ∂scorei∂E=∂pi∂E∂scorei∂pi=[−pici+(1−pi)(1−ci)][pi(1−pi)]=−pici[pi(1−pi)]+(1−pi)(1−ci)[pi(1−pi)]=pi−ci
其中 cic_ici 表示独热编码的类别,pip_ipi 表示 softmax
概率。最终结果为:
∂E∂scorei=pi−ci\frac{\partial E}{\partial score_i}=p_i-c_i ∂scorei∂E=pi−ci
6. 批量梯度下降、随机梯度下降和小批量梯度下降
优化神经网络的问题在于调整网络的权重 www,以使损失函数最小化。方便起见,可以将损失函数视为一个求和形式,这种形式能够代表常用的损失函数:
Q(w)=1n∑i=1nQi(w)Q(w)=\frac 1n\sum_{i=1}^nQ_i(w) Q(w)=n1i=1∑nQi(w)
在这种情况下,我们可以使用类似的步骤进行导数计算,其中 η\etaη 是学习率,∇\nabla∇ 是梯度:
w=w−η∇Q(w)=w−η∇∑i=1nQi(w)w=w-\eta\nabla Q(w)=w-\eta\nabla \sum_{i=1}^nQ_i(w) w=w−η∇Q(w)=w−η∇i=1∑nQi(w)
当训练集非常大时,计算上述梯度可能需要对所有求和函数的梯度进行耗时的计算。为了简化这一过程,可以使用三种梯度下降方法,每种方法在处理训练数据集的方式上有所不同。
6.1 批量梯度下降
批量梯度下降 (Batch Gradient Descent
, BGD
) 计算误差的变化,但仅在整个数据集被评估后再更新整个模型。在计算上,这种方法非常高效,但它要求将整个数据集的结果存储在内存中。
6.2 随机梯度下降
随机梯度下降 (Stochastic Gradient Descent
, SGD
) 并不是在评估完数据集后更新模型,而是在每一个训练样本之后进行更新。
6.3 小批量梯度下降
小批量梯度下降 (Mini-Batch Gradient Descent
, MBGD
) 将批量梯度下降和随机梯度下降结合为一种启发式方法。数据集被分成大小为 bs
的小批量,通常大小为 64
到 256
之间,然后对每个小批量单独进行评估。
需要注意的是,bs
是训练过程中需要调整的一个超参数。通过调整批大小和学习率参数,可以找到一个更接近全局最小值的解决方案。
与梯度下降方法相比,梯度下降能够平滑的最小化损失函数,而小批量梯度下降的损失函数最小化过程波动性较大,但损失函数仍然呈下降趋势。波动性的原因在于小批量是所有样本的一个子集,这种采样可能导致损失函数发生波动。
7. 反向传播和卷积神经网络
在本节中,将探讨反向传播和卷积神经网络。简单起见,以一个大小为 5 x 5
的输入 xxx、大小 3 x 3
且无填充、步长为 1
的卷积核 www 为例。标准的卷积计算过程如以下动图所示:
接下来,分析在当前层的反向传播。接收到一个反向传播信号 ∂L∂hij\frac {\partial L}{\partial h_{ij}}∂hij∂L 作为输入,并需要计算 ∂L∂wij\frac {\partial L}{\partial w_{ij}}∂wij∂L 和 ∂L∂xij\frac {\partial L}{\partial x_{ij}}∂xij∂L。需要注意的是,卷积核中的每个权重都会影响输出图中的每个像素,换句话说,卷积核的任何权重变化都会影响所有输出像素。
8. 反向传播和循环神经网络
循环神经网络 (Recurrent Neural Network
, RNN
) 的基本方程是 ht=tanh(Uxt+Wht−1)h_t=tanh(U{x_t}+Wh_{t-1})ht=tanh(Uxt+Wht−1),最终预测 y^t=softmax(Vht)\hat y_t=softmax(Vh_t)y^t=softmax(Vht),在时间步 ttt,真实值是 yty_tyt,而误差 EEE 是交叉熵。其中,UUU、VVV 和 WWW 是用于 RNN
方程的学习参数。方程可以通过展开递归来可视化,如下图所示。核心思想是总误差是每个时间步误差的总和。
如果使用 SGD
,需要对一个给定训练样本的每个时间步的误差和梯度进行求和:
通过使用链式法则,可以证明 ∂E∂V\frac {\partial E}{\partial V}∂V∂E 仅依赖于当前时间步值(以 ∂E3∂V\frac {\partial E_3}{\partial V}∂V∂E3 为例):
∂E3∂V=∂E3∂y^3∂y^3∂V=∂E3∂y^3∂y^3∂z3∂z3∂V=(y^3−y3)h3\frac {\partial E_3}{\partial V}=\frac {\partial E_3}{\partial \hat y_3}\frac {\partial \hat y_3}{\partial V}=\frac {\partial E_3}{\partial \hat y_3}\frac {\partial \hat y_3}{\partial z_3}\frac {\partial z_3}{\partial V}=(\hat y_3-y _3)h_3 ∂V∂E3=∂y^3∂E3∂V∂y^3=∂y^3∂E3∂z3∂y^3∂V∂z3=(y^3−y3)h3
而 ∂E∂W\frac {\partial E}{\partial W}∂W∂E 具有跨时间步的依赖性,因为例如,h3=tanh(Ux3+Wh2)h_3=tanh(Ux_3+Wh_2)h3=tanh(Ux3+Wh2) 依赖于 h2h_2h2,而 h2h_2h2 又依赖于 h1h_1h1。因此,梯度会稍微复杂一些,因为我们需要对每个时间步的贡献进行求和:
∂E3∂W=∑k=03∂E3∂y^3∂y^3∂h3∂h3∂hk∂hk∂W\frac {\partial E_3}{\partial W}=\sum_{k=0}^3\frac {\partial E_3}{\partial \hat y_3}\frac {\partial \hat y_3}{\partial h_3}\frac {\partial h_3}{\partial h_k}\frac {\partial h_k}{\partial W} ∂W∂E3=k=0∑3∂y^3∂E3∂h3∂y^3∂hk∂h3∂W∂hk
为了理解以上方程,假设使用传统前馈神经网络的标准反向传播算法。需要额外添加跨时间步的 W 的梯度,这是因为我们可以通过展开 RNN
使时间上的依赖性变得显式,因此 RNN
的反向传播通常称为时间反向传播 (Backpropagation Through Time
, BPTT
),RNN
的反向传播过程如下。
考虑到:
∂E3∂W=∑k=03∂E3∂y^3∂y^3∂h3∂h3∂hk∂hk∂W\frac {\partial E_3}{\partial W}=\sum_{k=0}^3\frac {\partial E_3}{\partial \hat y_3}\frac {\partial \hat y_3}{\partial h_3}\frac {\partial h_3}{\partial h_k}\frac {\partial h_k}{\partial W} ∂W∂E3=k=0∑3∂y^3∂E3∂h3∂y^3∂hk∂h3∂W∂hk
∂h3∂hk\frac {\partial h_3}{\partial h_k}∂hk∂h3 需要再次用链式法则进行计算,会产生大量的乘法。在这种情况下,需要对一个向量函数相对于另一个向量进行求导,因此需要一个包含所有逐点导数的矩阵(即雅可比矩阵):
∂h3∂hk=∏j=k+13∂hj∂hj−1\frac {\partial h_3}{\partial h_k}=\prod _{j=k+1}^3\frac{\partial h_j}{\partial h_{j-1}} ∂hk∂h3=j=k+1∏3∂hj−1∂hj
因此:
∂E3∂W=∑k=03∂E3∂y^3∂y^3∂h3(∏j=k+13∂hj∂hj−1)∂hk∂W\frac {\partial E_3}{\partial W}=\sum_{k=0}^3\frac {\partial E_3}{\partial \hat y_3}\frac {\partial \hat y_3}{\partial h_3}(\prod _{j=k+1}^3\frac{\partial h_j}{\partial h_{j-1}})\frac {\partial h_k}{\partial W} ∂W∂E3=k=0∑3∂y^3∂E3∂h3∂y^3(j=k+1∏3∂hj−1∂hj)∂W∂hk
由于方程中乘法的存在,且 sigmoid
和 tanh
在函数两端都会饱和,其导数趋近于 0
。在这种情况下,会使前面层的梯度趋近于 0
,这会导致梯度在几个时间步后完全消失,网络无法从相距较远的过去的信息中学习。
可以使用长短期记忆和门控递归单元来解决梯度消失的问题,并有效地学习长期依赖关系。同样,当雅可比矩阵中的某一项变得很大时,也可能出现梯度爆炸的情况,可以使用梯度裁剪来处理这一问题。
9. TensorFlow 和自动微分
TensorFlow
可以自动计算导数,这一特性称为自动微分,这同样是通过使用链式法则实现这一点。计算图中的每个节点都有一个附加的梯度操作,用于计算输入对输出的导数。之后,参数的梯度会在反向传播过程中自动计算。
自动微分是一个非常重要的特性,避免了为每种神经网络手动编写反向传播的过程,使我们能够专注于快速迭代和实验运行。
小结
在本节中,我们讨论了深度学习背后的数学原理。简单来说,深度学习模型通过给定一个输入向量逼近一个函数以产生输出,其中模型可以拥有数十亿个需要调整的参数(权重)。反向传播是深度学习用来高效训练人工神经网络的核心数学算法,采用了利用链式法则的梯度下降方法。该算法使用两个交替重复的步骤:前向传播和反向传播。
在前向传播步骤中,输入通过网络传播以预测输出。这些预测可能与真实值不同,使用真实值评估网络的质量。换句话说,预测值和真实值之间存在误差,网络目标是将损失最小化。反向传播的作用就是通过调整网络的权重来减少误差,误差通过损失函数计算,例如均方差或用于非连续值(如布尔值)的交叉熵。梯度下降优化算法用于通过计算损失函数的梯度来调整神经元的权重。反向传播计算梯度,而梯度下降使用这些梯度来训练模型。减少预测误差可以提高准确性,从而使机器学习模型得以改进。随机梯度下降是最简单的优化方法,通过沿着梯度方向移动实现,而 Adam
和 RMSProp
优化器使用梯度的一阶矩和二阶矩,一阶矩是先前梯度的指数衰减平均,二阶矩是先前平方梯度的指数衰减平均。
深度学习模型可以看作是由多个基本组件组成的计算图,图中的每个节点接收张量作为输入,并产生张量作为输出,训练过程通过反向传播调整每个节点的权重,其中的关键思想是通过梯度下降来减少最终输出节点的误差。
系列链接
TensorFlow深度学习实战(1)——神经网络与模型训练过程详解
TensorFlow深度学习实战(2)——使用TensorFlow构建神经网络
TensorFlow深度学习实战(3)——深度学习中常用激活函数详解
TensorFlow深度学习实战(4)——正则化技术详解
TensorFlow深度学习实战(5)——神经网络性能优化技术详解
TensorFlow深度学习实战(6)——回归分析详解
TensorFlow深度学习实战(7)——分类任务详解
TensorFlow深度学习实战(8)——卷积神经网络
TensorFlow深度学习实战(9)——构建VGG模型实现图像分类
TensorFlow深度学习实战(10)——迁移学习详解
TensorFlow深度学习实战(11)——风格迁移详解
TensorFlow深度学习实战(12)——词嵌入技术详解
TensorFlow深度学习实战(13)——神经嵌入详解
TensorFlow深度学习实战(14)——循环神经网络详解
TensorFlow深度学习实战(15)——编码器-解码器架构
TensorFlow深度学习实战(16)——注意力机制详解
TensorFlow深度学习实战(17)——主成分分析详解
TensorFlow深度学习实战(18)——K-means 聚类详解
TensorFlow深度学习实战(19)——受限玻尔兹曼机
TensorFlow深度学习实战(20)——自组织映射详解
TensorFlow深度学习实战(21)——Transformer架构详解与实现
TensorFlow深度学习实战(22)——从零开始实现Transformer机器翻译
TensorFlow深度学习实战(23)——自编码器详解与实现
TensorFlow深度学习实战(24)——卷积自编码器详解与实现
TensorFlow深度学习实战(25)——变分自编码器详解与实现
TensorFlow深度学习实战(26)——生成对抗网络详解与实现
TensorFlow深度学习实战(27)——CycleGAN详解与实现
TensorFlow深度学习实战(28)——扩散模型(Diffusion Model)
TensorFlow深度学习实战(29)——自监督学习(Self-Supervised Learning)
TensorFlow深度学习实战(30)——强化学习(Reinforcement learning,RL)
TensorFlow深度学习实战(31)——强化学习仿真库Gymnasium
TensorFlow深度学习实战(32)——深度Q网络(Deep Q-Network,DQN)
TensorFlow深度学习实战(33)——深度确定性策略梯度
TensorFlow深度学习实战(34)——TensorFlow Probability
TensorFlow深度学习实战(35)——概率神经网络
TensorFlow深度学习实战(36)——自动机器学习(AutoML)