规范化的作用
- 缓解内部协变量移位:
- 内部协变量移位指的是在训练过程中,网络各层的输入分布发生变化,导致后续层需要不断适应新的输入分布。规范化可以稳定中间层的输入分布,从而加快训练速度并提高模型的稳定性。
- 加速收敛:
- 规范化可以使得中间层的输入分布更加集中,有助于梯度下降更快地找到最优解。这是因为规范化后的数据通常具有更好的数值稳定性,梯度更容易传播。
- 提高模型泛化能力:
- 规范化可以减少模型对特定输入分布的依赖,使得模型在面对不同分布的数据时表现更好,从而提高模型的泛化能力。
- 改善梯度流动:
- 规范化可以改善梯度流动,防止梯度消失或梯度爆炸问题。通过规范化,梯度更容易在整个网络中传递。
层规范化和批规范的两种比较
1、批规范化(Batch Normalization,LN)
加速训练和改善模型泛化能力的一种正则化技术
2、层规范化(Layer Normalization,BN)
为了稳定神经网络训练而提出的一种规范化方法
层规范化计算公式:
对于每个样本 x ,层规范化计算如下: 计算每个特征维度上的均值 μ 和方差 σ 2 : μ = 1 D ∑ i = 1 D x i σ 2 = 1 D ∑ i = 1 D ( x i − μ ) 2 其中 D 是样本的特征数量。 归一化: x ^ i = x i − μ σ 2 + ϵ 其中 ϵ 是一个小的常数,防止除零错误。 缩放和平移: y i = γ x ^ i + β ,其中 γ 和 β 是可学习的参数。 对于每个样本x,层规范化计算如下:\\ 计算每个特征维度上的均值\mu和方差\sigma^2: \\ \mu = \frac{1}{D} \sum_{i=1}^{D} x_i\\ \sigma^2 = \frac{1}{D} \sum_{i=1}^{D} (x_i - \mu)^2 \\ 其中D是样本的特征数量。\\ ~\\ 归一化: \hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \epsilon}}\\ 其中\epsilon是一个小的常数,防止除零错误。\\ ~\\ 缩放和平移:y_i = \gamma \hat{x}_i + \beta,其中\gamma和\beta是可学习的参数。 对于每个样本x,层规范化计算如下:计算每个特征维度上的均值μ和方差σ2:μ=D1i=1∑Dxiσ2=D1i=1∑D(xi−μ)2其中D是样本的特征数量。 归一化:x^i=σ2+ϵxi−μ其中ϵ是一个小的常数,防止除零错误。 缩放和平移:yi=γx^i+β,其中γ和β是可学习的参数。
重点是对每一个样本的特征求均值和方差,在归一化。
批规范化计算公式:
对于给定的 b a t c h 的数据,批规范化计算如下: 计算 b a t c h 上的均值 μ 和方差 σ 2 : μ = 1 N ∑ i = 1 N x i σ 2 = 1 N ∑ i = 1 N ( x i − μ ) 2 其中 N 是 b a t c h 的大小。 归一化: x ^ i = x i − μ σ 2 + ϵ 缩放和平移: y i = γ x ^ i + β ,其中 γ 和 β 是可学习的参数。 对于给定的batch的数据,批规范化计算如下:\\ 计算batch上的均值\mu和方差\sigma^2: \\ \mu = \frac{1}{N} \sum_{i=1}^{N} x_i\\ \sigma^2 = \frac{1}{N} \sum_{i=1}^{N} (x_i - \mu)^2 \\ 其中N是batch的大小。\\ ~\\ 归一化: \hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \epsilon}} \\ ~\\ 缩放和平移: y_i = \gamma \hat{x}_i + \beta,其中\gamma和\beta是可学习的参数。 对于给定的batch的数据,批规范化计算如下:计算batch上的均值μ和方差σ2:μ=N1i=1∑Nxiσ2=N1i=1∑N(xi−μ)2其中N是batch的大小。 归一化:x^i=σ2+ϵxi−μ 缩放和平移:yi=γx^i+β,其中γ和β是可学习的参数。
重点是对一个batch大小数据在同一个特征维度上求均值和方差,在归一化。
代码比较
构建虚拟数据集sample.py
import numpy as np
import torchnp.random.seed(0)batch_size = 32
input_size = 10
inputs = np.random.randn(batch_size, input_size).astype(np.float32)
inputs_tensor = torch.tensor(inputs, requires_grad=True)
print(inputs_tensor.shape) #torch.Size([32, 10])
layer_norm.py文件
import torch
from sample import inputs_tensordef manual_layer_norm(x, gamma, beta, eps=1e-5):"""- x: 输入张量 (batch_size, input_size)- eps: 防止除零的小常数"""mean = x.mean(dim=-1, keepdim=True) # torch.Size([32, 1]) 每个样本计算自己的均值var = x.var(dim=-1, keepdim=True) # torch.Size([32, 1]) 每个样本计算自己的方差x_normalized = (x - mean) / torch.sqrt(var + eps) # torch.Size([32, 10]) 每个样本减自己的均值除自己方差x_normalized = gamma * x_normalized + betareturn x_normalized# 初始化缩放因子 gamma 和平移因子 beta 且需要反向更新
gamma = torch.ones(inputs_tensor.size(-1), requires_grad=True)
beta = torch.zeros(inputs_tensor.size(-1), requires_grad=True)layer_norm_outputs = manual_layer_norm(inputs_tensor, gamma, beta)print(f"Layer Normalized Inputs:\n{layer_norm_outputs.shape}") # torch.Size([32, 10])
print(f"Layer Normalized gamma:\n{gamma.shape}") # torch.Size([10])
print(f"Layer Normalized beta:\n{beta.shape}") # torch.Size([10])
batch_norm.py文件
import numpy as np
import torch
from sample import inputs_tensordef manual_batch_norm(x, gamma, beta, eps=1e-5):"""- x: 输入张量 (batch_size, input_size)- eps: 防止除零的小常数"""mean = x.mean(dim=0, keepdim=True) # torch.Size([1, 10]) 按batch大小每个特征方向求一个均值var = x.var(dim=0, keepdim=True) # torch.Size([1, 10]) 按batch大小每个特征方向求一个方差x_normalized = (x - mean) / torch.sqrt(var + eps) # torch.Size([32, 10]) 每个样本减去对应维度上的均值在除以方差x_normalized = gamma * x_normalized + betareturn (x_normalized,)# 初始化缩放因子 gamma 和平移因子 beta 且需要反向更新
gamma = torch.ones(inputs_tensor.size(-1), requires_grad=True)
beta = torch.zeros(inputs_tensor.size(-1), requires_grad=True)batch_norm_outputs = manual_batch_norm(inputs_tensor, gamma, beta)print(f"Batch Normalized Inputs:\n{batch_norm_outputs.shape}") # torch.Size([32, 10])
最后解释为什么都要进行缩放和平移?
规范化后的数据通常被调整到接近标准正态分布(均值为0,方差为1)。然而,直接使用这种规范化后的数据可能会限制网络的表达能力。因此,在规范化之后,通常会对数据进行缩放和平移,以便让网络能够学习到更适合任务的特征分布。
LN中的缩放和平移参数也是针对单个样本的所有特征共享的。
BN中的缩放和平移参数是针对mini-batch内的每个特征通道共享的。