深度网络中常用的normalization总结

今天看了些常用的normalization方法,顺便做个笔记记录一下。

Local Response Normalization

LRN最早在AlexNet中出现,它的定义如下,其中$a_{x,y}^i$代表第i个channel,位置为(x,y)的激活值,使用与它在channel上邻近的(-n/2,n/2)个激活值对其进行normalize。$k,\alpha,\beta$都是预先定义好的超参数。

但是在vgg的那篇论文中,作者在sec 4.1中提到,使用LRN对结果并没有多大的提升。

Batch Normalization

Gradient Vanishing

深度网络在训练的时候往往存在梯度弥散的问题,尤其是当网络较深的时候,如果使用的是sigmoid激活函数,当每一层的输入绝对值较大的时候,会使输出进入函数的饱和区,在这个区域梯度就会非常小,再反向传播的时候由于连乘,最终传到前面的梯度可能就等于0了。一般为了解决这个问题可以采用这几种方式:

  1. 使用ReLU激活
  2. 设计一个好的参数初始化策略
  3. 使用较小的学习速率

可以预见,如果我们可以稳定每一层输入的分布,则可以很大程度的避免训练的时候进入饱和区,加快网络的收敛。 下面来看一下它的具体做法:

1.首先计算输出的tensor每一维度的均值和方差,对于全连接层,我们的tensor维度一般为[N,D],其中N为mini-batch的size,我们沿着dimension计算会得到一个D维的mean和一个D维的var。如果位于卷积层,那么输入的维度一般为[N,H,W,C],其中C为filter的数量,这时候是沿着filter所在的这一维进行计算mean和std,得到两个C维的向量。然后使用公式$\hat{x}^{(k)}=\frac{x^{(k)}-E[x^{(k)}]}{\sqrt{Var[x^{(k)}]}}$对每个维度进行归一化。下图分别给出了两种情况下计算mean和var的方式。 2. 通常,我们会把bn放置于全连接或卷积层之后,激活函数之前 3. 如果单纯的对每层进行normalization,可能会破坏网络的表达能力。比如如果使用sigmoid激活,normalization之后会使输入大部分位于线性区域,从而丢失非线性映射的能力,为了避免这个问题,引入了两个可以学习的参数$\gamma,\beta$对normalization后的输出进行放缩和平移:$y^{(k)}=\gamma^{(k)}\hat{x}^{(k)}+\beta^{(k)}$。$\gamma,\beta$的shape同之前讨论的mean和var。 4. 在训练的时候会使用滑动平均的方式维护一个全局的mean和variance,在测试的时候直接使用这个全局的mean和variance进行bn操作

Group Normalization

Batch normalization有一个问题就是当batch size很小时(很多检测分割还有video的任务中batch size只有1或2),会造成一个batch内的mean和variance的统计不准确,从而使最终的结果变差。在GN中作者说在ResNet-50中,当batch size为2时,使用GN比BN错误率低了10.6%。下面这张图也给出了GN和BN随着batch size变化错误率的变化情况,可以看到GN基本不受batch size变化的影响,而BN当batch size变小时错误率急剧上升。

之前,为了解决这个问题,有人提出了synchronized BN,就是说如果我们用8卡训练,每张卡的batch size=2,那么计算mean和variance的时候跨GPU进行计算,这样相当于就把batch size变成了16。这从一定程度上缓解了这一问题,但是这对系统设计提出了新的要求,毕竟需要在不同的卡之间进行同步操作,同时也会使异步梯度下降(asynchronous solvers)无法工作了。

GN的原理和BN其实非常的像,只是GN会把所有的channels分成G个group,每个group内有C/G个channels。在计算mean和variance的时候是沿着(H,W),同时在每张图片的一个C/G个channels的group内计算。因为跟batch size无关了,所以就不需要像bn那样维护两个统计信息,mean和variance供inference的时候使用了。其他的部分和bn基本相同,也有两个可学习的参数$\gamma,\beta$对normalization的参数进行放缩和平移处理。代码的话实现如下:

def GroupNorm(x, gamma, beta, G, eps=1e􀀀5):
    # x: input features with shape [N,C,H,W]
    # gamma, beta: scale and offset, with shape [1,C,1,1]
    # G: number of groups for GN
    N, C, H, W = x.shape
    x = tf.reshape(x, [N, G, C // G, H, W])
    mean, var = tf.nn.moments(x, [2, 3, 4], keep_dims=True)
    x = (x * mean) / tf.sqrt(var + eps)
    x = tf.reshape(x, [N, C, H, W])
    return x * gamma + beta

从下面这张图也可以看出几种常见的normalization方法的区别,其中layer norm和instance norm可以看作是group norm的特例,一个是group的数量取1,这样所有的channels都在一个group内进行计算,另一个则是group取C,也就是每个channel单独计算mean和variance。


References

  1. Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift
  2. Group Normalization
  3. LRN
  4. 全面解读Group Normalization(知乎)