Batch Normalization(批量归一化)是深度学习中经常用到的一种归一化方法
我们知道 Sigmoid 函数在定义域为 $(-\infty,-4) \cup (4,\infty)$ 内导数趋于 0,由于容易出现梯度消失的现象,因此 ReLU 函数使用的较多
但在某些场合不可避免的要去使用 Sigmoid 函数,因此我们希望能把输入的 $x$ 值控制在有效的区间内,进行一个等效变化,使这些值均匀的分布在 0 附近,这样输入到 Sigmoid 函数后就能大概率避免梯度消失
如下面左侧的图所示,$x_1$ 的值处于一个比较小的区间 $x_2$ 的值处于一个比较大的区间,因此对于 $w_2$ 来说,少量的变化就会对 Loss 产生急剧的变化,而对 $w_1$ 来说,变化就会相对小一点,可以看下面的 “等高线” 图,沿着纵轴方向变化的话,等高线会急剧的变化,沿着横轴方向变化,等高线的变化就稍平缓一点
但如果如右图所示,输入的值区间都很接近,这样 $w_1$ 和 $w_2$ 对 Loss 的影响就比较接近,就会形成一种 “圆形” 路径,在这种情况进行搜索的时候,不管从哪个点出发,梯度的方向都是指向全局最小解的方向,这种搜索过程会比较快一些,而且更稳定
Batch Normalization 较多的应用于两个方面
- Image Normalization,例如对 RGB 三通道进行 Normalization,将数据进行统一缩放
- normalize = transforms.Normalize(mean=ean[0.485, 0.456, 0.406],
- std=[0.229, 0.224, 0.225])
mean 有三个值,分别对应 RGB 三个通道的均值,具体的 Normalize 过程就是
$$ \begin{align*} x_R&= \frac{x_R-0.485}{0.229} \\ x_G&= \frac{x_G-0.456}{0.224} \\ x_B&= \frac{x_B-0.406}{0.225} \\ \end{align*} $$
常见的 Normalization
目前常见的 Normalization 有四种,将输入的图像 shape 记为 [N,C,H,W],这几个方法主要区别是:
- BatchNorm:batch 方向做归一化,计算 NHW 的均值,对小 batchsize 效果不好;(BN 主要缺点是对 batchsize 的大小比较敏感,由于每次计算均值和方差是在一个 batch 上,所以如果 batchsize 太小,则计算的均值、方差不足以代表整个数据分布)
- LayerNorm:channel 方向做归一化,计算 CHW 的均值;(对 RNN 作用明显)
- InstanceNorm:一个 batch,一个 channel 内做归一化。计算 HW 的均值,用在风格化迁移;(因为在图像风格化中,生成结果主要依赖于某个图像实例,所以对整个 batch 归一化不适合图像风格化中,因而对 HW 做归一化。可以加速模型收敛,并且保持每个图像实例之间的独立)
- GroupNorm:将 channel 方向分 group,然后每个 group 内做归一化,算 (C//G) HW 的均值;这样与 batchsize 无关,不受其约束
详述 Batch Normalization
如下图,输入数据是 6 张 3 通道 784 个像素点的数据,将其分到三个通道上,在每个通道上也就是 [6,784] 的数据,然后分别得到和通道数一样多的统计数据均值 $\mu$ 和标准差 $\sigma$。将每个像素值减去 $\mu$,除以 $\sigma$ 就变换成了近似于 $N (0,1)$ 分布的数据,之后再用参数 $\gamma$ 和 $\beta$ 将其变化到近似 $N (\beta, \gamma)$ 的分布
$\mu$ 和 $\sigma$ 只是样本中的统计数据,是没有梯度信息的,不过会保存在运行参数里。而 $\gamma$ 和 $\beta$ 属于训练的参数,是有梯度信息的
Batch Normalize 的规范化写法为前三步是 batch normalization 的工序,经过这三步以后数据就近似于标准正态分布 $N (0,1)$,但是后面的公式还有一个反向操作,将 normalize 后的数据再平移和扩展,让数据近似于 $N (\beta, \gamma)$,这位为了让神经网络自己去学着使用和修改这两个扩展参数,这样神经网络就能自己慢慢琢磨出前面的 normalization 操作到底有没有起到优化的做哟个,如果没有起到优化的作用,就用 $\gamma$ 和 $\beta$ 来抵消一些 normalization 的操作
下面具体看一下在 PyTorch 中如何实现 Batch Normalize
- import torch
- import torch.nn as nn
- import torch.nn.functional as F
-
- # 随机生成一个Batch的模拟,100张16通道784像素点的数据
- # 均匀分布U(0~1)
- x = torch.rand(128, 16, 784)
- # 将28*28变为打平为一维的784
- layer = nn.BatchNorm1d(16)
- # Batch Normalization层,因为输入是将高度H和宽度W合成了一个维度,所以这里用1d
- # 因为Batch Norm的参数直接是由channel数量得来的,因此这里直接给定了channel的数量为16,后续会输出16个channel的统计信息
- out = layer(x) # f orward
-
- print(out.shape)
- print(layer.running_mean) # 全局的均值
- print(layer.running_var) # 全局的方差
运行结果
- tensor([0.0500, 0.0498, 0.0499, 0.0501, 0.0500, 0.0502, 0.0500, 0.0500, 0.0500,
- 0.0500, 0.0499, 0.0501, 0.0500, 0.0499, 0.0499, 0.0502])
- tensor([0.9084, 0.9084, 0.9083, 0.9084, 0.9084, 0.9083, 0.9083, 0.9083, 0.9083,
- 0.9083, 0.9084, 0.9084, 0.9083, 0.9083, 0.9083, 0.9083])
注意 layer.running_mean
和 layer.running_var
得到的是全局的均值和方差,不是当前 Batch 上的,只不过这里只跑了一个 Batch 而已所以它就是这个 Batch 上的。现在还没有办法直接查看某个 Batch 上的这两个统计量的值
- x = torch.randn(1, 16, 7, 7) # 1张16通道的7乘7的图像
-
- # Batch Normalization层,因为输入是有高度H和宽度W的,所以这里用2d
- layer = nn.BatchNorm2d(16) # 传入通道数
- out = layer(x)
-
- print(out.shape)
- print(layer.running_mean) # 全局的均值
- print(layer.running_var) # 全局的方差
- print(layer.weight) # weight也就是前面公式里的gamma
- print(layer.bias) # bias也就是前面公式里的beta
运行结果
- torch.Size([1, 16, 7, 7])
- tensor([ 0.0187, 0.0125, -0.0032, 0.0032, 0.0034, 0.0031, 0.0231, -0.0024,
- 0.0002, 0.0194, -0.0097, 0.0177, 0.0324, -0.0013, 0.0128, -0.0086])
- tensor([0.9825, 0.9799, 0.9984, 0.9895, 0.9992, 0.9809, 0.9919, 0.9769, 0.9928,
- 0.9949, 1.0055, 1.0368, 0.9867, 0.9904, 1.0097, 0.9910])
- Parameter containing:
- tensor([0.5925, 0.5662, 0.1066, 0.0073, 0.9517, 0.0476, 0.0416, 0.2041, 0.8666,
- 0.6467, 0.7665, 0.0300, 0.9050, 0.8024, 0.2816, 0.1745],
- requires_grad=True)
- Parameter containing:
- tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
- requires_grad=True)
注意这里的 layer.weight
和 layer.bias
是当前 batch 上的
如果在定义层时使用了参数 affine=False
,那么就是固定 $\gamma = 1$ 和 $\beta=0$ 不自动学习,这时参数 layer.weight
和 layer.bias
将是 None
总结
使用了 $\gamma$ 和 $\beta$ 之后,最后得到的分布是往 $N (\beta, \gamma)$ 上靠的,而不是往 $N (0, 1)$ 上靠的
使用了 Batch Normalization 让 Converge(收敛)的速度加快了,这个可以直观理解,使用了靠近 0 的部分的 Sigmoid 激活,其梯度信息更大了。并且能够得到一个更好的解
提升了 Robust(鲁棒性),这使得网络更加稳定,这可以从最前面第二张图所示来直观理解,如果参数有大有小,解空间像左边一样,那么稍微调整学习率可能就发生抖动(如图中左侧椭圆解空间上下方向走,且学习率太大时)或者训练速度太慢(如图中右侧椭圆解空间左右方向走,且学习率太小时)。这让超参数的调整没有那么敏感
你好,请问 batchnorm1d 如果想对例子中的第三维度 784 进行归一化,该如何操作呢?不同维度的归一化对防止过拟合这个作用有影响吗?