DLSYS3 神经网络

· Tech

如果数据在原始输入空间里不是线性可分的,我们就需要学习非线性特征。线性假设类具有以下形式:

hθ(x)=θTx,θ∈Rn×kh_\theta(x) = \theta^T x,\quad \theta \in \mathbb{R}^{n\times k}

对于一个输入 x∈Rnx \in \mathbb{R}^n ,模型会产生 kk 个输出 logits。当类别可以用线性决策边界分开时,这样做是可行的,但当类别结构是非线性的时候,就行不通了。例如下图的结构:

问题在于假设类(hypothesis class):线性模型在原始特征空间里只能表达线性边界。

一种让线性模型更具表达力的方式是对原始输入做特征映射(相关可参见核技巧)。如下式:

hθ(x)=θTϕ(x)h_\theta(x) = \theta^T \phi(x)

此处,ϕ(x)\phi(x) 是一个特征映射。如果 ϕ\phi 能把原始输入映射成一个更好的表示,那么在 ϕ(x)\phi(x) 之上加一个线性分类器,就能解决那些在原始输入空间里本来是非线性的问题。但如果这个特征映射只是线性映射,并且后面仍接一个线性模型,那么整体依然是线性的,因此不会增加模型的表达能力。例如:

ϕ(x)=WTx\phi(x) = W^T x

那么有:

hθ(x)=θTϕ(x)=θT(WTx)=(θTWT)xh_\theta(x) = \theta^T \phi(x) = \theta^T (W^T x) = (\theta^T W^T) x

可见,把多个线性层堆叠起来,最终还是会合并成一个线性层,所以这个模型依然没法表示非线性的决策边界。为了让模型具备非线性的表达能力,我们应插入一个非线性激活函数:

ϕ(x)=σ(WTx)\phi(x) = \sigma(W^T x)

那么有

hθ(x)=θTσ(WTx)h_\theta(x) = \theta^T \sigma(W^T x)

  • 在随机傅里叶特征方法(RFF)中, WW 等参数随机采样后固定,即固定的非线性特征。
  • 在神经网络中:从数据中学习 WW,即可学习的非线性特征。

1. 神经网络

神经网络可以看作一系列带参数的函数逐层复合而成。人工神经网络(Artificial Neural Networks)是受生物神经网络启发的。但是人类大脑是否使用反向传播的方式进行更新仍是一个开放性问题。

以典型的MLP为例,通过神经网络的输入经历这样几轮过程:隐藏层通常先对输入进行仿射变换(线性变换+偏置项),再经过非线性激活,并将结果传递给下一层;输出层则根据任务决定是否使用额外的激活函数。

x→affine→activation→h1→⋯→hn→affine→<output activation>→yx \to \text{affine} \to \text{activation} \to h_{1} \to \dots \to h_n \to \text{affine} \to \text{<output activation>} \to y

感知机(Perceptron)可以看作早期最简单的人工神经元模型,详情参见感知机。它先对输入做加权求和与偏置(即仿射变换),再通过阶跃函数作为激活得到输出。感知机的运作方式比较贴近逻辑门,实际上感知机所组成的网络确实可以搭建出各种逻辑计算的网络。但阶跃函数的输出是不连续的,参数发生微小变化时,输出可能保持不变或突然翻转,因此不适合使用梯度下降和反向传播进行训练。后来的神经网络通常使用 Sigmoid、ReLU 等激活函数,使网络更适合基于梯度的方法进行优化。

下面以双层神经网络举例说明。

1.1 例:双层神经网络

一个两层的神经网络可以写成如下形式:

hθ(x)=W2Tσ(W1Tx)h_\theta(x) = W_2^T \sigma(W_1^T x)

  • 矩阵 W1W_1 把输入映射成一个隐藏表示,非线性函数 𝜎 防止它退化回线性模型。
  • 矩阵 W2W_2 则把隐藏表示映射到输出。

因此,输入层不参与计算,隐藏层和输出层共两层参与计算。

对于 batch,表示为:

hθ(X)=σ(XW1)W2h_\theta(X) = \sigma(X W_1) W_2

1.2 万能逼近定理

