Pytorch 高效使用GPU的操作


Posted in Python onJune 27, 2020

前言

深度学习涉及很多向量或多矩阵运算,如矩阵相乘、矩阵相加、矩阵-向量乘法等。深层模型的算法,如BP,Auto-Encoder,CNN等,都可以写成矩阵运算的形式,无须写成循环运算。然而,在单核CPU上执行时,矩阵运算会被展开成循环的形式,本质上还是串行执行。GPU(Graphic Process Units,图形处理器)的众核体系结构包含几千个流处理器,可将矩阵运算并行化执行,大幅缩短计算时间。随着NVIDIA、AMD等公司不断推进其GPU的大规模并行架构,面向通用计算的GPU已成为加速可并行应用程序的重要手段。得益于GPU众核(many-core)体系结构,程序在GPU系统上的运行速度相较于单核CPU往往提升几十倍乃至上千倍。

目前,GPU已经发展到了较为成熟的阶段。利用GPU来训练深度神经网络,可以充分发挥其数以千计计算核心的能力,在使用海量训练数据的场景下,所耗费的时间大幅缩短,占用的服务器也更少。如果对适当的深度神经网络进行合理优化,一块GPU卡相当于数十甚至上百台CPU服务器的计算能力,因此GPU已经成为业界在深度学习模型训练方面的首选解决方案。

如何使用GPU?现在很多深度学习工具都支持GPU运算,使用时只要简单配置即可。Pytorch支持GPU,可以通过to(device)函数来将数据从内存中转移到GPU显存,如果有多个GPU还可以定位到哪个或哪些GPU。Pytorch一般把GPU作用于张量(Tensor)或模型(包括torch.nn下面的一些网络模型以及自己创建的模型)等数据结构上。

单GPU加速

使用GPU之前,需要确保GPU是可以使用,可通过torch.cuda.is_available()的返回值来进行判断。返回True则具有能够使用的GPU。

通过torch.cuda.device_count()可以获得能够使用的GPU数量。

如何查看平台GPU的配置信息?在命令行输入命令nvidia-smi即可 (适合于Linux或Windows环境)。图5-13是GPU配置信息样例,从中可以看出共有2个GPU。

Pytorch 高效使用GPU的操作

图 GPU配置信息

把数据从内存转移到GPU,一般针对张量(我们需要的数据)和模型。 对张量(类型为FloatTensor或者是LongTensor等),一律直接使用方法.to(device)或.cuda()即可。

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#或device = torch.device("cuda:0")
device1 = torch.device("cuda:1") 
for batch_idx, (img, label) in enumerate(train_loader):
  img=img.to(device)
  label=label.to(device)

对于模型来说,也是同样的方式,使用.to(device)或.cuda来将网络放到GPU显存。

#实例化网络
model = Net()
model.to(device)  #使用序号为0的GPU
#或model.to(device1) #使用序号为1的GPU

多GPU加速

这里我们介绍单主机多GPUs的情况,单机多GPUs主要采用的DataParallel函数,而不是DistributedParallel,后者一般用于多主机多GPUs,当然也可用于单机多GPU。

使用多卡训练的方式有很多,当然前提是我们的设备中存在两个及以上的GPU。

使用时直接用model传入torch.nn.DataParallel函数即可,如下代码:

#对模型

net = torch.nn.DataParallel(model)

这时,默认所有存在的显卡都会被使用。

如果你的电脑有很多显卡,但只想利用其中一部分,如只使用编号为0、1、3、4的四个GPU,那么可以采用以下方式:

#假设有4个GPU,其id设置如下
device_ids =[0,1,2,3]
#对数据
input_data=input_data.to(device=device_ids[0])
#对于模型
net = torch.nn.DataParallel(model)
net.to(device)

或者

os.environ["CUDA_VISIBLE_DEVICES"] = ','.join(map(str, [0,1,2,3]))

net = torch.nn.DataParallel(model)

其中CUDA_VISIBLE_DEVICES 表示当前可以被Pytorch程序检测到的GPU。

下面为单机多GPU的实现代码。

背景说明

这里使用波士顿房价数据为例,共506个样本,13个特征。数据划分成训练集和测试集,然后用data.DataLoader转换为可批加载的方式。采用nn.DataParallel并发机制,环境有2个GPU。当然,数据量很小,按理不宜用nn.DataParallel,这里只是为了说明使用方法。

