深度学习中的规范化

这篇文章介绍深度学习四种主流的规范化, 分别是Batch Normalization(BN[9]), Layer Normalization(LN[7]), Instance Normalization(IN[8])以及Group Normalization(GN[2])。html

1. 做用

为啥用Normalization? 这是由于训练深度神经网络会收敛很慢,颇有可能发生梯度弥散或者梯度爆炸。用了Normalization能够训练得很快,学习更好。python

2. 作法

给定输入\(x\),Normalization的处理方式能够由下面这个公式形容,不过各类Normalization对x的指望以及方差的求法不一样。这个公式能够分两个部分,第一个部分是\(\frac{x-\mathrm{E}[x]}{\sqrt{\operatorname{Var}[x]+\epsilon}}\)是对activation进行规范化操做,将activation变为均值为0,方差为1的正态分布,而最后的“scale and shift”\((\gamma,\beta)\)操做则是为了让因训练所需而“刻意”加入的规范化可以有可能还原最初的输入, 保证模型的能力。[1]
\[y=\frac{x-\mathrm{E}[x]}{\sqrt{\operatorname{Var}[x]+\epsilon}} * \gamma+\beta\]
下面这张图[2]很直观地解释了各类Normalization处理张量的不一样之处。
1.jpg-54kB
给定一个四维张量\(x\),四维依次表明\([batchsize, channel, height, width]\), 简单起见,表示为\((N, C, H, W)\),上图中,张量的第三个维度以及第四个维度合起来组成了一个维度,方便展现。网络

\(BN\):能够看到BN是以C为滑动轴,对BHW三个维度求和取平均,因此指望的维度是\((1, C, 1, 1)\),而后利用指望求出方差。dom

\(LN\): LN与BN恰好是垂直的位置,是以B为滑动轴,对CHW三个维度求和取平均,指望的维度是\((N, 1, 1, 1)\)性能

\(IN\): IN则是LN和BN的交汇,以B和C双轴滑动,对HW两个维度求和取平均,指望的维度是\((N, C, 1, 1)\)学习

\(GN\): GN则是IN和LN的一种折中考虑,对C维度进行了分组,上图中是分红了两组,因此最后指望的维度是\((N, 2, 1, 1)\)测试

3. 原理与使用

深度神经网络中的Normalization最早是出如今AlexNet网络中的LRN(local response normalization), 而LRN计算的是像素局部的统计量,对加速收敛没有什么做用。开山加速收敛的Normalization方法是BN,那么它是怎么加速收敛的呢?首先要弄清楚为何没有BN,收敛会慢,对于一个深层网络来讲,会发生梯度弥散, 这样在反向传播更新梯度时,会更新得很是慢,收敛也会变得慢,而BN将原来要变小的activation经过规范化操做,使activation的尺度变大,这样就消除了梯度弥散而致使参数更新慢的影响。spa

BN训练阶段与测试阶段:训练阶段的指望和方差经过当前批数据进行计算,\(\gamma\)\(\beta\)则是BN层的可学习参数,因为BN层会减去指望,因此前一层是不必加上偏置的。在测试阶段,通常是单例forward,对单例求指望和方差是无心义的,因此BN的测试阶段的指望和方差是训练时每一个批次的指望和方差的累计移动平均或者指数移动平均求得的[3][4][6],找了一个简单的BN训练实现,详细见[6]。.net

import numpy as np

def Batchnorm(x, gamma, beta, bn_param, momentum=0.1):

    # x_shape:[B, C, H, W]
    running_mean = bn_param['running_mean']
    running_var = bn_param['running_var']
    results = 0.
    eps = 1e-5

    x_mean = np.mean(x, axis=(0, 2, 3), keepdims=True)
    x_var = np.var(x, axis=(0, 2, 3), keepdims=True)
    x_normalized = (x - x_mean) / np.sqrt(x_var + eps)
    results = gamma * x_normalized + beta

    # 由于在测试时是单个图片测试,这里保留训练时的均值和方差,用在后面测试时用
    running_mean = momentum * running_mean + (1 - momentum) * x_mean
    running_var = momentum * running_var + (1 - momentum) * x_var

    bn_param['running_mean'] = running_mean
    bn_param['running_var'] = running_var

    return results, bn_param

从BN的训练阶段中知道,BN严重依赖批数据,经过批数据的统计信息来近似估计全局的统计信息,而在测试阶段,没有进行统计信息的计算,而是经过训练阶段的统计信息来估计新数据,当新数据来自未知的domain(风格迁移将每张图片看成一个domain,图像的生成结果主要依赖于某个图像实例,BN统计的近似全局信息并不会给任务带来收益,反而会弱化实例之间的特殊性[5]),训练的统计信息就用处不那么大了,另外大网络的大batchsize很占用GPU显存,对于缺乏多GPU的人来讲,这是很差办的,而减少batchsize会使计算的指望与方差不能表明总体分布,网络性能就会大大折扣。code

为了消除batch的影响,LN,IN,GN就出现了。这三个规范化操做均对于batch都是不敏感的。

  • BN是针对不一样神经元层计算指望和方差,同一个batch有相同的指望和方差。
  • LN是针对同层神经元计算指望和方差,不一样样本有不一样的指望和方差。
  • IN是不一样样本的不一样神经元层有不一样的指望和方差。
  • GN是不一样样本不一样分组有不一样的指望和方差。

这也致使了它们的用途不一样。BN统计的是数据的总体分布,判别模型的结果主要取决于数据的总体分布,因此BN常常用于固定深度的DNN,CNN中。

对于RNN来讲,序列的长度是不一致的,也就是深度不固定,不一样时间保存的统计信息不一样,这对于固定批次的BN是计算很麻烦的。而LN与输入序列的长度是没有关系的,所以LN在RNN中效果明显,但在cnn中不如BN。以下图所示[7],LSTM+LN更快收敛,学习得更好。
image_1d85h60r7m6ommj1k8ql4m15fq2e.png-73.6kB
在图像风格化任务中,生成结果主要依赖于单个图像实例,因此这类任务用BN并不合适,但能够对HW作规范化,能够加速模型收敛[6][8]。

GN根据传统的特征提取器组合特征的思路(例如HOG根据orientation分组),对channel进行分组,每一层的都有不少卷积核,被核学习到的特征不是彻底独立的,有的特征可能属于频率,还有的属于形状,亮度等等,所以对特征进行分组处理是天然的思路,最后的结果也很好,与BN的效果相差无几,但对batch是无依赖的,适合小批量任务[2]。下图是BN与GN的对比效果。
image_1d852df6j11hu1t0atf18ab1n1t11.png-54.8kB

具体使用参考见pytorch官方文档

参考

[1] 魏秀参的回答
[2] GNpaper
[3] YJango的BN文章
[4] BatchNorm源码
[5] Naiyang Wang的回答
[6] liuxiao的博客
[7] LNpaper
[8] INpaper
[9] BNpaper

相关文章
相关标签/搜索