赞
踩
Google在ICML文中描述得非常清晰,即在每次SGD时,通过mini-batch来对相应的activation做标准化操作,使得结果(输出信号各个维度)的均值为0,方差为1。而最后的“scale and shift”操作则是为了让因训练所需而“刻意”加入的BN能够有可能还原最初的输入,从而保证整个network的capacity(非线性、表达能力)。
为什么深度神经网络随着网络深度加深,训练起来越困难,收敛越来越慢?这是个在DL领域很接近本质的好问题。很多论文都是解决这个问题的,比如ReLU激活函数,再比如Residual Network,BN本质上也是解释并从某个不同的角度来解决这个问题的。
BN是用来解决“Internal Covariate Shift”(内部协变量偏移)问题的,对于深度学习这种包含很多隐层的网络结构,在训练过程中因为各层参数不停在变化,所以每个隐层都会面临covariate shift的问题,也就是在训练过程中,隐层的输入分布老是变来变去。所以,“Covariate Shift”可以理解为,如果ML系统实例集合<X,Y>中的输入值X的分布老是变,这不符合IID假设,网络模型很难稳定的学规律。所谓的“Internal Covariate Shift”,Internal指的是深层网络的隐层,是发生在网络内部的事情,而不是covariate shift问题只发生在输入层。深层神经网络在做非线性变换前的激活输入值(x2=Wx1+B,x1是输入)随着网络深度加深或者在训练过程中,其分布逐渐发生偏移或者变动,整体分布逐渐往非线性函数的取值区间的上下限两端靠近(对于Sigmoid函数来说,意味着激活输入值Wx1+B是大的负值或正值,其梯度接近0),所以这导致反向传播时低层神经网络的梯度消失。
NOTE:其实对于Sigmoid激活函数,其导数在(0,0.25),多个相乘的值越来越接近0,即使整体分布不往非线性函数的取值区间的上下限两端靠近,本身就很容易造成梯度消失。
BN的想法产生于白化思想,就是对输入数据分布变换到均值为0,方差为1(也可能不是1)的正态(高斯)分布——那么神经网络会较快收敛。其实对于深度网络来说,其中某个隐层的神经元是下一层的输入,意思是深度神经网络的每一个隐层都是输入层,不过是相对下一层来说而已。
基本思想:通过一定的规范化手段,把每层神经网络任意神经元这个输入值的分布强行拉回到均值为0,方差为1(或其它)的正态分布。可以看下面两个图:
如果对于每层的输入x服从正态分布,那么95%的值落在[-2,2]的范围内,对于激活函数sigmoid(x)在这个区间的导数不会接近于0,即梯度不会为0,这样的区域也被称为梯度非饱和区 。经过BN后,目前大部分的值落入非线性激活函数的线性区内(sigmoid函数自变量靠近0的范围内),这样来加速训练收敛过程,不会造成梯度消失问题。
NOTE:仔细想一下,当整体分布被强行拉到均值为0,方差为1时,会有什么问题呢???虽然避免了梯度接近0的情况,解决了梯度消失问题,但是这个操作会削弱模型的非线性能力,也就是标准化使特征数据完全满足正态分布,数据集中在sigmoid激活函数的线性区域。
要对每个隐层神经元的输出做BN,可以想象成每个隐层后又加上了一层BN操作层,它位于X2=W1+B线性变换之后,非线性激活函数变换之前,其图示如下:(比如在卷积神经网络中,BN位于卷积操作和激活函数之间)
目前BN主要分为四种(最后一种是最近提出来的):
前面说过,经过这个变换后某个神经元的激活x形成了均值为0,方差为1的正态分布,目的是把值往后续要进行的非线性变换的线性区拉动,增大导数值,增强反向传播信息流动性,加快训练收敛速度。但是这样会导致网络表达能力下降(即削弱了模型的非线性能力),为了防止这一点,每个神经元增加两个调节参数(scale和shift),这两个参数是通过训练来学习到的,用来对变换后的激活反变换,使得网络表达能力增强,即对变换后的激活进行如下的scale(参数被称为缩放因子)和shift(参数被称为偏移因子)操作,这其实是变换的反操作:
TensorFlow2代码实现:
class Baseline(Model): def __init__(self): super(Baseline, self).__init__() self.c1 = Conv2D(filters=6, kernel_size=(5, 5), padding='same') # 卷积层 self.b1 = BatchNormalization() # BN层 self.a1 = Activation('relu') # 激活层 self.p1 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same') # 池化层 self.d1 = Dropout(0.2) # dropout层 self.flatten = Flatten() self.f1 = Dense(128, activation='relu') self.d2 = Dropout(0.2) self.f2 = Dense(10, activation='softmax') def call(self, x): x = self.c1(x) x = self.b1(x) x = self.a1(x) x = self.p1(x) x = self.d1(x) x = self.flatten(x) x = self.f1(x) x = self.d2(x) y = self.f2(x) return y model = Baseline()
优点:
不足:
一些讨论:
就目前来看,争议的重点在于归一化的位置,还有gamma与beta参数的引入,从理论上分析,论文中的这两个细节实际上并不符合ReLU的特性:ReLU后,数据分布重新回到第一象限,这时是最应当进行归一化的;gamma与beta对sigmoid函数确实能起到一定的作用(实际也不如固定gamma=2),但对于ReLU这种分段线性的激活函数,并不存在sigmoid的低scale呈线性的现象。
有一篇论文否定了BN是改善 内部协变量偏移(Internal Covariate Shift) 的观点,参考论文How Does Batch Normalization Help Optimization?
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。