pytorch之添加BN的实现


Posted in Python onJanuary 06, 2020

pytorch之添加BN层

批标准化

模型训练并不容易,特别是一些非常复杂的模型,并不能非常好的训练得到收敛的结果,所以对数据增加一些预处理,同时使用批标准化能够得到非常好的收敛结果,这也是卷积网络能够训练到非常深的层的一个重要原因。

数据预处理

目前数据预处理最常见的方法就是中心化和标准化,中心化相当于修正数据的中心位置,实现方法非常简单,就是在每个特征维度上减去对应的均值,最后得到 0 均值的特征。标准化也非常简单,在数据变成 0 均值之后,为了使得不同的特征维度有着相同的规模,可以除以标准差近似为一个标准正态分布,也可以依据最大值和最小值将其转化为 -1 ~ 1之间,这两种方法非常的常见,如果你还记得,前面我们在神经网络的部分就已经使用了这个方法实现了数据标准化,至于另外一些方法,比如 PCA 或者 白噪声已经用得非常少了。

Batch Normalization

前面在数据预处理的时候,尽量输入特征不相关且满足一个标准的正态分布,

这样模型的表现一般也较好。但是对于很深的网路结构,网路的非线性层会使得输出的结果变得相关,且不再满足一个标准的 N(0, 1) 的分布,甚至输出的中心已经发生了偏移,这对于模型的训练,特别是深层的模型训练非常的困难。

所以在 2015 年一篇论文提出了这个方法,批标准化,简而言之,就是对于每一层网络的输出,对其做一个归一化,使其服从标准的正态分布,这样后一层网络的输入也是一个标准的正态分布,所以能够比较好的进行训练,加快收敛速度。batch normalization 的实现非常简单,对于给定的一个 batch 的数据pytorch之添加BN的实现算法的公式如下

pytorch之添加BN的实现

第一行和第二行是计算出一个 batch 中数据的均值和方差,接着使用第三个公式对 batch 中的每个数据点做标准化,ϵ是为了计算稳定引入的一个小的常数,通常取 pytorch之添加BN的实现,最后利用权重修正得到最后的输出结果,非常的简单,
实现一下简单的一维的情况,也就是神经网络中的情况

import sys
sys.path.append('..')

import torch
def simple_batch_norm_1d(x, gamma, beta):
 eps = 1e-5
 x_mean = torch.mean(x, dim=0, keepdim=True) # 保留维度进行 broadcast
 x_var = torch.mean((x - x_mean) ** 2, dim=0, keepdim=True)
 x_hat = (x - x_mean) / torch.sqrt(x_var + eps)
 return gamma.view_as(x_mean) * x_hat + beta.view_as(x_mean)
x = torch.arange(15).view(5, 3)
gamma = torch.ones(x.shape[1])
beta = torch.zeros(x.shape[1])
print('before bn: ')
print(x)
y = simple_batch_norm_1d(x, gamma, beta)
print('after bn: ')
print(y)

可以看到这里一共是 5 个数据点,三个特征,每一列表示一个特征的不同数据点,使用批标准化之后,每一列都变成了标准的正态分布这个时候会出现一个问题,就是测试的时候该使用批标准化吗?答案是肯定的,因为训练的时候使用了,而测试的时候不使用肯定会导致结果出现偏差,但是测试的时候如果只有一个数据集,那么均值不就是这个值,方差为 0 吗?这显然是随机的,所以测试的时候不能用测试的数据集去算均值和方差,而是用训练的时候算出的移动平均均值和方差去代替

实现以下能够区分训练状态和测试状态的批标准化方法

def batch_norm_1d(x, gamma, beta, is_training, moving_mean, moving_var, moving_momentum=0.1):
 eps = 1e-5
 x_mean = torch.mean(x, dim=0, keepdim=True) # 保留维度进行 broadcast
 x_var = torch.mean((x - x_mean) ** 2, dim=0, keepdim=True)
 if is_training:
 x_hat = (x - x_mean) / torch.sqrt(x_var + eps)
 moving_mean[:] = moving_momentum * moving_mean + (1. - moving_momentum) * x_mean
 moving_var[:] = moving_momentum * moving_var + (1. - moving_momentum) * x_var
 else:
 x_hat = (x - moving_mean) / torch.sqrt(moving_var + eps)
 return gamma.view_as(x_mean) * x_hat + beta.view_as(x_mean)