加载数据

boston = load_boston()
X,y  = (boston.data, boston.target)
 
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
#组合训练数据及标签
myset = list(zip(X_train,y_train))

把数据转换为批处理加载方式批次大小为128,打乱数据

from torch.utils import data
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
dtype = torch.FloatTensor
train_loader = data.DataLoader(myset,batch_size=128,shuffle=True)

定义网络

class Net1(nn.Module):
  """
  使用sequential构建网络,Sequential()函数的功能是将网络的层组合到一起
  """
  def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
    super(Net1, self).__init__()
    self.layer1 = torch.nn.Sequential(nn.Linear(in_dim, n_hidden_1))
    self.layer2 = torch.nn.Sequential(nn.Linear(n_hidden_1, n_hidden_2))
    self.layer3 = torch.nn.Sequential(nn.Linear(n_hidden_2, out_dim))
    
 
  def forward(self, x):
    x1 = F.relu(self.layer1(x))
    x1 = F.relu(self.layer2(x1))
    x2 = self.layer3(x1)
    #显示每个GPU分配的数据大小
    print("\tIn Model: input size", x.size(),"output size", x2.size())
    return x2

把模型转换为多GPU并发处理格式

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#实例化网络
model = Net1(13, 16, 32, 1)
if torch.cuda.device_count() > 1:
  print("Let's use", torch.cuda.device_count(), "GPUs")
  # dim = 0 [64, xxx] -> [32, ...], [32, ...] on 2GPUs
  model = nn.DataParallel(model)
model.to(device)

运行结果

Let's use 2 GPUs
DataParallel(
(module): Net1(
(layer1): Sequential(
(0): Linear(in_features=13, out_features=16, bias=True)
)
(layer2): Sequential(
(0): Linear(in_features=16, out_features=32, bias=True)
)
(layer3): Sequential(
(0): Linear(in_features=32, out_features=1, bias=True)
)
)
)

选择优化器及损失函数

optimizer_orig = torch.optim.Adam(model.parameters(), lr=0.01)

loss_func = torch.nn.MSELoss()

模型训练,并可视化损失值

from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter(log_dir='logs')
for epoch in range(100):    
  model.train()
  for data,label in train_loader:
    input = data.type(dtype).to(device)
    label = label.type(dtype).to(device)
    output = model(input)    
    loss = loss_func(output, label)
    # 反向传播
    optimizer_orig.zero_grad()
    loss.backward()
    optimizer_orig.step()
    print("Outside: input size", input.size() ,"output_size", output.size())
  writer.add_scalar('train_loss_paral',loss, epoch)

运行的部分结果

In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
Outside: input size torch.Size([128, 13]) output_size torch.Size([128, 1])
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
In Model: input size torch.Size([64, 13]) output size torch.Size([64, 1])
Outside: input size torch.Size([128, 13]) output_size torch.Size([128, 1])

从运行结果可以看出,一个批次数据(batch-size=128)拆分成两份,每份大小为64,分别放在不同的GPU上。此时用GPU监控也可发现,两个GPU都同时在使用。

Pytorch 高效使用GPU的操作

8. 通过web查看损失值的变化情况

Pytorch 高效使用GPU的操作

图 并发运行训练损失值变化情况

图形中出现较大振幅,是由于采用批次处理,而且数据没有做任何预处理,对数据进行规范化应该更平滑一些,大家可以尝试一下。

单机多GPU也可使用DistributedParallel,它多用于分布式训练,但也可以用在单机多GPU的训练,配置比使用nn.DataParallel稍微麻烦一点,但是训练速度和效果更好一点。具体配置为:

#初始化使用nccl后端
torch.distributed.init_process_group(backend="nccl")
#模型并行化
model=torch.nn.parallel.DistributedDataParallel(model)

单机运行时使用下面方法启动

python -m torch.distributed.launch main.py

使用GPU注意事项

使用GPU可以提升我们训练的速度,如果使用不当,可能影响使用效率,具体使用时要注意以下几点:

GPU的数量尽量为偶数,奇数的GPU有可能会出现异常中断的情况;

GPU很快,但数据量较小时,效果可能没有单GPU好,甚至还不如CPU;

