【学习笔记】Pytorch深度学习—Batch Normalization
本节内容主要分为2大部分:(1)Batch Normalization概念;(2)Pytorch的Batch Normalization 1d/2d/3d 实现
Batch Normalization概念
Batch Normalization
Batch Normalization 定义:“ 批标准化 ”
1、批:指的是一批数据,通常为mini-batch:假设训练集有10万个样本,通常选择64、128、256这样的小批量数据;
2、标准化:指将数据特征的分布进行标准化,处理到“0均值、1方差”的分布;
Batch Normalization 优点
Batch Normalization批标准化定义简单,但却具有很大“能量”,其优点从论文《Batch Normalization:Accelerating Deep Network Training by Reducing Internal Covariate Shfit》(2015)如下,分别是“1用4不用”:
(1)可以用更大学习率,加速模型收敛
在 优化器(二)学习率 学习过,采用较大的学习率易导致梯度激增而使得模型无法训练,但采用的Batch Normalization以后可以采用较大学习率加速模型训练。
(2)可以不用精心设计权值初始化
在 权值初始化 一节学习过,不恰当的权值初始化明明采用梯度下降法但仍不能减小Loss,反而引发梯度激增或消失,出现nan现象,为解决问题设计出了Xavier和Kaiming方法对权值进行初始化。但有了BN层后,BN可以对数据尺度进行规范和约束,不用再精心设计权值初始化。
(3)可以不用dropout或较小的dropout
(4)可以不用L2或较小的weight decay
(5)可以不用LRN(Local response normalization)
LRN局部响应值的标准化,这一方法在AlexNet中有使用到,功能与Batch Normalization一样,对数据尺度进行规范。
Batch Normalization 算法分析
BN计算概念是:在mini_batch形式上减去均值,除以标准差,即得到1个0均值、1标准差的数据分布形式,完成数据的normalize;
但BN还具有1个Affine transform,Affine给BN层提供了可逆的操作,可以改变、或不改变batch的数据分布,至于到底改不改变就交给模型,具体则看
γ
\gamma
γ 、
β
\beta
β 的学习结果。
Batch Normalization 优点分析
这样1个简单的操作是如何实现那么多的优点的?
在论文《Batch Normalization:Accelerating Deep Network Training by Reducing Internal Covariate Shfit》(2015),提出BN是为了解决Internal Covariate Shfit这一问题的。
Internal Covariate Shfit
ICS这一概念可以简单理解为“数据尺度、数据分布的变化”,在学习权值初始化时,我们分析过网络层数据方差的变化。
在全连接网络中,第一个网络层 H 1 H_{1} H1层中的神经元 H 11 H_{11} H11, D ( H 1 ) = n × D ( X ) × D ( W ) D(H_1)=n\times D(X)\times D(W) D(H1)=n×D(X)×D(W),方差是连乘形式;如果尺度上稍有减小趋势,随着网络的加深,减小趋势会越来越明显,从而导致梯度消失;抑或是尺度上有增大趋势,随着网络的加深,从而导致梯度爆炸,造成模型难以训练。所以BN的提出是为了解决ICS问题的,同时发现BN带来的一系列的好处(即上面提到的5大优点)。
实验一:bn层对于权值初始化不敏感
class MLP(nn.Module):
def __init__(self, neural_num, layers=100):
super(MLP, self).__init__()
self.linears = nn.ModuleList([nn.Linear(neural_num, neural_num, bias=False) for i in range(layers)])
self.bns = nn.ModuleList([nn.BatchNorm1d(neural_num) for i in range(layers)])
self.neural_num = neural_num
def forward(self, x):
for (i, linear), bn in zip(enumerate(self.linears), self.bns):
x = linear(x)
x = bn(x) # (1)如果加入bn,数据尺度则控制在了0.5;或者(2)直接去掉权值初始化,只加入bn层,也能更好的控制数据尺度。
x = torch.relu(x)
if torch.isnan(x.std()):
print("output is nan in {} layers".format(i))
break
print("layers:{}, std:{}".format(i, x.std().item()))
return x
def initialize(self):
for m in self.modules():
if isinstance(m, nn.Linear):
# method 1,虽然设置了0均值、1方差,但仍属于不恰当的权值初始化,数据的尺度在100层达到了-40的数量级,明显出现了“梯度消失”的现象。
# nn.init.normal_(m.weight.data, std=1) # normal: mean=0, std=1
# method 2 kaiming 是精心设计的权值初始化,数据的尺度从一开始0.8逐渐控制在了0.6,克服了梯度消失的问题
nn.init.kaiming_normal_(m.weight.data)
neural_nums = 256
layer_nums = 100
batch_size = 16
net = MLP(neural_nums, layer_nums)
# net.initialize()
inputs = torch.randn((batch_size, neural_nums)) # normal: mean=0, std=1
output = net(inputs)
print(output)
方法一:设置权值尺度属于0均值、1标准差的范围,但输出数据尺度在第100层网络层达到-40次数量级,出现了梯度消失;
方法二:精心设计kaiming初始化方法,数据尺度得以控制;
方法三:针对在有无权值初始化基础上使用bn层,
(一)case1:在初始化基础上使用bn层;在forward当中,一定要在激活函数ReLu之前使用bn层,对输入的数据进行batch_normalization,数据的尺度保持的更好了,维持在0.5左右。
(二)case2:在无权值初始化基础上使用bn层;加了bn层后,都使得数据的尺度保持的更好了,在本例中维持在0.5左右浮动。
有了bn层,可以不用精心设计权值初始化,甚至不用权值初始化。
实验二:bn层在人民币二分类实验中的使用情况,对Lenet改进
人民币二分类实验中主体函数部分——模型发生了变化,使用的是加入了bn层的lenet模型。
# ============================ step 2/5 模型 ============================
net = LeNet_bn(classes=2)
# net = LeNet(classes=2)
# net.initialize_weights()
关于加入了bn层的lenet模型结构是:
class LeNet_bn(nn.Module):
def __init__(self, classes):
super(LeNet_bn, self).__init__()
在输出特征时加入bn层,对特征数据的尺度进行规范化。
self.conv1 = nn.Conv2d(3, 6, 5)
在_init_中定义一系列的bn层,采用BatchNorm2d()形式,设置重要参数num_features(因为卷积层输出有几个特征,这里第1个卷积层有6个特征图)
self.bn1 = nn.BatchNorm2d(num_features=6)
self.conv2 = nn.Conv2d(6, 16, 5)
self.bn2 = nn.BatchNorm2d(num_features=16)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
这里采用BatchNorm1d()形式
self.bn3 = nn.BatchNorm1d(num_features=120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, classes)
def forward(self, x): #bn层在forward中的使用
out = self.conv1(x)
out = self.bn1(out)
bn层一定要在激活函数relu前使用
out = F.relu(out)
out = F.max_pool2d(out, 2)
out = self.conv2(out)
out = self.bn2(out)
out = F.relu(out)
out = F.max_pool2d(out, 2)
out = out.view(out.size(0), -1)
out = self.fc1(out)
out = self.bn3(out)
out = F.relu(out)
out = F.relu(self.fc2(out))
out = self.fc3(out)
return out
def initialize_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.xavier_normal_(m.weight.data)
if m.bias is not None:
m.bias.data.zero_()
elif isinstance(m, nn.BatchNorm2d):
m.weight.data.fill_(1)
m.bias.data.zero_()
elif isinstance(m, nn.Linear):
nn.init.normal_(m.weight.data, 0, 1)
m.bias.data.zero_()
对比:
(1)使用原始的Lenet模型,不进行权值初始化,观察Loss曲线;
(2)使用权值初始化后的lenet模型:如果加上1个精心设计的权值初始化,观察Loss曲线;
(3)使用加入bn层后的lenet模型:采用加入了bn的lenet,观察Loss曲线;
Pytorch的Batch Normalization 1d/2d/3d 实现
一、Batch Normalization 1d/2d/3d 基本属性
Pytorch中提供了Batch Normalization的1d、2d、3d,在pytorch中所有batch normalization的实现都继承于_BatchNorm这一基类:
_BatchNorm
nn.BatchNorm1d
nn.BatchNorm2d
nn.BatchNrom3d
_init(self,num_features,eps=1e-5,momentum=0.1,
affine=True,track_running_stats=True)
主要参数
num_features:一个样本特征数量(最重要)
eps:分母修正项(通常会设置1个比较小的数字,10的-5次方,为了防止分
母为0而造成计算错误)
momentum:指数加权平均估计当前mean/var(通常设置为0.1)
affine:是否需要affine transform(布尔变量,控制是否需要进行Affine,默认为打开)
track_running_stats:是训练状态,还是测试状态(指示状态)
如果在训练状态,均值、方差需要重新估计;如果在测试状态,会采用当前
的统计信息,均值、方差固定的,但训练时这两个数据是会根据batch发生改
变。
主要属性
running_mean:均值
running_var:方差
weight:affine transform中的gamma
bias:affine transform中的beta
通过上面bn——“batch normalization算法分析”的运算过程就可以知道,运算过程中有4个主要参数,在pytorch中就对应下述名称:
-----------进行normalize标准化时-----------
running_mean:均值
running_var:方差
不仅考虑mini_batch均值和方差,还需考虑之前mini_batch数据的均值和方差
------------进行affine transform-------------
weight:affine transform中的gamma
bias:affine transform中的beta
其中,weight和bias是由模型自主可学习的。
二、Batch Normalization 1d/2d/3d 对数据的要求
_BatchNorm
nn.BatchNorm1d input=B*特征数*1d特征
nn.BatchNorm2d input=B*特征数*2d特征
nn.BatchNrom3d input=B*特征数*3d特征
(1)Batch Normalization 1d形式
batch_size = 3
num_features = 5
momentum = 0.3
features_shape = (1)
(2)Batch Normalization 2d形式
通常,卷积神经网络输出的特征图就是2d形式。
2d形式:
batch_size = 3
num_features = 6
momentum = 0.3
features_shape = (2, 2)
3d形式
batch_size = 3
num_features = 4
momentum = 0.3
features_shape = (2, 2, 3)
三、Batch Normalization 1d/2d/3d 4个主要参数的计算
总结:
在bn中,有减均值、除标准差、乘以gamma、加beta这4个参数,该4个参数在特征这一维度,每1个特征维度都可以算的4个参数,计算均值、标准差时需要使用指数加权法,gamma、beta则不需要关心,可以自主学习。