文章转载自:http://www.cnblogs.com/guoyaohua/p/8724433.htmlhtml
Batch Normalization做为最近一年来DL的重要成果,已经普遍被证实其有效性和重要性。虽然有些细节处理还解释不清其理论缘由,可是实践证实好用才是真的好,别忘了DL从Hinton对深层网络作Pre-Train开始就是一个经验领先于理论分析的偏经验的一门学问。本文是对论文《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》的导读。网络
机器学习领域有个很重要的假设:IID独立同分布假设,就是假设训练数据和测试数据是知足相同分布的,这是经过训练数据得到的模型可以在测试集得到好的效果的一个基本保障。那BatchNorm的做用是什么呢?BatchNorm就是在深度神经网络训练过程当中使得每一层神经网络的输入保持相同分布的。机器学习
接下来一步一步的理解什么是BN。函数
为何深度神经网络随着网络深度加深,训练起来越困难,收敛愈来愈慢?这是个在DL领域很接近本质的好问题。不少论文都是解决这个问题的,好比ReLU激活函数,再好比Residual Network,BN本质上也是解释并从某个不一样的角度来解决这个问题的。学习
从论文名字能够看出,BN是用来解决“Internal Covariate Shift”问题的,那么首先得理解什么是“Internal Covariate Shift”?测试
论文首先说明Mini-Batch SGD相对于One Example SGD的两个优点:梯度更新方向更准确;并行计算速度快;(为何要说这些?由于BatchNorm是基于Mini-Batch SGD的,因此先夸下Mini-Batch SGD,固然也是大实话);而后吐槽下SGD训练的缺点:超参数调起来很麻烦。(做者隐含意思是用BN就能解决不少SGD的缺点)spa
接着引入covariate shift的概念:若是ML系统实例集合<X,Y>中的输入值X的分布总是变,这不符合IID假设,网络模型很难稳定的学规律,这不得引入迁移学习才能搞定吗,咱们的ML系统还得去学习怎么迎合这种分布变化啊。对于深度学习这种包含不少隐层的网络结构,在训练过程当中,由于各层参数不停在变化,因此每一个隐层都会面临covariate shift的问题,也就是在训练过程当中,隐层的输入分布总是变来变去,这就是所谓的“Internal Covariate Shift”,Internal指的是深层网络的隐层,是发生在网络内部的事情,而不是covariate shift问题只发生在输入层。3d
而后提出了BatchNorm的基本思想:能不能让每一个隐层节点的激活输入分布固定下来呢?这样就避免了“Internal Covariate Shift”问题了。orm
BN不是凭空拍脑壳拍出来的好点子,它是有启发来源的:以前的研究代表若是在图像处理中对输入图像进行白化(Whiten)操做的话——所谓白化,就是对输入数据分布变换到0均值,单位方差的正态分布——那么神经网络会较快收敛,那么BN做者就开始推论了:图像是深度神经网络的输入层,作白化能加快收敛,那么其实对于深度网络来讲,其中某个隐层的神经元是下一层的输入,意思是其实深度神经网络的每个隐层都是输入层,不过是相对下一层来讲而已,那么能不能对每一个隐层都作白化呢?这就是启发BN产生的原初想法,而BN也确实就是这么作的,能够理解为对深层神经网络每一个隐层神经元的激活值作简化版本的白化操做。htm
BN的基本思想其实至关直观:由于深层神经网络在作非线性变换前的激活输入值(就是那个x=WU+B,U是输入)随着网络深度加深或者在训练过程当中,其分布逐渐发生偏移或者变更,之因此训练收敛慢,通常是总体分布逐渐往非线性函数的取值区间的上下限两端靠近(对于Sigmoid函数来讲,意味着激活输入值WU+B是大的负值或正值),因此这致使反向传播时低层神经网络的梯度消失,这是训练深层神经网络收敛愈来愈慢的本质缘由,而BN就是经过必定的规范化手段,把每层神经网络任意神经元这个输入值的分布强行拉回到均值为0方差为1的标准正态分布,其实就是把愈来愈偏的分布强制拉回比较标准的分布,这样使得激活输入值落在非线性函数对输入比较敏感的区域,这样输入的小变化就会致使损失函数较大的变化,意思是这样让梯度变大,避免梯度消失问题产生,并且梯度变大意味着学习收敛速度快,能大大加快训练速度。
THAT’S IT。其实一句话就是:对于每一个隐层神经元,把逐渐向非线性函数映射后向取值区间极限饱和区靠拢的输入分布强制拉回到均值为0方差为1的比较标准的正态分布,使得非线性变换函数的输入值落入对输入比较敏感的区域,以此避免梯度消失问题。由于梯度一直都能保持比较大的状态,因此很明显对神经网络的参数调整效率比较高,就是变更大,就是说向损失函数最优值迈动的步子大,也就是说收敛地快。BN说到底就是这么个机制,方法很简单,道理很深入。
上面说得仍是显得抽象,下面更形象地表达下这种调整到底表明什么含义。
图1 几个正态分布
假设某个隐层神经元原先的激活输入x取值符合正态分布,正态分布均值是-2,方差是0.5,对应上图中最左端的浅蓝色曲线,经过BN后转换为均值为0,方差是1的正态分布(对应上图中的深蓝色图形),意味着什么,意味着输入x的取值正态分布总体右移2(均值的变化),图形曲线更平缓了(方差增大的变化)。这个图的意思是,BN其实就是把每一个隐层神经元的激活输入分布从偏离均值为0方差为1的正态分布经过平移均值压缩或者扩大曲线尖锐程度,调整为均值为0方差为1的正态分布。
那么把激活输入x调整到这个正态分布有什么用?首先咱们看下均值为0,方差为1的标准正态分布表明什么含义:
图2 均值为0方差为1的标准正态分布图
这意味着在一个标准差范围内,也就是说64%的几率x其值落在[-1,1]的范围内,在两个标准差范围内,也就是说95%的几率x其值落在了[-2,2]的范围内。那么这又意味着什么?咱们知道,激活值x=WU+B,U是真正的输入,x是某个神经元的激活值,假设非线性函数是sigmoid,那么看下sigmoid(x)其图形:
图3. Sigmoid(x)
及sigmoid(x)的导数为:G’=f(x)*(1-f(x)),由于f(x)=sigmoid(x)在0到1之间,因此G’在0到0.25之间,其对应的图以下:
图4 Sigmoid(x)导数图
假设没有通过BN调整前x的原先正态分布均值是-6,方差是1,那么意味着95%的值落在了[-8,-4]之间,那么对应的Sigmoid(x)函数的值明显接近于0,这是典型的梯度饱和区,在这个区域里梯度变化很慢,为何是梯度饱和区?请看下sigmoid(x)若是取值接近0或者接近于1的时候对应导数函数取值,接近于0,意味着梯度变化很小甚至消失。而假设通过BN后,均值是0,方差是1,那么意味着95%的x值落在了[-2,2]区间内,很明显这一段是sigmoid(x)函数接近于线性变换的区域,意味着x的小变化会致使非线性函数值较大的变化,也便是梯度变化较大,对应导数函数图中明显大于0的区域,就是梯度非饱和区。
从上面几个图应该看出来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操做的理论缘由。
上面是对BN的抽象分析和解释,具体在Mini-Batch SGD下作BN怎么作?其实论文里面这块写得很清楚也容易理解。为了保证这篇文章完整性,这里简单说明下。
假设对于一个深层神经网络来讲,其中两层结构以下:
图5 DNN其中两层
要对每一个隐层神经元的激活值作BN,能够想象成每一个隐层又加上了一层BN操做层,它位于X=WU+B激活值得到以后,非线性函数变换以前,其图示以下:
图6. BN操做
对于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其具体操做流程,如论文中描述的同样:
过程很是清楚,就是上述公式的流程化描述,这里不解释了,直接应该能看懂。
BN在训练的时候能够根据Mini-Batch里的若干训练实例进行激活数值调整,可是在推理(inference)的过程当中,很明显输入就只有一个实例,看不到Mini-Batch其它实例,那么这时候怎么对输入作BN呢?由于很明显一个实例是无法算实例集合求出的均值和方差的。这可如何是好?
既然没有从Mini-Batch数据里能够获得的统计量,那就想其它办法来得到这个统计量,就是均值和方差。能够用从全部训练实例中得到的统计量来代替Mini-Batch里面m个训练实例得到的均值和方差统计量,由于原本就打算用全局的统计量,只是由于计算量等太大因此才会用Mini-Batch这种简化方式的,那么在推理的时候直接用全局统计量便可。
决定了得到统计量的数据范围,那么接下来的问题是如何得到均值和方差的问题。很简单,由于每次作Mini-Batch训练时,都会有那个Mini-Batch里m个训练实例得到的均值和方差,如今要全局统计量,只要把每一个Mini-Batch的均值和方差统计量记住,而后对这些均值和方差求其对应的数学指望便可得出全局统计量,即:
有了均值和方差,每一个隐层神经元也已经有对应训练好的Scaling参数和Shift参数,就能够在推导的时候对每一个神经元的激活数据计算NB进行变换了,在推理过程当中进行BN采起以下方式:
这个公式其实和训练时
是等价的,经过简单的合并计算推导就能够得出这个结论。那么为啥要写成这个变换形式呢?我猜做者这么写的意思是:在实际运行的时候,按照这种变体形式能够减小计算量,为啥呢?由于对于每一个隐层节点来讲:
都是固定值,这样这两个值能够事先算好存起来,在推理的时候直接用就好了,这样比原始的公式每一步骤都现算少了除法的运算过程,乍一看也没少多少计算量,可是若是隐层节点个数多的话节省的计算量就比较多了。
BatchNorm为何NB呢,关键仍是效果好。①不只仅极大提高了训练速度,收敛过程大大加快;②还能增长分类效果,一种解释是这是相似于Dropout的一种防止过拟合的正则化表达方式,因此不用Dropout也能达到至关的效果;③另外调参过程也简单多了,对于初始化要求没那么高,并且可使用大的学习率等。总而言之,通过这么简单的变换,带来的好处多得很,这也是为什么如今BN这么快流行起来的缘由。