我们知道在深层神经网络训练时,会产生梯度消失问题,梯度消失的一个主要来源就是激活函数,以sigmoid函数为例,自变量在0附近导数较大,自变量越大或者越小都会造成导数缩小,如果数据分布在过小或者过大区域,过小的导数就很可能产生梯度消失问题(为何梯度消失要看激活函数的导数,这涉及到反向传播推导,可以看我的另一篇blog)。在比如,如果我们的数据大量分布在远离0的点,例如20和200,差了10倍,但是经过sigmoid激活后,输出值十分接近(我用torch.sigmoid()函数计算,结果全是1. ,可见差距是非常的小),这种过强的非线性,就很难体现出数据的差异性。为了解决这些问题,批标准化就被提出。
批标准化的基本思路是把数据拉扯到0附近,让数据满足均值为0,方差为1的正态分布,说到这里,一些人可能会想到某些方法,例如Z-Score标准化,其实批标准化就和Z-Score标准化很像,只不过我们不仅仅对输入做标准化,我们还在每一隐层的输入激活函数前,也加一层标准化。批标准化一般表示成 y = B N γ , β ( x ) y=BN_{\gamma, \beta}(x) y=BNγ,β(x)。 B N γ , β ( ⋅ ) BN_{\gamma, \beta}(·) BNγ,β(⋅)由下面四个操作构成:
上图来自论文Batch normalization: accelerating deep network training by reducing internal covariate shift,我先解释一下输入,假设目前我们想要进行批标准化的是隐层 l l l,该隐层输入的size是(B,N),其中B是batch size,N是当前隐层 l l l需要进行批标准化的神经元的个数,如果B=3,N=5,那么当前隐层输入的值,可以表示成下面的式子 [ [ x 11 x 12 x 13 x 14 x 15 ] [ x 21 x 22 x 23 x 24 x 25 ] [ x 31 x 32 x 33 x 34 x 35 ] ] \left[ \begin{matrix} [x_{11} & x_{12} & x_{13} & x_{14} & x_{15} & ] \\ [x_{21} & x_{22} & x_{23} & x_{24} & x_{25} & ] \\ [x_{31} & x_{32} & x_{33} & x_{34} & x_{35} & ] \end{matrix} \right] ⎣⎡[x11[x21[x31x12x22x32x13x23x33x14x24x34x15x25x35]]]⎦⎤
在Algoritm.1算法中,batch中的一个 x i x_{i} xi就是 [ x i 1 , x i 2 , x i 3 , x i 4 , x i 5 ] [x_{i1}, x_{i2}, x_{i3}, x_{i4}, x_{i5}] [xi1,xi2,xi3,xi4,xi5],得到的 μ B , σ B 2 , x ^ i , y i \mu_{B}, \sigma^{2}_{B},\hat{x}_{i}, y_{i} μB,σB2,x^i,yi全是五维的,所以上面的公式其实是向量运算。对于全连接层的批标准化,其实是考虑单个神经元的值为一个集体,这一集体有batch size个元素,在这一集体进行上面的四步操作,神经元间是独立的,同一隐层每个神经元各求得一个 μ B , σ B 2 \mu_{B}, \sigma^{2}_{B} μB,σB2。也就是说, x 11 , x 21 , x 31 x_{11}, x_{21}, x_{31} x11,x21,x31求一个均值,方差; x 12 , x 22 , x 33 x_{12}, x_{22}, x_{33} x12,x22,x33求一个均值,方差……
至于为何会存在 γ , β \gamma, \beta γ,β,是因为我们不想让数据经过批标准化后真的落在0附近,以sigmoid为例,0附近接近线性,而我们的神经网络需要依靠激活函数提供分线性,所以会加一个回退操作,这就是 γ , β \gamma, \beta γ,β的所用, γ , β \gamma, \beta γ,β可以通过反向传播算法进行学习, γ , β \gamma, \beta γ,β在上面例子中也是五维的。
我们知道训练数据时一般会用mini-batch,但是训练后作用于验证集或测试集时,只会一次带入一条数据,没有均值和方差,我们如何对单条数据进行批标准化呢?针对这种情况,我们利用训练集中的均值和方差,下图是论文中的原图,1-6步是训练过程,7-12步是推断过程,
从图中我们可以看出来,推断过程中,我们利用训练集的所有batch的均值 μ B \mu_{B} μB,方差 σ B 2 \sigma_{B}^{2} σB2,来通过公式 E [ x ] ← E B [ μ B ] V a r [ x ] ← m m − 1 E B [ σ B 2 ] E[x] \leftarrow E_{B}[\mu_{B}] \\ Var[x] \leftarrow \frac{m}{m-1}E_{B}[\sigma_{B}^{2}] E[x]←EB[μB]Var[x]←m−1mEB[σB2]来计算在推断过程要用的均值 E [ x ] E[x] E[x]和方差 V a r [ x ] Var[x] Var[x],这里求 E B [ μ B ] E_{B}[\mu_{B}] EB[μB]与 E B [ σ B 2 ] E_{B}[\sigma_{B}^{2}] EB[σB2]是用的指数加权平均法,具体如何实现的论文中并没有说,论文中只说了利用moving averages,关于指数加权平均法可以看这篇blog。 γ \gamma γ和 β \beta β是在训练过程中已经优化的值,推断时直接拿来用就可以,推断过程的标准化如下
y = γ x − E [ x ] V a r [ x ] + ε + β = γ x V a r [ x ] + ε + ( β − γ E [ x ] V a r [ x ] + ε ) y = \gamma \frac{x-E[x]}{\sqrt{Var[x]+\varepsilon}}+\beta=\gamma \frac{x}{\sqrt{Var[x]+\varepsilon}}+(\beta-\gamma \frac{E[x]}{\sqrt{Var[x]+\varepsilon}}) y=γVar[x]+εx−E[x]+β=γVar[x]+εx+(β−γVar[x]+εE[x])
这里参考了知乎中的推导过程。
我在之前的blog中,推导DNN和CNN的反向传播时候,都是推导的batch中的一条数据的反向传播,由于一个batch的损失函数可以表示成 L = 1 m ∑ i = 1 m J ( f ( x i ) , y i ) L=\frac{1}{m}\sum\limits_{i=1}^{m}J(f(x_{i}),y_{i}) L=m1i=1∑mJ(f(xi),yi)
其中 f ( ⋅ ) f(·) f(⋅)是整个神经网络抽象出来的函数, x i x_{i} xi是batch中的第 i i i个记录, y i y_{i} yi是第 i i i个记录的真实标签;考虑到DNN和CNN中batch中的不同记录是独立前向传播的,所以在推导公式时,可以先反向传递batch中的一条数据,最后得到的权值和偏置的偏导数求平均即可 ∂ L ∂ w = 1 m ∑ i = 1 m ∂ J ( f ( x i ) , y i ) ∂ w ∂ L ∂ b = 1 m ∑ i = 1 m ∂ J ( f ( x i ) , y i ) ∂ b \frac{\partial L}{\partial w}=\frac{1}{m}\sum\limits_{i=1}^{m} \frac{\partial J(f(x_{i}),y_{i})}{\partial w} \\ \frac{\partial L}{\partial b}=\frac{1}{m}\sum\limits_{i=1}^{m} \frac{\partial J(f(x_{i}),y_{i})}{\partial b} ∂w∂L=m1i=1∑m∂w∂J(f(xi),yi)∂b∂L=m1i=1∑m∂b∂J(f(xi),yi)
但是在含有批标准化的网络中,存在求batch的均值和方差的步骤,batch的数据在前向传播中不在独立,所以推导时利用整个batch的损失 L L L推导,不在利用单个数据的损失 J J J推导。
批标准化,相当于在激活函数层前面加了一个BN层,该BN层完成 y = B N γ , β ( x ) y=BN_{\gamma, \beta}(x) y=BNγ,β(x)的批标准化的操作,该BN层又可以分成两个子层,Algorithm.1中的前三个公式在第一个子层执行,第四个公式在第二个子层操作。假设我们在第 l l l层加入了BN,其示意结构如下:
反向传播过程需要用到多元复合函数的链式求导法则,
假设我们已经反向传播到 y y y所在隐层,即我们已经知道了 ∂ L ∂ y i , i = 1 , 2 … m \frac{\partial L}{\partial y_{i}},i=1,2\dots m ∂yi∂L,i=1,2…m, i i i表示batch中的第 i i i个数据,则我们可以得到损失函数对 γ \gamma γ和 β \beta β的偏导数如下:
∂ L ∂ γ = ∑ i m ∂ L ∂ y i ∂ y i ∂ γ = ∑ i m ∂ L ∂ y i x ^ i ∂ L ∂ β = ∑ i m ∂ L ∂ y i ∂ y i ∂ β = ∑ i m ∂ L ∂ y i \frac{\partial L}{\partial \gamma}=\sum\limits_{i}^{m}\frac{\partial L}{\partial y_{i}}\frac{\partial y_{i}}{\partial \gamma}=\sum\limits_{i}^{m}\frac{\partial L}{\partial y_{i}}\hat{x}_{i} \\ \frac{\partial L}{\partial \beta}=\sum\limits_{i}^{m}\frac{\partial L}{\partial y_{i}}\frac{\partial y_{i}}{\partial \beta}=\sum\limits_{i}^{m}\frac{\partial L}{\partial y_{i}} ∂γ∂L=i∑m∂yi∂L∂γ∂yi=i∑m∂yi∂Lx^i∂β∂L=i∑m∂yi∂L∂β∂yi=i∑m∂yi∂L
这样我们就用 ∂ L ∂ y i \frac{\partial L}{\partial y_{i}} ∂yi∂L求出损失函数对 γ \gamma γ和 β \beta β的偏导数。下面考虑把 ∂ L ∂ y i \frac{\partial L}{\partial y_{i}} ∂yi∂L进一步反向传播,即把误差由上图的 y y y所在隐层,传播到 x ^ i \hat{x}_{i} x^i所在隐层:
∂ L ∂ x ^ i = ∂ L ∂ y i ∂ y i ∂ x ^ i = ∂ L ∂ y i γ \frac{\partial L}{\partial \hat{x}_{i}}=\frac{\partial L}{\partial y_{i}}\frac{\partial y_{i}}{\partial \hat{x}_{i}}=\frac{\partial L}{\partial y_{i}}\gamma ∂x^i∂L=∂yi∂L∂x^i∂yi=∂yi∂Lγ
进一步把损失传播到 x x x所在隐层,这是BN最复杂的推导:
∂ L ∂ x i = ∂ L ∂ x ^ i ∂ x ^ i ∂ x i + ∂ L ∂ σ B 2 ∂ σ B 2 ∂ x i + ∂ L ∂ μ B ∂ μ B ∂ x i \frac{\partial L}{\partial x_{i}}=\frac{\partial L}{\partial \hat{x}_{i}}\frac{\partial \hat{x}_{i}}{\partial x_{i}}+\frac{\partial L}{\partial \sigma_{B}^{2}}\frac{\partial \sigma_{B}^{2}}{\partial x_{i}}+\frac{\partial L}{\partial \mu_{B}}\frac{\partial \mu_{B}}{\partial x_{i}} ∂xi∂L=∂x^i∂L∂xi∂x^i+∂σB2∂L∂xi∂σB2+∂μB∂L∂xi∂μB
这里把 L L L看成关于 x ^ i , σ B 2 , μ B \hat{x}_{i}, \sigma_{B}^{2}, \mu_{B} x^i,σB2,μB的多元复合函数( x ^ i \hat{x}_{i} x^i好理解,为何也把 σ B 2 , μ B \sigma_{B}^{2}, \mu_{B} σB2,μB当做 L L L的自变量有疑问,在我看来 σ B 2 , μ B \sigma_{B}^{2}, \mu_{B} σB2,μB是包含在 x ^ i \hat{x}_{i} x^i中,应该用相乘的链式法则向下写),目前我们只知道 ∂ L ∂ x ^ i \frac{\partial L}{\partial \hat{x}_{i}} ∂x^i∂L,下面求另外五个偏导数
∂ x ^ i ∂ x i = 1 σ B 2 + ϵ \frac{\partial \hat{x}_{i}}{\partial x_{i}} = \frac{1}{\sqrt{\sigma^{2}_{B}+\epsilon}} ∂xi∂x^i=σB2+ϵ1
∂ L ∂ σ B 2 = ∑ k = 1 m ∂ L ∂ x ^ k ∂ x ^ k ∂ σ B 2 = ∑ k = 1 m ∂ L ∂ x ^ k ( x k − μ B ) ( − 1 2 ) ( σ B 2 + ϵ ) − 3 2 \frac{\partial L}{\partial \sigma_{B}^{2}} = \sum\limits_{k=1}^{m}\frac{\partial L}{\partial \hat{x}_{k}}\frac{\partial \hat{x}_{k}}{\partial \sigma_{B}^{2}}=\sum\limits_{k=1}^{m}\frac{\partial L}{\partial \hat{x}_{k}}(x_{k}-\mu_{B})(-\frac{1}{2})(\sigma_{B}^{2}+\epsilon)^{-\frac{3}{2}} ∂σB2∂L=k=1∑m∂x^k∂L∂σB2∂x^k=k=1∑m∂x^k∂L(xk−μB)(−21)(σB2+ϵ)−23
∂ σ B 2 ∂ x i = 2 ( x i − μ m ) m \frac{\partial \sigma_{B}^{2}}{\partial x_{i}} = \frac{2(x_{i}-\mu_{m})}{m} ∂xi∂σB2=m2(xi−μm)
∂ L ∂ μ B = ∑ k = 1 m ∂ L ∂ x ^ k ∂ x ^ k ∂ μ B + ∂ L ∂ σ B 2 ∂ σ B 2 ∂ μ B = ∑ k = 1 m ∂ L ∂ x ^ k ( − 1 σ B 2 + ϵ ) + ∂ L ∂ σ B 2 ∑ k = 1 m − 2 ( x k − μ B ) m \frac{\partial L}{\partial \mu_{B}} = \sum\limits_{k=1}^{m}\frac{\partial L}{\partial \hat{x}_{k}}\frac{\partial \hat{x}_{k}}{\partial \mu_{B}}+ \frac{\partial L}{\partial \sigma_{B}^{2}}\frac{\partial \sigma_{B}^{2}}{\partial \mu_{B}} =\sum\limits_{k=1}^{m}\frac{\partial L}{\partial \hat{x}_{k}}(-\frac{1}{\sqrt{\sigma_{B}^{2}+\epsilon}})+\frac{\partial L}{\partial \sigma_{B}^{2}}\sum\limits_{k=1}^{m}\frac{-2(x_{k}-\mu_{B})}{m} ∂μB∂L=k=1∑m∂x^k∂L∂μB∂x^k+∂σB2∂L∂μB∂σB2=k=1∑m∂x^k∂L(−σB2+ϵ1)+∂σB2∂Lk=1∑mm−2(xk−μB)
∂ μ B ∂ x i = 1 m \frac{\partial \mu_{B}}{\partial x_{i}} = \frac{1}{m} ∂xi∂μB=m1
总结反向传播的公式如下:
传统的CNN由卷积层、池化层和全连接层构成,对于全连接层的批标准化同上,池化层不需要批标准化操作,因此主要有区别的地方就是卷积层的批标准化。关于卷积层的批标准化下面是我引用论文中的原话:
For convolutional layers, we additionally want the normalization to obey the convolutional property – so that different elements of the same feature map, at different locations, are normalized in the same way. To achieve this, we jointly normalize all the activations in a minibatch, over all locations. In Alg. 1, we let B be the set of all values in a feature map across both the elements of a mini-batch and spatial locations – so for a mini-batch of size m and feature maps of size p × q, we use the effective mini-batch of size m′ = |B| = m · p q. We learn a pair of parameters γ(k) and β(k) per feature map, rather than per activation. Alg. 2 is modified similarly, so that during inference the BN transform applies the same linear transformation to each activation in a given feature map.
大体意思就是,在全连接中,一个神经元为一个单位,计算一个均值方差,然后标准化该神经元的值。在卷积层中,我们以一个feature map为一个单位,计算一个均值方差,相当于把batch size从原来的m扩展到m · p · q,其中pq是feature map的尺寸,如果feature map有第三维度,尺寸是d,batch size就是m · p · q · d,然后根据得到均值方差,标准化这个feature map的所有元素值。
综上所述,批标准化,真的是对batch进行的标准化,这和近些年用的越来越少的LRN(Local Response Normalization,局部响应标准化)不一样,LRN并不对batch维度进行标准化,LRN对临近通道的数据进行标准化,具体可以看blog。
参考:
《Batch normalization: accelerating deep network training by reducing internal covariate shift》
https://www.cnblogs.com/guoyaohua/p/8724433.html
https://www.cnblogs.com/34fj/p/8805979.html
指数加权平均:https://www.jianshu.com/p/41218cb5e099?utm_source=oschina-app
批标准化推导:https://zhuanlan.zhihu.com/p/26138673
莫凡视频教程(有比较详细的动画):https://v.youku.com/v_show/id_XMTg1MTYwNDg2OA==
在AlexNet中LRN 局部响应归一化的理解:https://blog.csdn.net/program_developer/article/details/79430119