下面使用深度神经网络分类 mnist 数据集的例子来试验一下批标准化是否有用

import numpy as np
from torchvision.datasets import mnist # 导入 pytorch 内置的 mnist 数据
from torch.utils.data import DataLoader
from torch import nn
from torch.autograd import Variable

使用内置函数下载 mnist 数据集

train_set = mnist.MNIST('./data', train=True)
test_set = mnist.MNIST('./data', train=False)

def data_tf(x):
 x = np.array(x, dtype='float32') / 255
 x = (x - 0.5) / 0.5 # 数据预处理,标准化
 x = x.reshape((-1,)) # 拉平
 x = torch.from_numpy(x)
 return x

train_set = mnist.MNIST('./data', train=True, transform=data_tf, download=True) # 重新载入数据集,申明定义的数据变换
test_set = mnist.MNIST('./data', train=False, transform=data_tf, download=True)
train_data = DataLoader(train_set, batch_size=64, shuffle=True)
test_data = DataLoader(test_set, batch_size=128, shuffle=False)
class multi_network(nn.Module):
 def __init__(self):
 super(multi_network, self).__init__()
 self.layer1 = nn.Linear(784, 100)
 self.relu = nn.ReLU(True)
 self.layer2 = nn.Linear(100, 10)
 
 self.gamma = nn.Parameter(torch.randn(100))
 self.beta = nn.Parameter(torch.randn(100))
 
 self.moving_mean = Variable(torch.zeros(100))
 self.moving_var = Variable(torch.zeros(100))
 
 def forward(self, x, is_train=True):
 x = self.layer1(x)
 x = batch_norm_1d(x, self.gamma, self.beta, is_train, self.moving_mean, self.moving_var)
 x = self.relu(x)
 x = self.layer2(x)
 return x
net = multi_network()
# 定义 loss 函数
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(net.parameters(), 1e-1) # 使用随机梯度下降,学习率 0.1

from datetime import datetime
import torch
import torch.nn.functional as F
from torch import nn
from torch.autograd import Variable
def get_acc(output, label):
 total = output.shape[0]
 _, pred_label = output.max(1)
 num_correct = (pred_label == label).sum().item()
 return num_correct / total


#定义训练函数
def train(net, train_data, valid_data, num_epochs, optimizer, criterion):
 if torch.cuda.is_available():
 net = net.cuda()
 prev_time = datetime.now()
 for epoch in range(num_epochs):
 train_loss = 0
 train_acc = 0
 net = net.train()
 for im, label in train_data:
  if torch.cuda.is_available():
  im = Variable(im.cuda()) # (bs, 3, h, w)
  label = Variable(label.cuda()) # (bs, h, w)
  else:
  im = Variable(im)
  label = Variable(label)
  # forward
  output = net(im)
  loss = criterion(output, label)
  # backward
  optimizer.zero_grad()
  loss.backward()
  optimizer.step()
 train_loss += loss.item()
 train_acc += get_acc(output, label)

 cur_time = datetime.now()
 h, remainder = divmod((cur_time - prev_time).seconds, 3600)
 m, s = divmod(remainder, 60)
 time_str = "Time %02d:%02d:%02d" % (h, m, s)
 if valid_data is not None:
 valid_loss = 0
 valid_acc = 0
 net = net.eval()
 for im, label in valid_data:
  if torch.cuda.is_available():
  im = Variable(im.cuda(), volatile=True)
  label = Variable(label.cuda(), volatile=True)
  else:
  im = Variable(im, volatile=True)
  label = Variable(label, volatile=True)
  output = net(im)
  loss = criterion(output, label)
  valid_loss += loss.item()
  valid_acc += get_acc(output, label)
 epoch_str = (
  "Epoch %d. Train Loss: %f, Train Acc: %f, Valid Loss: %f, Valid Acc: %f, "
  % (epoch, train_loss / len(train_data),
  train_acc / len(train_data), valid_loss / len(valid_data),
  valid_acc / len(valid_data)))
 else:
 epoch_str = ("Epoch %d. Train Loss: %f, Train Acc: %f, " %
   (epoch, train_loss / len(train_data),
   train_acc / len(train_data)))
 prev_time = cur_time
 print(epoch_str + time_str)

