1.为什么要进行归一化处理
1.对于我们输入而言,标准化输入是一项重要的步骤,例如预测房间时,我们让特征值方差为1,均值为0,可以使我们的参数量级做到统一
2.对于典型的多层感知机而言,有些层输出的范围可能与输入的范围存在过大的区别,导致我们的模型收敛速度过慢
3.越深的模型,越容易过拟合·,就越需要正则化
2.归一化操作
具体流程:在每次训练迭代中,我们首先规范化输入,即通过减去其均值并除以其标准差,其中两者均基于当前小批量处理。接下来,我们应用比例系数和比 例偏移。
简单来说就是先将数据每个批次对应的特征归一化到均值为0,方差为1的区间,然后乘以一个可学习的y和加上一个偏执b
![](https://i-blog.csdnimg.cn/direct/39d0bcd83cbc4c47ac7476c92acb3a99.png)
特别的当批量大小为1时,我们学习不到任何东西,因为减去均值后,每个特征都为0 ,所以需要使用足够大的批量,批量规范化才是稳定的
我们的均值和方差是通过以下方法求出的
对于多层感知机而言,均值和方差是对一个批次中相同特征求(64,32)->(1,32),而对于卷积而言,是对于通道数去求,就相当于(3,3,224,224)批量为3的数据,变为(1,3,1,1)求每个通道的均值
特:我们在方差上加入了一个小的噪音,可以有效解决计算时除以0的问题,并且加入噪音也有利于我们去正则化
批量规范化层在”训练模式“和“预测模式”中的功能不同。在训练过程中,我们无法得知使用整个数据集来估计平均值和方差,所以只能根据每个小批 次的平均值和方差不断训练模型。而在预测模式下,可以根据整个数据集精确计算批量规范化所需的平均值 和方差。
从零实现
引入
import torch
from torch import nn
from d2l import torch as d2l
对于归一化块的实现
def batch_norm(X,gamma,beta,moving_mean,moving_var,eps,momentum):#eps防止除以0
#通过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:
#计算二维卷积层的情况
mean=X.mean(dim=(0,2,3),keepdim=True)#对通道做做平均 相当于一个一个取平均0 2 3维
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-momentum)*mean
moving_var=momentum*moving_var+(1-momentum)*var
Y=gamma*X_hat+beta#注意这里用的是点乘
return Y,moving_mean.data,moving_var.data
注意:我们在理想情况下希望的均值和方差是整个样本的,而不是批次,但在训练过程中我们是用批次的的均值和方差估算整体的,同时通过移动平均估算整个训练数据集的样 本均值和方差
所以在训练时用的是批次的均值和方差,而预测时用的是估算整个训练数据集的样 本均值和方差
归一化层的实现
class BatchNorm(nn.Module):
def __init__(self,num_features,num_dims):
super().__init__()
if num_dims==2:
shape=(1,num_features)
else:
shape=(1,num_features,1,1)
#申请可学习的参数
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)
#保存更新过的参数
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
num_features是特征的数量,num_dims是输入的维度,通过这两个去计算我们参数的形状
训练参数设置及训练
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())
d2l.plt.show()
torch.save(net.state_dict(),"规范化")
结果
批量归一化简介实现
其余的不变
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))
nn.batchnorm2d只接受特征数量,自动识别维度