带有 ReLU 这类非线性激活函数的神经网络,能够逼近有界区域上很大一类光滑函数。直观地说,ReLU 网络是在构建分段线性近似:只要隐藏单元足够多,这些分段就能把复杂函数拟合得任意好。

万能逼近定理(Universal Function Approximation):在适当的非线性激活函数下,一个具有足够多隐藏单元的单隐藏层前馈神经网络,可以以任意精度逼近闭且有界区域上的任意连续函数。

该定理只保证网络具有这种表示能力,并不保证训练算法能够找到对应参数,也不说明实现该逼近需要多少隐藏单元。

它主要由 Approximation by Superpositions of a Sigmoidal Function 1989 等工作整理得到,其证明了证明了带单隐藏层、sigmoid 非线性的前馈网络,可以在适当条件下任意精度逼近连续函数。注意到,单隐藏层下的网络就已经具备这一强大的能力,所以即使是极其简单的网络结构也能非常强大。

提示

关于该定理,可以注意两点:

  • 该定理并不意味着一个网络可以用来精确计算任何函数。通常来说,只要有足够多的隐藏层神经元,我们总能找到一个网络,使其输出与目标函数的差异落在我们设定的精度范围内。
  • 该定理表明用神经网络去逼近的函数是连续函数。对于不连续的目标函数,可以尝试用连续函数去近似它。

1.3 L层多层感知机

下面给出L层多层感知机(或称前馈网络、全连接网络)的(前向传播)表示:

Zi+1=σi(ZiWi),  i=1,…,LZ1=X,hθ(X)=ZL+1[Zi∈Rm×ni,  Wi∈Rni×ni+1]\begin{aligned} Z_{i+1} &= \sigma_i(Z_i W_i),\; i=1,\dots,L \\ Z_1 &= X, \\ h_\theta(X) &= Z_{L+1} \\ \big[Z_i &\in \mathbb{R}^{m\times n_i},\; W_i \in \mathbb{R}^{n_i\times n_{i+1}}\big] \end{aligned}

其中非线性函数 σ:R→R\sigma: \mathbb{R} \to \mathbb{R} 是逐元素作用的,参数如下:

θ={W1,…,WL}\theta = \{ W_1,\dots,W_L \}

(上述表示也可以选择加偏置项,即 Zi+1=σi(ZiWi+bi)Z_{i+1} = \sigma_i(Z_i W_i + b_i))

  • ZiZ_i :某层表示,其中 mm 为样本数(batch size),nin_i 为第 ii 层神经元数
  • WiW_i:权重矩阵
  • σi\sigma_i :激活函数

最终的输出为 ZL+1Z_{L+1}。

2. 激活函数

2.1 ReLU

ReLU(Rectified linear unit),称为修正线性单元,是最受欢迎的激活函数。其形式如下:

ReLU(x)=max(0,x)ReLU(x)=max(0,x)

如图,当输入为负时,ReLU函数的导数为0,而当输入为正时,ReLU函数的导数为1。 注意,当输入值精确等于0时,ReLU函数不可导,不过此时我们默认使用左侧的导数,即导数值为 0。下面绘制ReLU函数的导数:

使用ReLU的原因是,它求导表现得特别好:要么让参数消失,要么让参数通过。关于 ReLU,通常需要注意以下几点:

  • 研究发现,跟 sigmoid/tanh 函数比起来,它能让随机梯度下降的收敛速度大大加快。并且它的计算也比其他激活函数更为简单。
  • ReLU减轻了困扰以往神经网络的梯度消失问题。例如,采用 sigmoid/tanh 函数,当输入很大或很小时,sigmoid 的导数接近 0,多层连续相乘后,前面层的梯度就可能接近 0。而 ReLU 在正半轴的梯度为 1,在反向传播时就可以显著缓解由激活函数饱和导致的梯度消失。
  • 但ReLU可能引入神经元死亡的问题。当一个很大的梯度流过某个 ReLU 神经元时(或者学习率大,导致一次剧烈更新后,神经元的预测值总是落在负半轴),可能会导致权重更新到一种状态,使这个神经元以后再也不会对任何数据点激活。如果发生这种情况,那么从这个时刻起,流过这个单元的梯度就会永远是零。

Leaky ReLU:尝试解决"Dying ReLU"问题的一种方法。该变体为ReLU添加了一个线性项,因此即使参数是负的,某些信息仍然可以通过。其形式如下,其中 α\alpha 是一个较小的固定常数:

