BatchNormalization、LayerNormalization、InstanceNorm、GroupNorm、SwitchableNorm总结

本篇博客总结几种归一化办法,并给出相应计算公式和代码。python


一、综述

1.1 论文连接

一、Batch Normalizationgit

https://arxiv.org/pdf/1502.03167.pdfgithub

二、Layer Normalizaitonweb

https://arxiv.org/pdf/1607.06450v1.pdf算法

三、Instance Normalization网络

https://arxiv.org/pdf/1607.08022.pdfsvg

https://github.com/DmitryUlyanov/texture_nets学习

四、Group Normalization测试

https://arxiv.org/pdf/1803.08494.pdfspa

五、Switchable Normalization

https://arxiv.org/pdf/1806.10779.pdf

https://github.com/switchablenorms/Switchable-Normalization

1.2 介绍

归一化层,目前主要有这几个方法,Batch Normalization(2015年)、Layer Normalization(2016年)、Instance Normalization(2017年)、Group Normalization(2018年)、Switchable Normalization(2018年);

将输入的图像shape记为[N, C, H, W],这几个方法主要的区别就是在,

  • batchNorm是在batch上,对NHW作归一化,对小batchsize效果很差;
  • layerNorm在通道方向上,对CHW归一化,主要对RNN做用明显;
  • instanceNorm在图像像素上,对HW作归一化,用在风格化迁移;
  • GroupNorm将channel分组,而后再作归一化;
  • SwitchableNorm是将BN、LN、IN结合,赋予权重,让网络本身去学习归一化层应该使用什么方法。

这里写图片描述

二、Batch Normalization

首先,在进行训练以前,通常要对数据作归一化,使其分布一致,可是在深度神经网络训练过程当中,一般以送入网络的每个batch训练,这样每一个batch具备不一样的分布;此外,为了解决internal covarivate shift问题,这个问题定义是随着batch normalizaiton这篇论文提出的,在训练过程当中,数据分布会发生变化,对下一层网络的学习带来困难。

因此batch normalization就是强行将数据拉回到均值为0,方差为1的正太分布上,这样不只数据分布一致,并且避免发生梯度消失。

此外,internal corvariate shift和covariate shift是两回事,前者是网络内部,后者是针对输入数据,好比咱们在训练数据前作归一化等预处理操做。

这里写图片描述

算法过程:

  • 沿着通道计算每一个batch的均值u
  • 沿着通道计算每一个batch的方差σ^2
  • 对x作归一化,x’=(x-u)/开根号(σ^2+ε)
  • 加入缩放和平移变量γ和β ,归一化后的值,y=γx’+β

加入缩放平移变量的缘由是:保证每一次数据通过归一化后还保留原有学习来的特征,同时又能完成归一化操做,加速训练。 这两个参数是用来学习的参数。

import numpy as np

def Batchnorm(x, gamma, beta, bn_param):

    # 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=True0)
    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

三、Layer Normalizaiton

batch normalization存在如下缺点:

  • 对batchsize的大小比较敏感,因为每次计算均值和方差是在一个batch上,因此若是batchsize过小,则计算的均值、方差不足以表明整个数据分布;
  • BN实际使用时须要计算而且保存某一层神经网络batch的均值和方差等统计信息,对于对一个固定深度的前向神经网络(DNN,CNN)使用BN,很方便;但对于RNN来讲,sequence的长度是不一致的,换句话说RNN的深度不是固定的,不一样的time-step须要保存不一样的statics特征,可能存在一个特殊sequence比其余sequence长不少,这样training时,计算很麻烦。(参考于http://www.noobyard.com/article/p-vpvwfdhy-mv.html

与BN不一样,LN是针对深度网络的某一层的全部神经元的输入按如下公式进行normalize操做。

这里写图片描述

BN与LN的区别在于:

  • LN中同层神经元输入拥有相同的均值和方差,不一样的输入样本有不一样的均值和方差;
  • BN中则针对不一样神经元输入计算均值和方差,同一个batch中的输入拥有相同的均值和方差。

    因此,LN不依赖于batch的大小和输入sequence的深度,所以能够用于batchsize为1和RNN中对边长的输入sequence的normalize操做。

LN用于RNN效果比较明显,可是在CNN上,不如BN。

def ln(x, b, s):
    _eps = 1e-5
    output = (x - x.mean(1)[:,None]) / tensor.sqrt((x.var(1)[:,None] + _eps))
    output = s[None, :] * output + b[None,:]
    return output

用在四维图像上,

def Layernorm(x, gamma, beta):

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

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

四、Instance Normalization

BN注重对每一个batch进行归一化,保证数据分布一致,由于判别模型中结果取决于数据总体分布。

可是图像风格化中,生成结果主要依赖于某个图像实例,因此对整个batch归一化不适合图像风格化中,于是对HW作归一化。能够加速模型收敛,而且保持每一个图像实例之间的独立。

公式:

这里写图片描述

代码:

def Instancenorm(x, gamma, beta):

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

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

五、Group Normalization

主要是针对Batch Normalization对小batchsize效果差,GN将channel方向分group,而后每一个group内作归一化,算(C//G)*H*W的均值,这样与batchsize无关,不受其约束。

公式:

这里写图片描述

伪代码:

这里写图片描述

代码:

def GroupNorm(x, gamma, beta, G=16):

    # x_shape:[B, C, H, W]
    results = 0.
    eps = 1e-5
    x = np.reshape(x, (x.shape[0], G, x.shape[1]/16, x.shape[2], x.shape[3]))

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

六、Switchable Normalization

本篇论文做者认为,

  • 第一,归一化虽然提升模型泛化能力,然而归一化层的操做是人工设计的。在实际应用中,解决不一样的问题原则上须要设计不一样的归一化操做,并无一个通用的归一化方法可以解决全部应用问题;
  • 第二,一个深度神经网络每每包含几十个归一化层,一般这些归一化层都使用一样的归一化操做,由于手工为每个归一化层设计操做须要进行大量的实验。

所以做者提出自适配归一化方法——Switchable Normalization(SN)来解决上述问题。与强化学习不一样,SN使用可微分学习,为一个深度网络中的每个归一化层肯定合适的归一化操做。

公式:

这里写图片描述

这里写图片描述

这里写图片描述

代码:

def SwitchableNorm(x, gamma, beta, w_mean, w_var):
    # x_shape:[B, C, H, W]
    results = 0.
    eps = 1e-5

    mean_in = np.mean(x, axis=(2, 3), keepdims=True)
    var_in = np.var(x, axis=(2, 3), keepdims=True)

    mean_ln = np.mean(x, axis=(1, 2, 3), keepdims=True)
    var_ln = np.var(x, axis=(1, 2, 3), keepdims=True)

    mean_bn = np.mean(x, axis=(0, 2, 3), keepdims=True)
    var_bn = np.var(x, axis=(0, 2, 3), keepdims=True)

    mean = w_mean[0] * mean_in + w_mean[1] * mean_ln + w_mean[2] * mean_bn
    var = w_var[0] * var_in + w_var[1] * var_ln + w_var[2] * var_bn

    x_normalized = (x - mean) / np.sqrt(var + eps)
    results = gamma * x_normalized + beta
    return results

结果比较:

这里写图片描述
这里写图片描述