目录
6.1 请问这里讲的batch normalization和之前讲的xavier有什么区别之分?
6.5 batch_size是把显存占满好?还是利用率到100%?
1. 背景和基本原理,以及对它到底作用的猜测
2. 从零实现Batch normalization
下面,我们从头开始实现一个具有张量的批量规范化层。
import torch
from torch import nn
from d2l import torch as d2l
def batch_norm(X, gamma, beta, moving_mean, moving_var, eps, momentum):
''' 通过is_grad_enabled来判断当前模式是训练模式还是预测模式 '''
if not torch.is_grad_enabled():
''' 如果是在预测模式下,直接使用传入的移动平均所得的均值和方差 '''
X_hat = (X - moving_mean) / torch.sqrt(moving_var + eps)
else:
assert len(X.shape) in (2, 4)
if len(X.shape) == 2:
''' 使用全连接层的情况,计算特征维上的均值和方差 '''
mean = X.mean(dim=0)
var = ((X - mean) ** 2).mean(dim=0)
else:
''' 使用二维卷积层的情况,计算通道维上(axis=1)的均值和方差。 '''
''' 这里我们需要保持X的形状以便后面可以做广播运算 '''
mean = X.mean(dim=(0, 2, 3), keepdim=True)
var = ((X - mean) ** 2).mean(dim=(0, 2, 3), keepdim=True) '''
''' 训练模式下,用当前的均值和方差做标准化
X_hat = (X - mean) / torch.sqrt(var + eps)
''' 更新移动平均的均值和方差 '''
moving_mean = momentum * moving_mean + (1.0 - momentum) * mean
moving_var = momentum * moving_var + (1.0 - momentum) * var
Y = gamma * X_hat + beta ''' 缩放和移位 '''
return Y, moving_mean.data, moving_var.data
我们现在可以创建一个正确的BatchNorm
层。 这个层将保持适当的参数:拉伸gamma
和偏移beta
,这两个参数将在训练过程中更新。 此外,我们的层将保存均值和方差的移动平均值,以便在模型预测期间随后使用。
撇开算法细节,注意我们实现层的基础设计模式。 通常情况下,我们用一个单独的函数定义其数学原理,比如说batch_norm
。 然后,我们将此功能集成到一个自定义层中,其代码主要处理数据移动到训练设备(如GPU)、分配和初始化任何必需的变量、跟踪移动平均线(此处为均值和方差)等问题。 为了方便起见,我们并不担心在这里自动推断输入形状,因此我们需要指定整个特征的数量。 不用担心,深度学习框架中的批量规范化API将为我们解决上述问题,我们稍后将展示这一点。
class BatchNorm(nn.Module):
''' num_features:完全连接层的输出数量或卷积层的输出通道数。 '''
''' num_dims:2表示完全连接层,4表示卷积层 '''
def __init__(self, num_features, num_dims):
super().__init__()
if num_dims == 2:
shape = (1, num_features)
else:
shape = (1, num_features, 1, 1)
''' 参与求梯度和迭代的拉伸和偏移参数,分别初始化成1和0 '''
self.gamma = nn.Parameter(torch.ones(shape))
self.beta = nn.Parameter(torch.zeros(shape))
''' 非模型参数的变量初始化为0和1 '''
self.moving_mean = torch.zeros(shape)
self.moving_var = torch.ones(shape)
def forward(self, X):
''' 如果X不在内存上,将moving_mean和moving_var '''
''' 复制到X所在显存上 '''
if self.moving_mean.device != X.device:
self.moving_mean = self.moving_mean.to(X.device)
self.moving_var = self.moving_var.to(X.device)
''' 保存更新过的moving_mean和moving_var '''
Y, self.moving_mean, self.moving_var = batch_norm(
X, self.gamma, self.beta, self.moving_mean,
self.moving_var, eps=1e-5, momentum=0.9)
return Y
3. 使用批量规范化层的 LeNet
为了更好理解如何应用BatchNorm
,下面我们将其应用于LeNet模型。 回想一下,批量规范化是在卷积层或全连接层之后、相应的激活函数之前应用的。
net = nn.Sequential(
nn.Conv2d(1, 6, kernel_size=5), BatchNorm(6, num_dims=4), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, kernel_size=5), BatchNorm(16, num_dims=4), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),
nn.Linear(16*4*4, 120), BatchNorm(120, num_dims=2), nn.Sigmoid(),
nn.Linear(120, 84), BatchNorm(84, num_dims=2), nn.Sigmoid(),
nn.Linear(84, 10))
和以前一样,我们将在Fashion-MNIST数据集上训练网络。 这个代码与我们第一次训练LeNet时几乎完全相同,主要区别在于学习率大得多。
lr, num_epochs, batch_size = 1.0, 10, 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
4. 简明实现
除了使用我们刚刚定义的BatchNorm
,我们也可以直接使用深度学习框架中定义的BatchNorm
。 该代码看起来几乎与我们上面的代码相同。
net = nn.Sequential(
nn.Conv2d(1, 6, kernel_size=5), nn.BatchNorm2d(6), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, kernel_size=5), nn.BatchNorm2d(16), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2), nn.Flatten(),
nn.Linear(256, 120), nn.BatchNorm1d(120), nn.Sigmoid(),
nn.Linear(120, 84), nn.BatchNorm1d(84), nn.Sigmoid(),
nn.Linear(84, 10))
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
5. 争议
直观地说,批量规范化被认为可以使优化更加平滑。 然而,我们必须小心区分直觉和对我们观察到的现象的真实解释。 回想一下,我们甚至不知道简单的神经网络(多层感知机和传统的卷积神经网络)为什么如此有效。 即使在暂退法和权重衰减的情况下,它们仍然非常灵活,因此无法通过常规的学习理论泛化保证来解释它们是否能够泛化到看不见的数据。
在提出批量规范化的论文中,作者除了介绍了其应用,还解释了其原理:通过减少内部协变量偏移(internal covariate shift)。 据推测,作者所说的“内部协变量转移”类似于上述的投机直觉,即变量值的分布在训练过程中会发生变化。 然而,这种解释有两个问题: 1、这种偏移与严格定义的协变量偏移(covariate shift)非常不同,所以这个名字用词不当。 2、这种解释只提供了一种不明确的直觉,但留下了一个有待后续挖掘的问题:为什么这项技术如此有效? 本书旨在传达实践者用来发展深层神经网络的直觉。 然而,重要的是将这些指导性直觉与既定的科学事实区分开来。 最终,当你掌握了这些方法,并开始撰写自己的研究论文时,你会希望清楚地区分技术和直觉。
随着批量规范化的普及,“内部协变量偏移”的解释反复出现在技术文献的辩论,特别是关于“如何展示机器学习研究”的更广泛的讨论中。 Ali Rahimi在接受2017年NeurIPS大会的“接受时间考验奖”(Test of Time Award)时发表了一篇令人难忘的演讲。他将“内部协变量转移”作为焦点,将现代深度学习的实践比作炼金术。 他对该示例进行了详细回顾 [Lipton & Steinhardt, 2018],概述了机器学习中令人不安的趋势。 此外,一些作者对批量规范化的成功提出了另一种解释:在某些方面,批量规范化的表现出与原始论文 [Santurkar et al., 2018]中声称的行为是相反的。
然而,与机器学习文献中成千上万类似模糊的说法相比,内部协变量偏移没有更值得批评。 很可能,它作为这些辩论的焦点而产生共鸣,要归功于目标受众对它的广泛认可。 批量规范化已经被证明是一种不可或缺的方法。它适用于几乎所有图像分类器,并在学术界获得了数万引用。
6. QA
6.1 请问这里讲的batch normalization和之前讲的xavier有什么区别之分?
本质很接近,xavier是为了在初始训练的时候,让数据在一个比较稳定的初始化,便于收敛。而BN这里主要是在一轮轮的训练过程中,调整数据的规整,这里在统计上应该叫归一化。其次xavier是对权重层的初始化,而BN是对feature map。
6.2 batch norm能用在MLP中嘛?
一般不会,BN是在层数很多的时候,加入这个变化,使得不会出现上层变动大,底层变化小的这种现象,让底层的调整更容易传导到上层。
6.3 BN是做了线性变换,和加一个线性层有什么区别?
没什么区别,但是加一个线性层它很难学到让你的数据归一化的这种特点,帮助我们使数据更容易收敛。
6.4 为什么加上了batch norm收敛时间变短?
因为加了BN你的数据稳定,就可以上更大的学习率。BN主要是使得地图层变平缓。
6.5 batch_size是把显存占满好?还是利用率到100%?
利用率90%就好了,你去增加batch size观察同时增加了多少数据处理数,当发现增加不多就可以了。