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的线程来解决生产者消费问题的示例
Apr 02 Python
Python获取邮件地址的方法
Jul 10 Python
Python字符串格式化输出方法分析
Apr 13 Python
解析Python中的生成器及其与迭代器的差异
Jun 20 Python
python中的随机函数random的用法示例
Jan 27 Python
VScode编写第一个Python程序HelloWorld步骤
Apr 06 Python
python opencv读mp4视频的实例
Dec 07 Python
Django实现后台上传并显示图片功能
May 29 Python
python中pdb模块实例用法
Jan 15 Python
Django显示可视化图表的实践
May 10 Python
pytorch 如何使用batch训练lstm网络
May 28 Python
Python IO文件管理的具体使用
Mar 20 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下通过IP获取地理位置的代码(小偷程序)
2011/06/09 PHP
使用php伪造referer的方法 利用referer防止图片盗链
2014/01/20 PHP
php中限制ip段访问、禁止ip提交表单的代码分享
2014/08/22 PHP
Json_encode防止汉字转义成unicode的方法
2016/02/25 PHP
PHP设计模式(九)外观模式Facade实例详解【结构型】
2020/05/02 PHP
超越Jquery_01_isPlainObject分析与重构
2010/10/20 Javascript
两个Javascript小tip资料
2010/11/23 Javascript
判断对象是否Window的实现代码
2012/01/10 Javascript
jquery实现隐藏与显示动画效果/输入框字符动态递减/导航按钮切换
2013/07/01 Javascript
jQuery中live()方法用法实例
2015/01/19 Javascript
javascript实现简单查找与替换的方法
2015/07/22 Javascript
jQuery实现可编辑的表格实例讲解(2)
2015/09/17 Javascript
基于jQuery实现带动画效果超炫酷的弹出对话框(附源码下载)
2016/02/22 Javascript
jQuery Ajax和getJSON获取后台普通json数据和层级json数据用法分析
2016/06/08 Javascript
用vue快速开发app的脚手架工具
2018/06/11 Javascript
JS中call和apply函数用法实例分析
2018/06/20 Javascript
创建echart多个联动的示例代码
2018/11/23 Javascript
Vue程序调试的方法
2019/06/17 Javascript
layui 弹出层值回传解决方式
2019/11/14 Javascript
Python实现的ini文件操作类分享
2014/11/20 Python
python实现的文件夹清理程序分享
2014/11/22 Python
使用pyecharts在jupyter notebook上绘图
2020/04/23 Python
Python实现PS滤镜特效之扇形变换效果示例
2018/01/26 Python
python如何变换环境
2020/07/21 Python
canvas学习笔记之绘制简单路径
2019/01/28 HTML / CSS
美国受欢迎的女性牛仔裤品牌:DL1961
2016/11/12 全球购物
如何整合JQuery和Prototype
2014/01/31 面试题
公司拓展活动方案
2014/02/13 职场文书
《诚实与信任》教学反思
2014/04/10 职场文书
一年级小学生评语
2014/04/22 职场文书
Django展示可视化图表的多种方式
2021/04/08 Python
MySQL 常见存储引擎的优劣
2021/06/02 MySQL
golang用type-switch判断interface的实际存储类型
2022/04/14 Golang
选购到合适的激光打印机
2022/04/21 数码科技
Spring Boot 使用 Spring-Retry 进行重试框架
2022/04/24 Java/Android
python神经网络 tf.name_scope 和 tf.variable_scope 的区别
2022/05/04 Python