深度学习—BN的理解(⼀)
0、问题
  机器学习领域有个很重要的假设:IID独⽴同分布假设,就是假设训练数据和测试数据是满⾜相同分布的,这是通过训练数据获得的模型能够在测试集获得好的效果的⼀个基本保障。那BatchNorm的作⽤是什么呢?BatchNorm就是在深度神经⽹络训练过程中使得每⼀层神经⽹络的输⼊保持相同分布的。
  思考⼀个问题:为什么传统的神经⽹络在训练开始之前,要对输⼊的数据做Normalization?原因在于神经⽹络学习过程本质上是为了学习数据的分布,⼀旦训练数据与测试数据的分布不同,那么⽹络的泛化能⼒也⼤⼤降低;另⼀⽅⾯,⼀旦在mini-batch梯度下降训练的时候,每批训练数据的分布不相同,那么⽹络就要在每次迭代的时候去学习以适应不同的分布,这样将会⼤⼤降低⽹络的训练速度,这也正是为什么我们需要对所有训练数据做⼀个Normalization预处理的原因。
  为什么深度神经⽹络随着⽹络深度加深,训练起来越困难,收敛越来越慢?这是个在DL领域很接近本质的好问题。很多论⽂都是解决这个问题的,⽐如ReLU激活函数,再⽐如Residual Network,BN本质上也是解释并从某个不同的⾓度来解决这个问题的。
  结论:BN 层在激活函数之前。BN层的作⽤机制也许是通过平滑隐藏层输⼊的分布,帮助随机梯度下降
的进⾏,缓解随机梯度下降权重更新对后续层的负⾯影响。因此,实际上,⽆论是放⾮线性激活之前,还是之后,也许都能发挥这个作⽤。只不过,取决于具体激活函数的不同,效果也许有⼀点差别(⽐如,对sigmoid和tanh⽽⾔,放⾮线性激活之前,也许顺便还能缓解sigmoid/tanh的梯度衰减问题,⽽对ReLU⽽⾔,这个平滑作⽤经ReLU“扭曲”之后也许有所衰弱)。
  (1)sigmoid、tanh 激活函数。函数图像的两端,相对于x的变化,y的变化都很⼩(这其实很正常,毕竟tanh就是拉伸过的sigmoid)。也就是说,容易出现梯度衰减的问题。那么,如果在tanh或sigmoid之前,进⾏⼀些normalization处理,就可以缓解梯度衰减的问题。我想这可能也是最初的BN论⽂选择把BN层放在⾮线性激活之前的原因。
  (2)relu激活函数。所以,现在我们假设所有的激活都是relu,也就是使得负半区的卷积值被抑制,正半区的卷积值被保留。⽽bn的作⽤是使得输⼊值的均值为0,⽅差为1,也就是说假如relu之前是bn的话,会有接近⼀半的输⼊值被抑制,⼀半的输⼊值被保留。
  所以bn放到relu之前的好处可以这样理解:bn可以防⽌某⼀层的激活值全部都被抑制,从⽽防⽌从这⼀层往前传的梯度全都变成0,也就是防⽌梯度消失。(当然也可以防⽌梯度爆炸)
1、“Internal Covariate Shift”问题
  从论⽂名字可以看出,BN是⽤来解决“Internal Covariate Shift”问题的,那么⾸先得理解什么是“Internal Covariate Shift”?
  论⽂⾸先说明Mini-Batch SGD相对于One Example SGD的两个优势:梯度更新⽅向更准确;并⾏计算速度快;(为什么要说这些?因为BatchNorm是基于Mini-Batch SGD的,所以先夸下Mini-Batch SGD,当然也是⼤实话);然后吐槽下SGD训练的缺点:超参数调起来很⿇烦。(作者隐含意思是⽤BN就能解决很多SGD的缺点)
  接着引⼊covariate shift的概念:如果ML系统实例集合<X,Y>中的输⼊值X的分布⽼是变,这不符合IID假设,⽹络模型很难稳定的学规律,这不得引⼊迁移学习才能搞定吗,我们的ML系统还得去学习怎么迎合这种分布变化啊。对于深度学习这种包含很多隐层的⽹络结构,在训练过程中,因为各层参数不停在变化,所以每个隐层都会⾯临covariate shift的问题,也就是在训练过程中,隐层的输⼊分布⽼是变来变去,这就是所谓的“Internal Covariate Shift”,Internal指的是深层⽹络的隐层,是发⽣在⽹络内部的事情,⽽不是covariate shift问题只发⽣在输⼊层。
  然后提出了BatchNorm的基本思想:能不能让每个隐层节点的激活输⼊分布固定下来呢?这样就避免了“Internal Covariate Shift”问题了,顺带解决反向传播中梯度消失问题。BN 其实就是在做 feature scaling,⽽且它的⽬的也是为了在训练的时候避免这种 Internal Covariate Shift 的问题,只是刚好也解决了 sigmoid 函数梯度消失的问题。
  BN不是凭空拍脑袋拍出来的好点⼦,它是有启发来源的:之前的研究表明如果在图像处理中对输⼊图像进⾏⽩化(Whiten)操作的话——所谓⽩化,就是对输⼊数据分布变换到0均值,单位⽅差的正态分布——那么神经⽹络会较快收敛,那么BN作者就开始推论了:图像是深度神经⽹络的输⼊层,做⽩化能加快收敛,那么其实对于深度⽹络来说,其中某个隐层的神经元是下⼀层的输⼊,意思是其实深度神经⽹络的每⼀个隐层都是输⼊层,不过是相对下⼀层来说⽽已,那么能不能对每个隐层都做⽩化呢?这就是启发BN产⽣的原初想法,⽽BN也确实就是这么做的,可以理解为对深层神经⽹络每个隐层神经元的激活值做简化版本的⽩化操作。