train(net, train_data, test_data, 10, optimizer, criterion)

#这里的 γ都作为参数进行训练,初始化为随机的高斯分布,

#moving_mean 和 moving_var 都初始化为 0,并不是更新的参数,训练完 10 次之后,我们可以看看移动平均和移动方差被修改为了多少

#打出 moving_mean 的前 10 项

print(net.moving_mean[:10])
no_bn_net = nn.Sequential(
 nn.Linear(784, 100),
 nn.ReLU(True),
 nn.Linear(100, 10)
)

optimizer = torch.optim.SGD(no_bn_net.parameters(), 1e-1) # 使用随机梯度下降,学习率 0.1
train(no_bn_net, train_data, test_data, 10, optimizer, criterion)

可以看到虽然最后的结果两种情况一样,但是如果我们看前几次的情况,可以看到使用批标准化的情况能够更快的收敛,因为这只是一个小网络,所以用不用批标准化都能够收敛,但是对于更加深的网络,使用批标准化在训练的时候能够很快地收敛从上面可以看到,我们自己实现了 2 维情况的批标准化,对应于卷积的 4 维情况的标准化是类似的,只需要沿着通道的维度进行均值和方差的计算,但是我们自己实现批标准化是很累的,pytorch 当然也为我们内置了批标准化的函数,一维和二维分别是 torch.nn.BatchNorm1d() 和 torch.nn.BatchNorm2d(),不同于我们的实现,pytorch 不仅将 β作为训练的参数,也将 moving_mean 和 moving_var 也作为参数进行训练

下面在卷积网络下试用一下批标准化看看效果

def data_tf(x):
 x = np.array(x, dtype='float32') / 255
 x = (x - 0.5) / 0.5 # 数据预处理,标准化
 x = torch.from_numpy(x)
 x = x.unsqueeze(0)
 return x

train_set = mnist.MNIST('./data', train=True, transform=data_tf, download=True) # 重新载入数据集,申明定义的数据变换
test_set = mnist.MNIST('./data', train=False, transform=data_tf, download=True)
train_data = DataLoader(train_set, batch_size=64, shuffle=True)
test_data = DataLoader(test_set, batch_size=128, shuffle=False)

使用批标准化

class conv_bn_net(nn.Module):
 def __init__(self):
 super(conv_bn_net, self).__init__()
 self.stage1 = nn.Sequential(
  nn.Conv2d(1, 6, 3, padding=1),
  nn.BatchNorm2d(6),
  nn.ReLU(True),
  nn.MaxPool2d(2, 2),
  nn.Conv2d(6, 16, 5),
  nn.BatchNorm2d(16),
  nn.ReLU(True),
  nn.MaxPool2d(2, 2)
 )
 
 self.classfy = nn.Linear(400, 10)
 def forward(self, x):
 x = self.stage1(x)
 x = x.view(x.shape[0], -1)
 x = self.classfy(x)
 return x

net = conv_bn_net()
optimizer = torch.optim.SGD(net.parameters(), 1e-1) # 使用随机梯度下降,学习率 0.1
train(net, train_data, test_data, 5, optimizer, criterion)

不使用批标准化

class conv_no_bn_net(nn.Module):
 def __init__(self):
 super(conv_no_bn_net, self).__init__()
 self.stage1 = nn.Sequential(
  nn.Conv2d(1, 6, 3, padding=1),
  nn.ReLU(True),
  nn.MaxPool2d(2, 2),
  nn.Conv2d(6, 16, 5),
  nn.ReLU(True),
  nn.MaxPool2d(2, 2)
 )
 
 self.classfy = nn.Linear(400, 10)
 def forward(self, x):
 x = self.stage1(x)
 x = x.view(x.shape[0], -1)
 x = self.classfy(x)
 return x

net = conv_no_bn_net()
optimizer = torch.optim.SGD(net.parameters(), 1e-1) # 使用随机梯度下降,学习率 0.1
train(net, train_data, test_data, 5, optimizer, criterion)

