Batch Normalization
Batch Normalization 学习笔记
原文地址:http://blog.csdn.net/hjimce/article/details/50866313
作者:hjimce
一、背景意义
本篇博文主要讲解2015年深度学习领域,非常值得学习的一篇文献:《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》,这个算法目前已经被大量的应用,最新的文献算法很多都会引用这个算法,进行网络训练,可见其强大之处非同一般啊。
近年来深度学习捷报连连、声名鹊起,随机梯度下架成了训练深度网络的主流方法。尽管随机梯度下降法对于训练深度网络简单高效,但是它有个毛病,就是需要我们人为的去选择参数,比如学习率、参数初始化、权重衰减系数、Drop out比例等。这些参数的选择对训练结果至关重要,以至于我们很多时间都浪费在这些的调参上。那么学完这篇文献之后,你可以不需要那么刻意的慢慢调整参数。BN算法(Batch Normalization)其强大之处如下:
(1)你可以选择比较大的初始学习率,让你的训练速度飙涨。以前还需要慢慢调整学习率,甚至在网络训练到一半的时候,还需要想着学习率进一步调小的比例选择多少比较合适,现在我们可以采用初始很大的学习率,然后学习率的衰减速度也很大,因为这个算法收敛很快。当然这个算法即使你选择了较小的学习率,也比以前的收敛速度快,因为它具有快速训练收敛的特性;
(2)你再也不用去理会过拟合中drop out、L2正则项参数的选择问题,采用BN算法后,你可以移除这两项了参数,或者可以选择更小的L2正则约束参数了,因为BN具有提高网络泛化能力的特性;
(3)再也不需要使用使用局部响应归一化层了(局部响应归一化是Alexnet网络用到的方法,搞视觉的估计比较熟悉),因为BN本身就是一个归一化网络层;
(4)可以把训练数据彻底打乱(防止每批训练的时候,某一个样本都经常被挑选到,文献说这个可以提高1%的精度,这句话我也是百思不得其解啊)。
开始讲解算法前,先来思考一个问题:我们知道在神经网络训练开始前,都要对输入数据做一个归一化处理,那么具体为什么需要归一化呢?归一化后有什么好处呢?原因在于神经网络学习过程本质就是为了学习数据分布,一旦训练数据与测试数据的分布不同,那么网络的泛化能力也大大降低;另外一方面,一旦每批训练数据的分布各不相同(batch 梯度下降),那么网络就要在每次迭代都去学习适应不同的分布,这样将会大大降低网络的训练速度,这也正是为什么我们需要对数据都要做一个归一化预处理的原因。
对于深度网络的训练是一个复杂的过程,只要网络的前面几层发生微小的改变,那么后面几层就会被累积放大下去。一旦网络某一层的输入数据的分布发生改变,那么这一层网络就需要去适应学习这个新的数据分布,所以如果训练过程中,训练数据的分布一直在发生变化,那么将会影响网络的训练速度。
我们知道网络一旦train起来,那么参数就要发生更新,除了输入层的数据外(因为输入层数据,我们已经人为的为每个样本归一化),后面网络每一层的输入数据分布是一直在发生变化的,因为在训练的时候,前面层训练参数的更新将导致后面层输入数据分布的变化。以网络第二层为例:网络的第二层输入,是由第一层的参数和input计算得到的,而第一层的参数在整个训练过程中一直在变化,因此必然会引起后面每一层输入数据分布的改变。我们把网络中间层在训练过程中,数据分布的改变称之为:“Internal Covariate Shift”。Paper所提出的算法,就是要解决在训练过程中,中间层数据分布发生改变的情况,于是就有了Batch Normalization,这个牛逼算法的诞生。
二、初识BN(Batch Normalization)
1、BN概述
就像**函数层、卷积层、全连接层、池化层一样,BN(Batch Normalization)也属于网络的一层。在前面我们提到网络除了输出层外,其它层因为低层网络在训练的时候更新了参数,而引起后面层输入数据分布的变化。这个时候我们可能就会想,如果在每一层输入的时候,再加个预处理操作那该有多好啊,比如网络第三层输入数据X3(X3表示网络第三层的输入数据)把它归一化至:均值0、方差为1,然后再输入第三层计算,这样我们就可以解决前面所提到的“Internal Covariate Shift”的问题了。
而事实上,paper的算法本质原理就是这样:在网络的每一层输入的时候,又插入了一个归一化层,也就是先做一个归一化处理,然后再进入网络的下一层。不过文献归一化层,可不像我们想象的那么简单,它是一个可学习、有参数的网络层。既然说到数据预处理,下面就先来复习一下最强的预处理方法:白化。
2、预处理操作选择
说到神经网络输入数据预处理,最好的算法莫过于白化预处理。然而白化计算量太大了,很不划算,还有就是白化不是处处可微的,所以在深度学习中,其实很少用到白化。经过白化预处理后,数据满足条件:a、特征之间的相关性降低,这个就相当于pca;b、数据均值、标准差归一化,也就是使得每一维特征均值为0,标准差为1。如果数据特征维数比较大,要进行PCA,也就是实现白化的第1个要求,是需要计算特征向量,计算量非常大,于是为了简化计算,作者忽略了第1个要求,仅仅使用了下面的公式进行预处理,也就是近似白化预处理:
公式简单粗糙,但是依旧很牛逼。因此后面我们也将用这个公式,对某一个层网络的输入数据做一个归一化处理。需要注意的是,我们训练过程中采用batch 随机梯度下降,上面的E(xk)指的是每一批训练数据神经元xk的平均值;然后分母就是每一批数据神经元xk**度的一个标准差了。
三、BN算法实现
1、BN算法概述
经过前面简单介绍,这个时候可能我们会想当然的以为:好像很简单的样子,不就是在网络中间层数据做一个归一化处理嘛,这么简单的想法,为什么之前没人用呢?然而其实实现起来并不是那么简单的。其实如果是仅仅使用上面的归一化公式,对网络某一层A的输出数据做归一化,然后送入网络下一层B,这样是会影响到本层网络A所学习到的特征的。打个比方,比如我网络中间某一层学习到特征数据本身就分布在S型**函数的两侧,你强制把它给我归一化处理、标准差也限制在了1,把数据变换成分布于s函数的中间部分,这样就相当于我这一层网络所学习到的特征分布被你搞坏了,这可怎么办?于是文献使出了一招惊天地泣鬼神的招式:变换重构,引入了可学习参数γ、β,这就是算法关键之处:
每一个神经元xk都会有一对这样的参数γ、β。这样其实当:
、
是可以恢复出原始的某一层所学到的特征的。因此我们引入了这个可学习重构参数γ、β,让我们的网络可以学习恢复出原始网络所要学习的特征分布。最后Batch Normalization网络层的前向传导过程公式就是:
上面的公式中m指的是mini-batch size。
2、源码实现
- m = K.mean(X, axis=-1, keepdims=True)#计算均值
- std = K.std(X, axis=-1, keepdims=True)#计算标准差
- X_normed = (X - m) / (std + self.epsilon)#归一化
- out = self.gamma * X_normed + self.beta#重构变换
3、实战使用
(1)可能学完了上面的算法,你只是知道它的一个训练过程,一个网络一旦训练完了,就没有了min-batch这个概念了。测试阶段我们一般只输入一个测试样本,看看结果而已。因此测试样本,前向传导的时候,上面的均值u、标准差σ 要哪里来?其实网络一旦训练完毕,参数都是固定的,这个时候即使是每批训练样本进入网络,那么BN层计算的均值u、和标准差都是固定不变的。我们可以采用这些数值来作为测试样本所需要的均值、标准差,于是最后测试阶段的u和σ 计算公式如下:
上面简单理解就是:对于均值来说直接计算所有batch u值的平均值;然后对于标准偏差采用每个batch σB的无偏估计。最后测试阶段,BN的使用公式就是:
(2)根据文献说,BN可以应用于一个神经网络的任何神经元上。文献主要是把BN变换,置于网络**函数层的前面。在没有采用BN的时候,**函数层是这样的:
z=g(Wu+b)
也就是我们希望一个**函数,比如s型函数s(x)的自变量x是经过BN处理后的结果。因此前向传导的计算公式就应该是:
z=g(BN(Wu+b))
其实因为偏置参数b经过BN层后其实是没有用的,最后也会被均值归一化,当然BN层后面还有个β参数作为偏置项,所以b这个参数就可以不用了。因此最后把BN层+**函数层就变成了:
z=g(BN(Wu))
四、Batch Normalization在CNN中的使用
通过上面的学习,我们知道BN层是对于每个神经元做归一化处理,甚至只需要对某一个神经元进行归一化,而不是对一整层网络的神经元进行归一化。既然BN是对单个神经元的运算,那么在CNN中卷积层上要怎么搞?假如某一层卷积层有6个特征图,每个特征图的大小是100*100,这样就相当于这一层网络有6*100*100个神经元,如果采用BN,就会有6*100*100个参数γ、β,这样岂不是太恐怖了。因此卷积层上的BN使用,其实也是使用了类似权值共享的策略,把一整张特征图当做一个神经元进行处理。
卷积神经网络经过卷积后得到的是一系列的特征图,如果min-batch sizes为m,那么网络某一层输入数据可以表示为四维矩阵(m,f,p,q),m为min-batch sizes,f为特征图个数,p、q分别为特征图的宽高。在cnn中我们可以把每个特征图看成是一个特征处理(一个神经元),因此在使用Batch Normalization,mini-batch size 的大小就是:m*p*q,于是对于每个特征图都只有一对可学习参数:γ、β。说白了吧,这就是相当于求取所有样本所对应的一个特征图的所有神经元的平均值、方差,然后对这个特征图神经元做归一化。下面是来自于keras卷积层的BN实现一小段主要源码:
- input_shape = self.input_shape
- reduction_axes = list(range(len(input_shape)))
- del reduction_axes[self.axis]
- broadcast_shape = [1] * len(input_shape)
- broadcast_shape[self.axis] = input_shape[self.axis]
- if train:
- m = K.mean(X, axis=reduction_axes)
- brodcast_m = K.reshape(m, broadcast_shape)
- std = K.mean(K.square(X - brodcast_m) + self.epsilon, axis=reduction_axes)
- std = K.sqrt(std)
- brodcast_std = K.reshape(std, broadcast_shape)
- mean_update = self.momentum * self.running_mean + (1-self.momentum) * m
- std_update = self.momentum * self.running_std + (1-self.momentum) * std
- self.updates = [(self.running_mean, mean_update),
- (self.running_std, std_update)]
- X_normed = (X - brodcast_m) / (brodcast_std + self.epsilon)
- else:
- brodcast_m = K.reshape(self.running_mean, broadcast_shape)
- brodcast_std = K.reshape(self.running_std, broadcast_shape)
- X_normed = ((X - brodcast_m) /
- (brodcast_std + self.epsilon))
- out = K.reshape(self.gamma, broadcast_shape) * X_normed + K.reshape(self.beta, broadcast_shape)
个人总结:2015年个人最喜欢深度学习的一篇paper就是Batch Normalization这篇文献,采用这个方法网络的训练速度快到惊人啊,感觉训练速度是以前的十倍以上,再也不用担心自己这破电脑每次运行一下,训练一下都要跑个两三天的时间。另外这篇文献跟空间变换网络《Spatial Transformer Networks》的思想神似啊,都是一个变换网络层。
参考文献:
1、《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》
2、《Spatial Transformer Networks》
3、https://github.com/fchollet/keras
**********************作者:hjimce 时间:2016.3.12 联系QQ:1393852684 原创文章,转载请保留作者、原文地址信息********************
************************************************************************************************************************************
以下是另一篇关于BN的博客,重点在BN的反向传播公式推导,在此转载!
一:BN层
BN(Batch Normalization)层的作用
(1)加速收敛(2)控制过拟合,可以少用或不用Dropout和正则(3)降低网络对初始化权重不敏感(4)允许使用较大的学习率
链接:https://www.zhihu.com/question/38102762/answer/85238569
来源:知乎
顾名思义,batch normalization嘛,就是“批规范化”咯。Google在ICML文中描述的非常清晰,即在每次SGD时,通过mini-batch来对相应的activation做规范化操作,使得结果(输出信号各个维度)的均值为0,方差为1. 而最后的“scale and shift”操作则是为了让因训练所需而“刻意”加入的BN能够有可能还原最初的输入(即当),从而保证整个network的capacity。(有关capacity的解释:实际上BN可以看作是在原模型上加入的“新操作”,这个新操作很大可能会改变某层原来的输入。当然也可能不改变,不改变的时候就是“还原原来输入”。如此一来,既可以改变同时也可以保持原输入,那么模型的容纳能力(capacity)就提升了。)
关于DNN中的normalization,大家都知道白化(whitening),只是在模型训练过程中进行白化操作会带来过高的计算代价和运算时间。因此本文提出两种简化方式:1)直接对输入信号的每个维度做规范化(“normalize each scalar feature independently”);2)在每个mini-batch中计算得到mini-batch mean和variance来替代整体训练集的mean和variance. 这便是Algorithm 1.
关于DNN中的normalization,大家都知道白化(whitening),只是在模型训练过程中进行白化操作会带来过高的计算代价和运算时间。因此本文提出两种简化方式:1)直接对输入信号的每个维度做规范化(“normalize each scalar feature independently”);2)在每个mini-batch中计算得到mini-batch mean和variance来替代整体训练集的mean和variance. 这便是Algorithm 1.
2. How to Batch Normalize?怎样学BN的参数在此就不赘述了,就是经典的chain rule:
3. Where to use BN?
BN可以应用于网络中任意的activation set。文中还特别指出在CNN中,BN应作用在非线性映射前,即对做规范化。另外对CNN的“权值共享”策略,BN还有其对应的做法(详见文中3.2节)。
4. Why BN?
好了,现在才是重头戏--为什么要用BN?BN work的原因是什么?
说到底,BN的提出还是为了克服深度神经网络难以训练的弊病。其实BN背后的insight非常简单,只是在文章中被Google复杂化了。
首先来说说“Internal Covariate Shift”。文章的title除了BN这样一个关键词,还有一个便是“ICS”。大家都知道在统计机器学习中的一个经典假设是“源空间(source domain)和目标空间(target domain)的数据分布(distribution)是一致的”。如果不一致,那么就出现了新的机器学习问题,如,transfer learning/domain adaptation等。而covariate shift就是分布不一致假设之下的一个分支问题,它是指源空间和目标空间的条件概率是一致的,但是其边缘概率不同,即:对所有,,但是.
大家细想便会发现,的确,对于神经网络的各层输出,由于它们经过了层内操作作用,其分布显然与各层对应的输入信号分布不同,而且差异会随着网络深度增大而增大,可是它们所能“指示”的样本标记(label)仍然是不变的,这便符合了covariate shift的定义。由于是对层间信号的分析,也即是“internal”的来由。
那么好,为什么前面我说Google将其复杂化了。其实如果严格按照解决covariate shift的路子来做的话,大概就是上“importance weight”(ref)之类的机器学习方法。可是这里Google仅仅说“通过mini-batch来规范化某些层/所有层的输入,从而可以固定每层输入信号的均值与方差”就可以解决问题。如果covariate
shift可以用这么简单的方法解决,那前人对其的研究也真真是白做了。此外,试想,均值方差一致的分布就是同样的分布吗?当然不是。显然,ICS只是这个问题的“包装纸”嘛,仅仅是一种high-level demonstration。
那BN到底是什么原理呢?说到底还是为了防止“梯度弥散”。关于梯度弥散,大家都知道一个简单的栗子:。在BN中,是通过将activation规范为均值和方差一致的手段使得原本会减小的activation的scale变大。可以说是一种更有效的local
response normalization方法(见4.2.1节)。
5. When to use BN?
OK,说完BN的优势,自然可以知道什么时候用BN比较好。例如,在神经网络训练时遇到收敛速度很慢,或梯度爆炸等无法训练的状况时可以尝试BN来解决。另外,在一般使用情况下也可以加入BN来加快训练速度,提高模型精度。
二:LN层
LN提出:BN针对一个minibatch的输入样本,计算均值和方差,基于计算的均值和方差来对某一层神经网络的输入X中每一个case进行归一化操作。但BN有两个明显不足:1、高度依赖于mini-batch的大小,实际使用中会对mini-Batch大小进行约束,不适合类似在线学习(mini-batch为1)情况;2、不适用于RNN网络中normalize操作:BN实际使用时需要计算并且保存某一层神经网络mini-batch的均值和方差等统计信息,对于对一个固定深度的前向神经网络(DNN,CNN)使用BN,很方便;但对于RNN来说,sequence的长度是不一致的,换句话说RNN的深度不是固定的,不同的time-step需要保存不同的statics特征,可能存在一个特殊sequence比其的sequence长很多,这样training时,计算很麻烦。但LN可以有效解决上面这两个问题。
LN怎么做:
LN是基于BN转化而来的,所以还是先从BN谈起。
对于一个多层前向神经网络中的某一层Hi,计算方式如公式(1)所示:
针对深度学习,存在“covariate shift”(具体定义见BN介绍文章)现象,因此需要通过normalize操作,使Hi层的输入拥有固定的均值和方差,以此削弱协方差偏移现象对深度网络的训练时的影响,加快网络收敛。normalize的对Hi层输入进行变换,如公式(2)所示:
直接使用公式(2)进行normalize不现实,因为需要针对整个trainingset来进行计算,因此,BN通过mini-batch的输入样本近似的计算normalize中的均值和方差,因此成为batch-Normalization。
与BN不同,LN是针对深度网络的某一层的所有神经元的输入按公式(3)进行normalize操作:
由此可见BN与LN的不同之处在于:LN中同层神经元输入拥有相同的均值和方差,不同的输入样本有不同的均值和方差;而BN中则针对不同神经元输入计算均值和方差,同一个minibatch中的输入拥有相同的均值和方差。因此,LN不依赖于mini-batch的大小和输入sequence的深度,因此可以用于bath-size为1和RNN中对边长的输入sequence的normalize操作。
RNN中的LN操作与公式(3)稍有不同,计算公式如(4)所示:
在传统RNN中,recurrent unit经过累加求和后的输入(summed input)的量级会随着训练进行发生波动,导致梯度爆炸或梯度消失发生。加入LN之后,Normalization term会对summed input的进行尺度变换,使RNN在training和inference时更加稳定。
实践证明,LN用于RNN进行Normalization时,取得了比BN更好的效果。但用于CNN时,效果并不如BN明显。
3:WN层
对深度学习网络权值W进行normalization的操作公式如下:
通过一个标量g和一个向量V对权重向量W进行尺度变换。标量g的值一般为||W||,即权重W的大小。
更新公式如下:
如此便解耦了权重向量的范数和方向,加速了收敛,保证了gradient的稳定,不会爆炸或者消失;同时解决了BN的数据依赖性,计算开销也降低了许多;相比于BN,该方法没能将每一层特征尺度固定住,因此作者设计了一种初始化方法,在初始化时利用了mini-batch的信息,保证了features在初始化时为0均值,1方差。可以通过向量V的大小||V||的变化对权重W进行norm约束,使得网络参数训练相对Learning rate更加鲁棒,即可以选择一个更大的学习速率来加速网络训练。
推荐阅读
-
浅谈tensorflow中dataset.shuffle和dataset.batch dataset.repeat注意点
-
基于mybatis batch实现批量提交大量数据
-
tensorflow tf.train.batch之数据批量读取方式
-
使用Tensorflow将自己的数据分割成batch训练实例
-
TensorFlow实现Batch Normalization
-
tensorflow中next_batch的具体使用
-
详解Tensorflow数据读取有三种方式(next_batch)
-
tensorflow入门:TFRecordDataset变长数据的batch读取详解
-
如何让FasterTransformer支持动态batch和动态sequence length
-
TensorFlow dataset.shuffle、batch、repeat的使用详解