LeakyReLU(x)=1(x<0)(ax)+1(x≥0)(x)Leaky ReLU(x) = \mathbb{1}(x < 0)(a x) + \mathbb{1}(x \ge 0)(x)

Parameterized ReLU,pReLU(参数化ReLU函数 He et al., 2015): 其形式与Leaky ReLU一致,但其中 α\alpha 是可学习参数:

pReLU(x)=max⁡(0,x)+αmin⁡(0,x)\text{pReLU}(x) = \max(0, x) + \alpha \min(0, x)

2.2 Sigmoid

对于一个定义域在 R\mathbb{R} 中的输入, sigmoid函数将输入变换为区间 (0,1)(0, 1) 上的输出。 因此,sigmoid通常称为 挤压函数(squashing function): 它将范围 (−inf,inf)(-inf, inf) 中的任意输入压缩到区间 (0,1)(0, 1) 中的某个值:

sigmoid(x)=11+exp⁡(−x)\text{sigmoid}(x) = \frac{1}{1+\exp(-x)}

Sigmoid 函数在历史上曾被频繁使用,因为它有一个很好的解释,可以看作神经元的放电率:从完全不放电(0)到以假定的最大频率完全饱和放电(1)。在实践中,Sigmoid 非线性最近已经不再受欢迎,几乎很少使用。它有两个主要缺点:

  • Sigmoid 会饱和并“杀死梯度”。当神经元的激活在 0 或 1 两端饱和时,这些区域的梯度几乎为零。直接导致我们上一节中提到的梯度消失问题。
  • Sigmoid 的输出总是正数,不以 0 为中心。如果一个神经元收到的数据总是正的(比如在 f=wTx+bf=w^Tx+b 中逐元素 x>0x \gt 0),那么在反向传播时,权重 ww 上的梯度要么全是正的,要么全是负的(取决于整个表达式 ff 的梯度)。这可能会在权重的梯度更新中引入不好的之字形震荡。

Sigmoid 函数的导数有如下性质:

ddxsigmoid(x)=exp⁡(−x)(1+exp⁡(−x))2=sigmoid(x)(1−sigmoid(x))\frac{d}{dx}\text{sigmoid}(x) = \frac{\exp(-x)}{(1+\exp(-x))^2} = \text{sigmoid}(x)\big(1-\text{sigmoid}(x)\big)

注意,当输入为0时,sigmoid函数的导数达到最大值0.25; 而输入在任一方向上越远离0点时,导数越接近0。

2.3 tanh

tanh(双曲正切)函数的公式如下:

tanh⁡(x)=1−exp⁡(−2x)1+exp⁡(−2x)\tanh(x) = \frac{1-\exp(-2x)}{1+\exp(-2x)}

它把一个实数压缩到 (−1,1)(-1, 1) 这个范围里。和 sigmoid 神经元一样,它的激活值也会饱和,但跟 sigmoid 神经元不同的是,它的输出是以零为中心的。所以,在实际使用中,tanh 非线性函数总是比 sigmoid 非线性函数更受青睐。

另外还要注意,tanh 神经元其实就是一个缩放过的 sigmoid 神经元,具体来说就是:tanh(x)=2sigmoid(2x)−1tanh(x)=2sigmoid(2x)−1。

tanh函数的导数是:

ddxtanh⁡(x)=1−tanh⁡2(x)\frac{d}{dx}\tanh(x) = 1-\tanh^2(x)

 当输入接近0时,tanh函数的导数接近最大值1。 与我们在sigmoid函数图像中看到的类似, 输入在任一方向上越远离0点,导数越接近0。

2.4 maxout

一个神经元内部同时有两(或多)组参数,得到两个仿射结果,然后谁大就输出谁,即:

max⁡(w1Tx+b1,  w2Tx+b2)\max\left(w_1^T x + b_1,\; w_2^T x + b_2\right)

ReLU 和 Leaky ReLU 都可以看成 Maxout 的特例。因此,Maxout 神经元享有 ReLU 单元的所有好处(运行时处于线性区域,不会饱和),而且没有它的缺点(Dying ReLU)。不过,和 ReLU 神经元不同的是,它会让每一个神经元的参数数量翻倍,从而导致总参数数量很高。