以上这篇pytorch之添加BN的实现就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
跨平台python异步回调机制实现和使用方法
Nov 26 Python
Python操作Word批量生成文章的方法
Jul 28 Python
Python语言生成水仙花数代码示例
Dec 18 Python
python 遍历目录(包括子目录)下所有文件的实例
Jul 11 Python
示例详解Python3 or Python2 两者之间的差异
Aug 23 Python
python 同时读取多个文件的例子
Jul 16 Python
python进程的状态、创建及使用方法详解
Dec 06 Python
解决jupyter notebook打不开无反应 浏览器未启动的问题
Apr 10 Python
Python如何输出百分比
Jul 31 Python
Python代码注释规范代码实例解析
Aug 14 Python
django中cookiecutter的使用教程
Dec 03 Python
用python获取txt文件中关键字的数量
Dec 24 Python
PyTorch学习:动态图和静态图的例子
Jan 06 #Python
pytorch动态网络以及权重共享实例
Jan 06 #Python
selenium中get_cookies()和add_cookie()的用法详解
Jan 06 #Python
pytorch中的自定义反向传播,求导实例
Jan 06 #Python
PyTorch中 tensor.detach() 和 tensor.data 的区别详解
Jan 06 #Python
6行Python代码实现进度条效果(Progress、tqdm、alive-progress​​​​​​​和PySimpleGUI库)
Jan 06 #Python
基于python+selenium的二次封装的实现
Jan 06 #Python
You might like
PHP获取windows登录用户名的方法
2014/06/24 PHP
PHP数组函数知识汇总
2016/05/12 PHP
深入理解Yii2.0乐观锁与悲观锁的原理与使用
2017/07/26 PHP
PHP使用SOAP调用API操作示例
2018/12/25 PHP
js动态调用css属性的小规律及实例说明
2013/12/28 Javascript
使用jQuery不判断浏览器高度解决iframe自适应高度问题
2014/12/16 Javascript
JS中使用变量保存arguments对象的方法
2016/06/03 Javascript
JavaScript定义数组的三种方法(new Array(),new Array('x','y')
2016/10/04 Javascript
js Canvas绘制圆形时钟效果
2017/02/17 Javascript
es7学习教程之Decorators(修饰器)详解
2017/07/21 Javascript
js登录滑动验证的实现(不滑动无法登陆)
2018/01/03 Javascript
JSON生成Form表单的方法示例
2018/11/21 Javascript
详解vuex的简单todolist例子
2019/07/14 Javascript
JavaScript中作用域链的概念及用途讲解
2020/08/06 Javascript
Python 网络编程起步(Socket发送消息)
2008/09/06 Python
六个窍门助你提高Python运行效率
2015/06/09 Python
Python最火、R极具潜力 2017机器学习调查报告
2017/12/11 Python
Python语言描述连续子数组的最大和
2018/01/04 Python
python时间序列按频率生成日期的方法
2019/05/14 Python
Python基于Opencv来快速实现人脸识别过程详解(完整版)
2019/07/11 Python
Python3网络爬虫开发实战之极验滑动验证码的识别
2019/08/02 Python
使用Python调取任意数字资产钱包余额功能
2019/08/15 Python
Python切图九宫格的实现方法
2019/10/10 Python
使用python 对验证码图片进行降噪处理
2019/12/18 Python
python爬虫使用scrapy注意事项
2020/11/23 Python
html5利用canvas实现颜色容差抠图功能
2019/12/23 HTML / CSS
如何找出EMP表里面SALARY第N高的employee
2013/12/05 面试题
宠物店的创业计划书范文
2014/01/11 职场文书
教学实习自我评价
2014/01/28 职场文书
办公室文员自荐书
2014/02/03 职场文书
2014年商场超市庆元旦活动方案
2014/02/14 职场文书
小区消防演习方案
2014/02/21 职场文书
副总经理岗位职责
2014/03/16 职场文书
2014年护士长工作总结
2014/11/11 职场文书
2014年车间主任工作总结
2014/12/10 职场文书
Python机器学习之逻辑回归
2021/05/11 Python