当前位置:   article > 正文

深度学习归一化方法

深度学习归一化

神经网络学习的本质就是学习数据的分布。如果没有对数据进行归一化处理,那么每一批次训练的数据的分布就有可能不一样。从大的方面来讲,神经网络需要在多个分布中找到一个合适的平衡点;从小的方面来说,由于每层网络的输入数据在不断的变化,这会导致不容易找到合适的平衡点,最终使得构建的神经网络模型不容易收敛。当然,如果只是对输入数据做归一化,这样只能保证数据在输入层是一致的,并不能保证每层网络的输入数据分布是一致的,所以在神经网络模型的中间层也需要加入归一化处理。

本文将介绍4种归一化方法,使用numpy实现归一化过程,并将结果和torch对应的结果对比:

1、BatchNormalization

2、LayerNormalization

3、InstanceNormalization

4、GroupNormalization

目录

一、归一化

二、BatchNormalization

三、LayerNormalization

四、InstanceNormalization

五、GroupNormalization

六、参考

【深度学习】归一化方法 


一、归一化

深度学习归一化原理较为简单,对于输入数据x,计算其均值E[x]和方差Var[x],通过下式进行计算即可。但是需要注意的是,引入了2个可训练参数缩放因子γ和平移因子β,引入的可学习因子提升了模型的表达能力。

在深度学习网络训练过程中,由于数据集量级较大,常常把数据集分批次送入网络训练,那么在BN层计算均值和方差的时候,就没办法一次性计算出整个数据集的均值和方差,在mini batch训练阶段并无太大影响,因为mini batch训练时的方差和均值就是根据mini batch内的数据计算出的,但是当模型用于推理时,使用的均值和方差是训练集的均值和方差,于是需要一个方法来计算数据集的均值和方差,通常使用滑动平均法(momentum一般为0.1)计算全局均值和方差:

\widehat{mean}_{new}=(1-momentum)*\widehat{mean}+momentum*mean_{t}

\widehat{var}_{new}=(1-momentum)*\widehat{var}+momentum*var_{t}

训练和推理阶段的区别:

训练阶段根据mini batch的数据计算均值和方差,并使用滑动平均法计算全局均值和方差;推理阶段使用训练阶段计算的全局均值和方差参与计算。

二、BatchNormalization

这里选择torch.nn.BatchNorm2d为例,对于(N, C, H, W)的特征图(下图可以理解为[N ,C, H*W]),保留通道C计算均值和方差,故有2C个可训练参数(C个γ和C个β),2C个不可训练参数(mean和var)。

 numpy实现BatchNorm2D,并测试与torch.nn.BatchNorm2D是否前向对其,代码如下:

  1. import torch
  2. import numpy as np
  3. def batch_normalize2d(data, alpha=1.0, beta=0.0, eps=1e-5):
  4. u = np.mean(data, axis=(0, 2, 3), keepdims=True) # [1, C, 1, 1]
  5. variance = np.var(data, axis=(0, 2, 3), keepdims=True) # [1, C, 1, 1]
  6. alpha = np.zeros_like(u) + alpha
  7. beta = np.zeros_like(u) + beta
  8. data = alpha * (data - u) / np.sqrt(variance + eps) + beta
  9. return data
  10. np.random.seed(10001)
  11. in_channels = 10
  12. data = (np.random.rand(4, in_channels, 100, 100) * 3 - 1.5).astype('float32')
  13. alpha = 1.0
  14. beta = 0.0
  15. eps = 1e-5
  16. np_normed_data = batch_normalize2d(data, alpha=alpha, beta=beta, eps=eps)
  17. norm_layer = torch.nn.BatchNorm2d(num_features=in_channels, eps=eps, momentum=None)
  18. torch_normed_data = norm_layer(torch.from_numpy(data))
  19. print("implement error:", np.mean(np.abs(np_normed_data - torch_normed_data.detach().numpy())))
  20. assert np.allclose(np_normed_data, torch_normed_data.detach().numpy()), "Normalized value not equal."
  21. # 输出:
  22. # implement error: 3.778329e-08

三、LayerNormalization

注意:LayerNorm的均值和方差是根据单个数据计算的,所以不需要计算全局均值和全局方差

这里选择torch.nn.LayerNorm为例(normalized_shape=[C,H,W]),对于(N, C, H, W)的特征图(下图可以理解为[N ,C, H*W]),保留通道N计算均值和方差。

注意与BatchNorm2d的不同:

当torch.nn.LayerNorm的elementwise_affine为True时,γ和β的参数数量分别时C*H*W

当torch.nn.LayerNorm的elementwise_affine为False时,没有γ和β参数

均值和方差根据输入数据计算,不需要在训练集上用滑动平均方法计算,所以不保存均值和方差。

numpy实现LayerNorm,并测试与torch.nn.LayerNorm是否前向对其,代码如下:

吐槽一下:自己实现的时候γ和β参数的维度和一开始想的不一样(一开始以为和batch_norm2d一样,那应该是2N个可学习参数,实际上是根据元素数量来的),看了源码才发现没有均值和方差这2个参数。

  1. import torch
  2. import numpy as np
  3. def layer_normalize2d(data, alpha=1.0, beta=0.0, eps=1e-5):
  4. u = np.mean(data, axis=(1, 2, 3), keepdims=True) # [N, 1, 1, 1]
  5. variance = np.var(data, axis=(1, 2, 3), keepdims=True) # [N, 1, 1, 1]
  6. alpha = np.zeros(shape=(1,) + data.shape[1:]) + alpha
  7. beta = np.zeros(shape=(1,) + data.shape[1:]) + beta
  8. data = alpha * (data - u) / np.sqrt(variance + eps) + beta
  9. return data
  10. np.random.seed(10001)
  11. in_channels = 10
  12. data = (np.random.rand(4, in_channels, 100, 100) * 3 - 1.5).astype('float32')
  13. alpha = 1.0
  14. beta = 0.0
  15. eps = 1e-5
  16. np_normed_data = layer_normalize2d(data, alpha=alpha, beta=beta, eps=eps)
  17. norm_layer = torch.nn.LayerNorm(normalized_shape=data.shape[1:], eps=eps, elementwise_affine=True)
  18. torch_normed_data = norm_layer(torch.from_numpy(data))
  19. print("LayerNorm weight shape: ", norm_layer.weight.shape, ", bias shape: ", norm_layer.bias.shape)
  20. print("implement error:", np.mean(np.abs(np_normed_data - torch_normed_data.detach().numpy())))
  21. assert np.allclose(np_normed_data, torch_normed_data.detach().numpy()), "Normalized value not equal."
  22. # 输出:
  23. # LayerNorm weight shape: torch.Size([10, 100, 100]) , bias shape: torch.Size([10, 100, 100])
  24. # implement error: 6.919199475696921e-08

四、InstanceNormalization

此处以torch.nn.InstanceNorm2d(track_running_stats=True, affine=True)为例,对于(N, C, H, W)的特征图(下图可以理解为[N ,C, H*W]),保留通道N和C计算均值和方差。

相信有很多朋友都是根据下面这张图来理解Instance Norm,一眼看上去mean和variance的维度应该是[N, C],但是打印出runing_mean和runing_var维度一看,竟然是[C]!What?

好家伙,经过多次测试,终于搞明白了,原来在归一化计算(一、归一化  中的公式)的时候,均值和方差维度是[N, C],但是因为batch_size可能会变化,所以running_mean和running_var保存的时候把[N,C]的均值和方差取了个均值,所以runing_mean和runing_var维度是[C]。

 numpy实现InstanceNorm,并测试与torch.nn.InstanceNorm2d是否前向对其,代码如下:

吐槽一下:维度的问题网上找了很久没找到想要的答案,最后一下一下试出来的。

  1. import torch
  2. import numpy as np
  3. def instance_normalize2d(data, alpha=1.0, beta=0.0, eps=1e-5):
  4. u = np.mean(data, axis=(2, 3), keepdims=True) # [N, C, 1, 1]
  5. variance = np.var(data, axis=(2, 3), keepdims=True) # [N, C, 1, 1]
  6. alpha = np.zeros_like(u) + alpha
  7. beta = np.zeros_like(u) + beta
  8. # print(u[:, :, 0, 0].mean(axis=0), variance[:, :, 0, 0].mean(axis=0))
  9. data = alpha * (data - u) / np.sqrt(variance + eps) + beta
  10. return data
  11. np.random.seed(10001)
  12. in_channels = 10
  13. data = (np.random.rand(4, in_channels, 100, 100) * 3 - 1.5).astype('float32')
  14. alpha = 1.0
  15. beta = 0.0
  16. eps = 1e-5
  17. np_normed_data = instance_normalize2d(data, alpha=alpha, beta=beta, eps=eps)
  18. norm_layer = torch.nn.InstanceNorm2d(num_features=data.shape[1], eps=eps, momentum=1.0, track_running_stats=True, affine=True)
  19. torch_normed_data = norm_layer(torch.from_numpy(data))
  20. print("running_mean shape:", norm_layer.running_mean.shape, "running_var shape:", norm_layer.running_var.shape)
  21. print("weight shape:", norm_layer.weight.shape, "bias shape:", norm_layer.bias.shape)
  22. print("implement error:", np.mean(np.abs(np_normed_data - torch_normed_data.detach().numpy())))
  23. assert np.allclose(np_normed_data, torch_normed_data.detach().numpy()), "Normalized value not equal."
  24. # 输出:
  25. # running_mean shape: torch.Size([10]) running_var shape: torch.Size([10])
  26. # weight shape: torch.Size([10]) bias shape: torch.Size([10])
  27. # implement error: 3.8745e-08

五、GroupNormalization

InstanceNorm就是GroupNorm的特例,当Group Norm分组和channels相同时,就是instance norm,当分组为1时,就是LayerNorm。

GroupNorm可以参考上方的InstanceNorm和LayerNorm修改。

六、参考

【深度学习】归一化方法 

BatchNorm2d — PyTorch 1.10 documentation

GroupNorm — PyTorch 1.10 documentation

InstanceNorm2d — PyTorch 1.10 documentation

LayerNorm — PyTorch 1.10 documentation

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/670923
推荐阅读
相关标签
  

闽ICP备14008679号