2.5 总结

一般都用 ReLU,其他的基本上排不上用场了(

3. 反向传播(梯度计算)

回想一下,神经网络只是指定了机器学习算法“三要素”中的一个,我们还需要:

• 损失函数:还是交叉熵损失,跟上次一样
• 优化过程:还是随机梯度下降,跟上次一样

为何这里softmax回归和神经网络MLP其损失函数都使用交叉熵损失?
因为目前这些问题的(概率)假设均是多分类假设,该假设下最大似然对应的负对数似然恰好就是交叉熵损失的形式。

换句话说,我们仍然要解决这个优化问题:

min⁡θ1m∑i=1mℓce(hθ(x(i)),y(i))\min_{\theta} \frac{1}{m}\sum_{i=1}^{m}\ell_{ce}\big(h_{\theta}(x^{(i)}),y^{(i)}\big)

不过现在的 hθ(x)h_\theta(x) 是一个神经网络。我们需要对 θ\theta 的每个元素计算梯度:

∇θℓce(hθ(x(i)),y(i))\nabla_{\theta}\ell_{ce}\big(h_{\theta}(x^{(i)}),y^{(i)}\big)

3.1 双层神经网络

我们来推导一下双层神经网络的梯度,采用batch形式写出来,即求解:

∇{W1,W2}ℓce(σ(XW1)W2, y)\nabla_{\{W_1,W_2\}} \ell_{ce}\big(\sigma(XW_1)W_2,\,y\big)

对于 W2W_2 的梯度,其计算形似上节 softmax 回归(其中 mm 是 batch size,dd 是隐藏层宽度,kk 是类别数):

∂ℓce(σ(XW1)W2, y)∂W2=∂ℓce(σ(XW1)W2, y)∂σ(XW1)W2⋅∂σ(XW1)W2∂W2=(S−Iy)m×k⋅σ(XW1)m×d=σ(XW1)T⋅(S−Iy)[S=softmax(σ(XW1)W2)]\begin{aligned} \frac{\partial \ell_{ce}\big(\sigma(XW_1)W_2,\,y\big)}{\partial W_2} &= \frac{\partial \ell_{ce}\big(\sigma(XW_1)W_2,\,y\big)}{\partial \sigma(XW_1)W_2} \cdot \frac{\partial \sigma(XW_1)W_2}{\partial W_2} \\ &= \big(S-I_y\big)_{m\times k} \cdot \sigma(XW_1)_{m\times d} \\ &= \sigma(XW_1)^T \cdot \big(S-I_y\big) \\ \big[S &= \mathrm{softmax}\big(\sigma(XW_1)W_2\big)\big] \end{aligned}

  • 第一步偏导数计算,复用了上一节计算交叉熵损失的导数的结果
  • 第二步偏导数计算,根据链式法则后的简单计算即可

对于 W1W_1 的梯度,需要多次应用链式法则:

∂ℓce(σ(XW1)W2,y)∂W1=∂ℓce(σ(XW1)W2,y)∂(σ(XW1)W2)⋅∂(σ(XW1)W2)∂σ(XW1)⋅∂σ(XW1)∂(XW1)⋅∂(XW1)∂W1=(S−Iy)m×k⋅(W2T)k×d⋅σ′(XW1)m×d⋅Xm×n=XT[((S−Iy)W2T)⊙σ′(XW1)][S=softmax(σ(XW1)W2)]\begin{aligned} \frac{\partial \ell_{ce}\big(\sigma(XW_1)W_2,y\big)}{\partial W_1} &= \frac{\partial \ell_{ce}\big(\sigma(XW_1)W_2,y\big)}{\partial \big(\sigma(XW_1)W_2\big)} \cdot \frac{\partial \big(\sigma(XW_1)W_2\big)}{\partial \sigma(XW_1)} \cdot \frac{\partial \sigma(XW_1)}{\partial (XW_1)} \cdot \frac{\partial (XW_1)}{\partial W_1} \\ &= (S-I_y)_{m\times k}\cdot (W_2^T)_{k\times d}\cdot \sigma'(XW_1)_{m\times d}\cdot X_{m\times n} \\ &= X^T\Big[\big((S-I_y)W_2^T\big)\odot \sigma'(XW_1)\Big] \\ \big[S &= \mathrm{softmax}\big(\sigma(XW_1)W_2\big)\big] \end{aligned}