2、BatchNorm的本质思想
  BN的基本思想其实相当直观:因为深层神经⽹络在做⾮线性变换前的激活输⼊值(就是那个x=WU+B,U是输⼊)随着⽹络深度加深或者在训练过程中,其分布逐渐发⽣偏移或者变动,之所以训练收敛慢,⼀般是整体分布逐渐往⾮线性函数的取值区间的上下限两端靠近(对于Sigmoid函数来说,意味着激活输⼊值WU+B是⼤的负值或正值),所以这导致反向传播时低层神经⽹络的梯度消失,这是训练深层神经⽹络收敛越来越慢的本质原因,⽽BN就是通过⼀定的规范化⼿段,把每层神经⽹络任意神经元这个输⼊值的分布强⾏拉回到均值为0⽅差为1的标准正态分布,其实就是把越来越偏的分布强制拉回⽐较标准的分布,这样使得激活输⼊值落在⾮线性函数对输⼊⽐较敏感的区域,这样输⼊的⼩变化就会导致损失函数较⼤的变化,意思是这样让梯度变⼤,避免梯度消失问题产⽣,⽽且梯度变⼤意味着学
习收敛速度快,能⼤⼤加快训练速度。
  THAT’S IT。其实⼀句话就是:对于每个隐层神经元,把逐渐向⾮线性函数映射后向取值区间极限饱和区靠拢的输⼊分布强制拉回到均值为0⽅差为1的⽐较标准的正态分布,使得⾮线性变换函数的输⼊值落⼊对输⼊⽐较敏感的区域,以此避免梯度消失问题。因为梯度⼀直都能保持⽐较⼤的状态,所以很明显对神经⽹络的参数调整效率⽐较⾼,就是变动⼤,就是说向损失函数最优值迈动的步⼦⼤,也就是说收敛地快。BN说到底就是这么个机制,⽅法很简单,道理很深刻。
  从上⾯⼏个图应该看出来BN在⼲什么了吧?其实就是把隐层神经元激活输⼊x=WU+B从变化不拘⼀格的正态分布通过BN操作拉回到了均值为0,⽅差为1的正态分布,即原始正态分布中⼼左移或者右移到以0为均值,拉伸或者缩减形态形成以1为⽅差的图形。什么意思?就是说经过BN后,⽬前⼤部分Activation的值落⼊⾮线性函数的线性区内,其对应的导数远离导数饱和区,这样来加速训练收敛过程。
  但是很明显,看到这⾥,稍微了解神经⽹络的读者⼀般会提出⼀个疑问:如果都通过BN,那么不就跟把⾮线性函数替换成线性函数效果相同了?这意味着什么?我们知道,如果是多层的线性函数变换其实这个深层是没有意义的,因为多层线性⽹络跟⼀层线性⽹络是等价的。这意味着⽹络的表达能⼒下降了,这也意味着深度的意义就没有了。所以BN为了保证⾮线性的获得,对变换后的满⾜均值为0⽅差为1的x⼜进⾏了scale加上shift操作(y=scale*x+shift),每个神经元增加了两个参数scale和shift参数,这两
