跳到主要内容

批量规范化

批量规范化是什么?

批量规范化(Batch Normalization,简称BN)是一种用于提高深度神经网络训练速度和稳定性的技术。它是由 Sergey Ioffe 和 Christian Szegedy 在2015年提出的。

提出背景

1、为什么随着网络深度增加,神经网络训练越来越慢呢?

因为深层神经网络在做非线性变换前的激活输入值,随着网络深度加深或者在训练过程中,其分布逐渐发生偏移或者变动。训练收敛慢,一般是因为 整体分布逐渐往非线性函数的取值区间的上下限两端靠近,这导致反向传播时低层神经网络的梯度消失,这是训练深层神经网络收敛越来越慢的本质原因。

2、BN(Batch Normalization)的基本思想

BN 不仅仅对输入层做标准化处理,还对网络的每一中间层的输入(激活函数前)做标准化处理,使得输出服从均值为0,方差为1的正态分布,从而避免变量分布偏移的问题。之所以称之为批标准化,是因为在训练期间,我们仅通过计算当前层一小批数据的均值和方差来标准化每一层的输入。相当于把每层神经网络任意神经元这个输入值的分布强行拉回到均值为0,方差为1的标准正态分布。

3、Batch Normalization 的优点

  1. 可以使用较大的学习率,加速模型的收敛。
  2. 减少对参数初始化的依赖。
  3. 可以充当正则化,减少(或省略)其他正则化技术,如Dropout。
  4. 有助于解决梯度消失和梯度爆炸问题。

操作步骤

  1. 对每个小批量(mini-batch)的数据进行标准化,使其具有零均值和单位方差。
  2. 将标准化的数据进行缩放和平移,使用两个可学习的参数:缩放因子 γ\gamma(gamma)和平移因子 β\beta(beta)。这两个参数允许模型学习到最佳的数据分布。

数学上,批量规范化可以表示为:

μB=1mi=1mxi(计算均值)\mu_B = \frac{1}{m} \sum_{i=1}^{m} x_i \quad \text{(计算均值)} σB2=1mi=1m(xiμB)2(计算方差)\sigma_B^2 = \frac{1}{m} \sum_{i=1}^{m} (x_i - \mu_B)^2 \quad \text{(计算方差)} x^i=xiμBσB2+ϵ(标准化)\hat{x}_i = \frac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} \quad \text{(标准化)} yi=γx^i+β(缩放和平移)y_i = \gamma \hat{x}_i + \beta \quad \text{(缩放和平移)}

其中,ϵ\epsilon 是一个很小的数,用于防止除以零;γ\gammaβ\beta 是可学习的参数。

注意: 虽然批量规范化在训练时是基于小批量的统计数据进行的,但在测试时,为了得到稳定的输出,通常使用整个训练集的均值和方差(或移动平均)来进行规范化。

在 PyTorch 中使用

在PyTorch中,批量规范化可以通过nn.BatchNorm层来实现。这个层可以在卷积网络或全连接网络中使用,以帮助网络更快地收敛并提高性能。

以下是如何在PyTorch中使用批量规范化的简单例子:

  1. 全连接网络中的批量规范化
import torch.nn as nn

class SimpleFCNetwork(nn.Module):
def __init__(self):
super(SimpleFCNetwork, self).__init__()
self.fc1 = nn.Linear(in_features=784, out_features=500)
self.bn1 = nn.BatchNorm1d(500) # 批量规范化层
self.fc2 = nn.Linear(in_features=500, out_features=10)

def forward(self, x):
x = self.fc1(x)
x = self.bn1(x) # 在激活函数之前或之后使用批量规范化
x = nn.ReLU()(x)
x = self.fc2(x)
return x
  1. 卷积网络中的批量规范化
class SimpleCNN(nn.Module):
def __init__(self):
super(SimpleCNN, self).__init__()
self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, stride=1, padding=1)
self.bn1 = nn.BatchNorm2d(32) # 批量规范化层
self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1, padding=1)
self.bn2 = nn.BatchNorm2d(64) # 批量规范化层
self.fc1 = nn.Linear(64 * 7 * 7, 10)

def forward(self, x):
x = self.conv1(x)
x = self.bn1(x) # 在激活函数之前或之后使用批量规范化
x = nn.ReLU()(x)
x = self.conv2(x)
x = self.bn2(x) # 在激活函数之前或之后使用批量规范化
x = nn.ReLU()(x)
x = nn.MaxPool2d(kernel_size=2, stride=2)(x)
x = x.view(x.size(0), -1)
x = self.fc1(x)
return x

注意:批量规范化可以在激活函数之前或之后使用。两种方法都有其支持者,可以根据自己的实验结果选择最佳的方法。

References