Batch Normal - 批量规范化(CNN卷积神经网络)

Batch Normal - 批量规范化

批量规范化(batch normalization) [Ioffe & Szegedy, 2015],这是一种流行且有效的技术,是通过对样本数据进行规范化操作(如标准化正态分布、softmax回归等),使样本数据更加稳定,可持续加速深层网络的收敛速度

主要公式如下:

B N ( x ) = γ ⊙ x − μ ^ B σ ^ B + β . \mathrm{BN}(\mathbf{x}) = \boldsymbol{\gamma} \odot \frac{\mathbf{x} - \hat{\boldsymbol{\mu}}_\mathcal{B}}{\hat{\boldsymbol{\sigma}}_\mathcal{B}} + \boldsymbol{\beta}. BN(x)=γσ^Bxμ^B+β.

其中 μ ^ B \hat{\boldsymbol{\mu}}_\mathcal{B} μ^B均值 σ ^ B \hat{\boldsymbol{\sigma}}_\mathcal{B} σ^B标准差 γ {\gamma} γ拉伸参数(scale) β {\beta} β偏移参数(shift), x − μ ^ B σ ^ B \frac{\mathbf{x} - \hat{\boldsymbol{\mu}}_\mathcal{B}}{\hat{\boldsymbol{\sigma}}_\mathcal{B}} σ^Bxμ^B标准化正态分布

实现如下:

全连接层

通常,我们将批量规范化层置于全连接层中的仿射变换和激活函数之间

若全连接层的输入为x,权重参数和偏置参数分别为 W \mathbf{W} W b \mathbf{b} b,激活函数为 ϕ \phi ϕ,批量规范化的运算符为 B N \mathrm{BN} BN
那么,使用批量规范化的全连接层的输出的计算详情如下:

h = ϕ ( B N ( W x + b ) ) \mathbf{h} = \phi(\mathrm{BN}(\mathbf{W}\mathbf{x} + \mathbf{b}) ) h=ϕ(BN(Wx+b))

卷积层

同样,对于卷积层,我们可以在卷积层之后和非线性激活函数之前应用批量规范化

所以,在计算平均值和方差时,我们会收集所有空间位置的值,然后在给定通道内应用相同的均值和方差,以便在每个空间位置对值进行规范化

预测过程中的批量规范化

正如我们前面提到的,批量规范化在训练模式和预测模式下的行为通常不同。

首先,将训练好的模型用于预测时,我们不再需要样本均值中的噪声以及在微批次上估计每个小批次产生的样本方差了。 其次,例如,我们可能需要使用我们的模型对逐个样本进行预测。 一种常用的方法是通过移动平均估算整个训练数据集的样本均值和方差,并在预测时使用它们得到确定的输出。

可见,和暂退法一样,批量规范化层在训练模式预测模式下的计算结果也是不一样的。

从零实现

接下来我们从零实现一个具有张量的批量规范化层。

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:
        #要求输入的X数据必须为全连接层2个维度或者卷积层4个维度
        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) 的均值和方差
            #这里我们需要保持X1的形状以便后面可以做广播运算
            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, moving_var

我们现在可以创建一个正确的BatchNorm层。 这个层将保持适当的参数:拉伸gamma和偏移beta,这两个参数将在训练过程中更新。 此外,我们的层将保存均值和方差的移动平均值,以便在模型预测期间随后使用。

#定义批量数据集规范化模型
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
        

使用批量规范化层的 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时几乎完全相同,主要区别在于学习率大得多。

X = torch.rand(size=(1 ,1, 28, 28))

for layer in net:
    X = layer(X)
    print(layer.__class__.__name__, 'output:\t', X.shape)
Conv2d output:	 torch.Size([1, 6, 24, 24])
BatchNorm output:	 torch.Size([1, 6, 24, 24])
Sigmoid output:	 torch.Size([1, 6, 24, 24])
AvgPool2d output:	 torch.Size([1, 6, 12, 12])
Conv2d output:	 torch.Size([1, 16, 8, 8])
BatchNorm output:	 torch.Size([1, 16, 8, 8])
Sigmoid output:	 torch.Size([1, 16, 8, 8])
AvgPool2d output:	 torch.Size([1, 16, 4, 4])
Flatten output:	 torch.Size([1, 256])
Linear output:	 torch.Size([1, 120])
BatchNorm output:	 torch.Size([1, 120])
Sigmoid output:	 torch.Size([1, 120])
Linear output:	 torch.Size([1, 84])
BatchNorm output:	 torch.Size([1, 84])
Sigmoid output:	 torch.Size([1, 84])
Linear output:	 torch.Size([1, 10])

开始训练模型

lr, num_epochs, batch_size = 0.1, 10, 128
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())

在这里插入图片描述

可见模型对于训练集的准确度为 0.908,测试集的精度为 0.846。

简明实现

除了使用我们刚刚定义的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))

再次训练模型

lr, num_epochs, batch_size = 0.1, 10, 128
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size, resize=96)
d2l.train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())

在这里插入图片描述

可能迭代次数较低,所以测试集的准确度不是非常理想,有兴趣的小伙伴可以设置迭代次数为20次或50次,再次测试结果。

小结

1.在模型训练过程中,批量规范化利用小批量的均值和标准差,不断调整神经网络的中间输出,使整个神经网络各层的中间输出值更加稳定

2.批量规范化在全连接层和卷积层的使用略有不同。

3.批量规范化层和暂退层一样,在训练模式和预测模式下计算不同。

4.批量规范化有许多有益的副作用,主要是正则化。另一方面,”减少内部协变量偏移“的原始动机似乎不是一个有效的解释。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
批量归一化(Batch Normalization,简称BN)是一种常用于深度神经网络的技术,旨在加速模型训练并提高模型的泛化能力。它的作用是对输入数据进行标准化处理,即将数据按特征维度减去均值,再除以标准差,以使其分布接近标准正态分布。 BN主要包括两个步骤:训练阶段和推理阶段。 在训练阶段,对于每个批次的数据,首先计算该批次数据在每个特征维度上的均值和方差,并使用这些统计量对该批次的数据进行标准化处理。然后,通过一个可学习的缩放因子和偏移项,对标准化后的数据进行线性变换,以恢复数据的表达能力。这样可以使得输入数据在网络中传播时的数值更稳定,并且减少了网络对初始参数的敏感程度。 在推理阶段,由于每个测试样本可能无法组成完整的批次,无法计算该批次数据的均值和方差。因此,在训练阶段通过累积一批次数据的均值和方差得到的统计量进行标准化处理。这种方法保持了训练阶段获得的统计特性,并且可以应用于单个测试样本。 批量归一化处理BN的优点包括: - 提高模型训练的收敛速度和稳定性。 - 减少了对初始参数选择的敏感度,使得网络更容易训练。 - 缓解了梯度消失和梯度爆炸问题。 - 有一定的正则化效果,减少了对 Dropout 等正则化技术的依赖。 需要注意的是,BN在一些情况下可能会引入一定的计算开销,且对小批次数据的效果可能不如大批次数据明显。在实际应用中,BN通常被应用于卷积神经网络CNN)和全连接神经网络(FCN)中,并在很多深度学习任务中得到了广泛应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gaolw1102

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值