个参数是通过训练学习到的,意思是通过scale和shift把这个值从标准正态分布左移或者右移⼀点并长胖⼀点或者变瘦⼀点,每个实例挪动的程度不⼀样,这样等价于⾮线性函数的值从正中⼼周围的线性区往⾮线性区动了动。
  核⼼思想应该是想到⼀个线性和⾮线性的较好平衡点,既能享受⾮线性的较强表达能⼒的好处,⼜避免太靠⾮线性区两头使得⽹络收敛速度太慢。当然,这是我的理解,论⽂作者并未明确这样说。但是很明显这⾥的scale和shift操作是会有争议的,因为按照论⽂作者论⽂⾥写的理想状态,就会⼜通过scale和shift操作把变换后的x调整回未变换的状态,那不是饶了⼀圈⼜绕回去原始的“Internal Covariate Shift”问题⾥去了吗,感觉论⽂作者并未能够清楚地解释scale和shift操作的理论原因。
3、训练阶段如何做BatchNorm
  对于Mini-Batch SGD来说,⼀次训练过程⾥⾯包含m个训练实例,其具体BN操作就是对于隐层内每个神经元的激活值来说,进⾏如下变换:
  要注意,这⾥t层某个神经元的x(k)不是指原始输⼊,就是说不是t-1层每个神经元的输出,⽽是t层这个神经元的线性激活x=WU+B,这⾥的U才是t-1层神经元的输出。变换的意思是:某个神经元对应的原始的激活x通过减去mini-Batch内m个实例获得的m个激活x求得的均值E(x)并除以求得的⽅差Var(x)来进⾏转换。
正则化可以防止过拟合  上⽂说过经过这个变换后某个神经元的激活x形成了均值为0,⽅差为1的正态分布,⽬的是把值往后续要进⾏的⾮线性变换的线性区拉动,增⼤导数值,增强反向传播信息流动性,加快训练收敛速度。但是这样会导致⽹络表达能⼒下降,为了防⽌这⼀点,每个神经元增加两个调节参数(scale和shift),这两个参数是通过训练来学习到的,⽤来对变换后的激活反变换,使得⽹络表达能⼒增强,即对变换后的激活进⾏如下的scale和shift操作,这其实是变换的反操作:
  BN其具体操作流程,如论⽂中描述的⼀样:
  ⾛⼀遍Batch Normalization⽹络层的前向传播过程。
4、BatchNorm的推理(Inference)过程
  BN在训练的时候可以根据Mini-Batch⾥的若⼲训练实例进⾏激活数值调整,但是在推理(inference)的过程中,很明显输⼊就只有⼀个实例,看不到Mini-Batch其它实例,那么这时候怎么对输⼊做BN呢?因为很明显⼀个实例是没法算实例集合求出的均值和⽅差的。这可如何是好?既然没有从Mini-Batch数据⾥可以得到的统计量,那就想其它办法来获得这个统计量,就是均值和⽅差。可以⽤从所有训练实例中获得的统计量来代替Mini-Batch⾥⾯m个训练实例获得的均值和⽅差统计量,因为本来就打算⽤全局的统计量,只是因为计算量等太⼤所以才会⽤Mini-Batch这种简化⽅式的,那么在推理的时候直接⽤全局统计量即可。
  决定了获得统计量的数据范围,那么接下来的问题是如何获得均值和⽅差的问题。很简单,因为每次做Mini-Batch训练时,都会有那个Mini-Batch⾥m个训练实例获得的均值和⽅差,现在要全局统计量,只要把每个Mini-Batch的均值和⽅差统计量记住,然后对这些均值和⽅差求其对应的数学期望即可得出全局统计量
5、BatchNorm的好处
  BatchNorm为什么NB呢,关键还是效果好。
①不仅仅极⼤提升了训练速度,收敛过程⼤⼤加快;
②还能增加分类效果,⼀种解释是这是类似于Dropout的⼀种防⽌过拟合的正则化表达⽅式,所以不⽤Dropout也能达到相当的效果;
③另外调参过程也简单多了,对于初始化要求没那么⾼,⽽且可以使⽤⼤的学习率等。
总⽽⾔之,经过这么简单的变换,带来的好处多得很,这也是为何现在BN这么快流⾏起来的原因。
6、tensorflow中的BN
  为了activation能更有效地使⽤输⼊信息,所以⼀般BN放在激活函数之前。
  ⼀个batch⾥的128个图,经过⼀个64 kernels卷积层处理,得到了128×64个图,再针对每⼀个kernel所对应的128个图,求它们所有像素的mean和variance,因为总共有64个kernels,输出的结果就是⼀个⼀维长度64的数组啦!最后输出是(64,)的数组向量。
参考⽂献:郭耀华博客:t/developer/article/1157136
     zhuanlan.zhihu/p/36222443

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。