可以拆解为以下几个中间变量便于理解:

A=XW1,H=σ(A),Z=HW2,L=ℓce(Z,y)A = XW_1,\quad H = \sigma(A),\quad Z = HW_2,\quad L = \ell_{ce}(Z,y)

那么我们即需要计算 ∂L∂W1\frac{\partial L}{\partial W_1},那么梯度将从 LL 一路反向传到 W1W_1(注意检查矩阵维度):

  1. 从 ce loss 传到 ZZ :复用前述结论,有 ∂L∂Z=S−Iy\frac{\partial L}{\partial Z} = S - I_y,其中 S=softmax(Z)S = softmax(Z)
  2. 从 ZZ 传到 HH :有 ∂L∂H=(S−Iy)W2T\frac{\partial L}{\partial H} = (S - I_y)W_2^T
  3. 从 HH 传到 AA :此处激活函数是逐元素作用的,所以其局部导数也逐元素作用在上游梯度上,即 ∂L∂A=∂L∂H⊙σ′(A)\frac{\partial L}{\partial A} = \frac{\partial L}{\partial H} \odot \sigma^{'}(A) 。代入后有 ∂L∂A=(S−Iy)W2T⊙σ′(A)\frac{\partial L}{\partial A} = (S - I_y)W_2^T \odot \sigma^{'}(A)
  4. 最后从 AA 传到 W1W_1 :∂L∂W1=XT∂L∂A=XT((S−Iy)W2T⊙σ′(A))\frac{\partial L}{\partial W_1} = X^T \frac{\partial L}{\partial A} = X^T ((S - I_y)W_2^T \odot \sigma^{'}(A))

关于第 3 步,可以举个例子:如采用 ReLU 激活函数,那么有

σ′(A)ij={1,Aij>00,Aij≤0\sigma'(A)_{ij}= \begin{cases} 1, & A_{ij} \gt 0 \\ 0, & A_{ij} \le 0 \end{cases}

在反向传播的时候,上游传来的梯度会跟 ReLU 的导数逐元素相乘。此处神经元通过 ReLU 激活函数,其导数为 1 的位置保留上游梯度,导数为 0 的位置将梯度置零。

3.2 MLP

继续将上述做法推广到我们的 LL 层全连接网络,给出:

Zi+1=σi(ZiWi),i=1,…,LZ_{i+1} = \sigma_i(Z_i W_i),\quad i=1,\dots,L

注意,Zi+1Z_{i+1} 是第 ii 层的输出,同时是第 i+1i + 1 层的输入。 LL 层全连接网络的最终输出是 ZL+1Z_{L+1} 。那么,假设我们想要求某一层参数 WiW_i 的梯度,可以发现:

∂ℓ(ZL+1,y)∂Wi=∂ℓ∂ZL+1⋅∂ZL+1∂ZL⋅∂ZL∂ZL−1⋅⋯⋅∂Zi+2∂Zi+1⏟Gi+1⋅∂Zi+1∂Wi\frac{\partial \ell(Z_{L+1},y)}{\partial W_i} = \underbrace{ \frac{\partial \ell}{\partial Z_{L+1}} \cdot \frac{\partial Z_{L+1}}{\partial Z_L} \cdot \frac{\partial Z_L}{\partial Z_{L-1}} \cdot \cdots \cdot \frac{\partial Z_{i+2}}{\partial Z_{i+1}} }_{G_{i+1}} \cdot \frac{\partial Z_{i+1}}{\partial W_i} \\[1.6em] \kern{7em}

其中,定义已经从最终 loss 反向传播到第 i+1i+1 层的梯度为:

Gi+1=∂ℓ(ZL+1,y)∂Zi+1G_{i+1} = \frac{\partial \ell(Z_{L+1},y)}{\partial Z_{i+1}}

那么,对 GiG_i 有递推式:

Gi=∂ℓ∂Zi=Gi+1⋅∂Zi+1∂Zi=Gi+1⋅∂σi(ZiWi)∂ZiWi⋅∂ZiWi∂Zi=Gi+1⋅σ′(ZiWi)⋅Wi\begin{aligned} G_{i} &= \frac{\partial \ell}{\partial Z_{i}} \\ &= G_{i+1}\cdot \frac{\partial Z_{i+1}}{\partial Z_{i}} \\ &= G_{i+1}\cdot \frac{\partial \sigma_{i}(Z_{i}W_{i})}{\partial Z_{i}W_{i}}\cdot \frac{\partial Z_{i}W_{i}}{\partial Z_{i}} \\ &= G_{i+1}\cdot \sigma'(Z_{i}W_{i})\cdot W_{i} \end{aligned}

考虑上述记号中的矩阵维度,那么有

Gi=[Gi+1]m×ni+1⋅σ′(ZiWi)m×ni+1⋅[Wi]ni×ni+1=(Gi+1⊙σ′(ZiWi))WiT\begin{aligned} G_i &= \left[G_{i+1}\right]_{m\times n_{i+1}} \cdot \sigma'(Z_i W_i)_{m\times n_{i+1}} \cdot \left[W_i\right]_{n_i\times n_{i+1}} \\ &= \left(G_{i+1} \odot \sigma'(Z_i W_i)\right) W_i^T \end{aligned}

将 GiG_i 式代回 WiW_i 的梯度求解式中,与之前的过程有些类似:

∂ℓ(ZL+1,y)∂Wi=Gi+1⋅∂Zi+1∂Wi=Gi+1⋅∂σ(ZiWi)∂ZiWi⋅∂ZiWi∂Wi=[Gi+1]m×ni+1⋅σ′(ZiWi)m×ni+1⋅[Zi]m×ni=ZiT⋅[Gi+1⊙σ′(ZiWi)]\begin{aligned} \frac{\partial \ell(Z_{L+1},y)}{\partial W_i} &= G_{i+1}\cdot \frac{\partial Z_{i+1}}{\partial W_i} \\ &= G_{i+1}\cdot \frac{\partial \sigma(Z_i W_i)}{\partial Z_i W_i}\cdot \frac{\partial Z_i W_i}{\partial W_i} \\ &= \left[G_{i+1}\right]_{m\times n_{i+1}} \cdot \sigma'(Z_i W_i)_{m\times n_{i+1}} \cdot \left[Z_i\right]_{m\times n_i} \\ &= Z_i^T \cdot \left[G_{i+1} \odot \sigma'(Z_i W_i)\right] \end{aligned}

3.3 总结

综合以上内容,我们可以按照下面的步骤高效地计算神经网络所需的所有梯度:

前向传播(forward pass):

  • 初始时:Z1=XZ_1 = X
  • 迭代:Zi+1=σi(ZiWi),i=1,…,LZ_{i+1} = \sigma_i(Z_i W_i),\quad i=1,\dots,L

反向传播(Backward pass):

  • 初始时:GL+1=∇ZL+1ℓ(ZL+1,y)=S−IyG_{L+1} = \nabla_{Z_{L+1}} \ell(Z_{L+1},y) = S-I_y
  • 迭代:Gi=(Gi+1∘σi′(ZiWi))WiT,i=L,…,1G_{i} = \left(G_{i+1} \circ \sigma'_{i}(Z_{i}W_{i})\right) W_{i}^{T},\quad i = L,\dots,1

而且我们可以一路算出所有需要的梯度:

∇Wiℓ(ZL+1,y)=ZiT(Gi+1∘σi′(ZiWi))\nabla_{W_i}\ell(Z_{L+1},y) = Z_i^T\left(G_{i+1}\circ \sigma'_i(Z_i W_i)\right)

“反向传播”其实就是链式法则加上对中间结果的缓存。

下节课将把这套机制推广到任意计算图。

参考

  1. https://ickma2311.github.io/ML/DLSys/cmu-dlsys-lecture-3-manual-neural-networks.html
  2. Neural Networks and Deep Learning Chapter 1

    https://neuralnetworksanddeeplearning.com/chap1.html

  3. DIVE INTO DEEP LEARNING

    https://zh.d2l.ai/chapter_multilayer-perceptrons/mlp.html

  4. Neural Networks and Deep Learning Chapter 4

    https://neuralnetworksanddeeplearning.com/chap4.html