如果内存不够大,使用多GPU训练的时候可通过设置pin_memory为False,当然使用精度稍微低一点的数据类型有时也效果。

以上这篇Pytorch 高效使用GPU的操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python 抓取动态网页内容方案详解
Dec 25 Python
python中Genarator函数用法分析
Apr 08 Python
Python变量作用范围实例分析
Jul 07 Python
Python3字符串学习教程
Aug 20 Python
Python常见异常分类与处理方法
Jun 04 Python
Python 实现异步调用函数的示例讲解
Oct 14 Python
python3 批量获取对应端口服务的实例
Jul 25 Python
Windows+Anaconda3+PyTorch+PyCharm的安装教程图文详解
Apr 03 Python
pyinstaller打包找不到文件的问题解决
Apr 15 Python
requests在python中发送请求的实例讲解
Feb 17 Python
python使用PySimpleGUI设置进度条及控件使用
Jun 10 Python
Python list列表删除元素的4种方法
Nov 01 Python
Keras中的两种模型:Sequential和Model用法
Jun 27 #Python
keras输出预测值和真实值方式
Jun 27 #Python
使用Keras预训练好的模型进行目标类别预测详解
Jun 27 #Python
浅谈keras 模型用于预测时的注意事项
Jun 27 #Python
python suds访问webservice服务实现
Jun 26 #Python
解析Python 偏函数用法全方位实现
Jun 26 #Python
Python如何优雅删除字符列表空字符及None元素
Jun 25 #Python
You might like
php快速url重写更新版[需php 5.30以上]
2010/04/25 PHP
在Windows系统上安装PHP运行环境文字教程
2010/07/19 PHP
LotusPhp笔记之:基于ObjectUtil组件的使用分析
2013/05/06 PHP
php获取用户浏览器版本的方法
2015/01/03 PHP
php导入excel文件到mysql数据库的方法
2015/01/14 PHP
WordPress中给文章添加自定义字段及后台编辑功能区域
2015/12/19 PHP
PHP反射原理与用法深入分析
2019/09/28 PHP
Js sort排序使用方法
2011/10/17 Javascript
Javascript中实现trim()函数的两种方法
2015/02/04 Javascript
jQuery+css3动画属性制作猎豹浏览器宽屏banner焦点图
2015/03/16 Javascript
关于cookie的初识和运用(js和jq)
2016/04/07 Javascript
vue2.0 与 bootstrap datetimepicker的结合使用实例
2017/05/22 Javascript
实时监控input框,实现输入框与下拉框联动的实例
2018/01/23 Javascript
浅谈Vue 数据响应式原理
2018/05/07 Javascript
H5+C3+JS实现双人对战五子棋游戏(UI篇)
2020/05/28 Javascript
详解Vue iview IE浏览器不兼容报错(Iview Bable polyfill)
2019/01/07 Javascript
javascript中的闭包概念与用法实践分析
2019/07/26 Javascript
vue实现倒计时获取验证码效果
2020/04/17 Javascript
ES6如何用一句代码实现函数的柯里化
2020/01/18 Javascript
[01:17:55]VGJ.T vs Mineski 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/20 DOTA
python数据结构学习之实现线性表的顺序
2018/09/28 Python
Python tkinter label 更新方法
2018/10/11 Python
Python输出\u编码将其转换成中文的实例
2018/12/15 Python
python消费kafka数据批量插入到es的方法
2018/12/27 Python
Python之lambda匿名函数及map和filter的用法
2019/03/05 Python
python写日志文件操作类与应用示例
2019/07/01 Python
python常见字符串处理函数与用法汇总
2019/10/30 Python
python OpenCV GrabCut使用实例解析
2019/11/11 Python
Stefania Mode美国:奢华设计师和时尚服装
2018/01/07 全球购物
英国时尚泳装品牌:Maru Swimwear
2019/10/06 全球购物
英国百年闻名的优质健康产品连锁店:Holland & Barrett
2019/12/19 全球购物
农业局学习党的群众路线教育实践活动心得体会
2014/03/07 职场文书
小学生演讲稿大全
2014/04/25 职场文书
创新社会管理心得体会
2014/09/12 职场文书
应届毕业生求职简历自我评价
2015/03/02 职场文书
国庆节到了,利用JS实现一个生成国庆风头像的小工具 详解实现过程
2021/10/05 Javascript