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正则表达式修复网站文章字体不统一的解决方法
Feb 21 Python
浅谈Python 集合(set)类型的操作——并交差
Jun 30 Python
python xml解析实例详解
Nov 14 Python
Tensorflow实现AlexNet卷积神经网络及运算时间评测
May 24 Python
关于python写入文件自动换行的问题
Jun 23 Python
使用Python实现跳帧截取视频帧
May 31 Python
Python产生一个数值范围内的不重复的随机数的实现方法
Aug 21 Python
pyqt5 QScrollArea设置在自定义侧(任何位置)
Sep 25 Python
python orm 框架中sqlalchemy用法实例详解
Feb 02 Python
python如何操作mysql
Aug 17 Python
Python如何利用Har文件进行遍历指定字典替换提交的数据详解
Nov 05 Python
彻底解决pip下载pytorch慢的问题方法
Mar 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&mysql(一)
2006/10/09 PHP
一个基于PDO的数据库操作类(新) 一个PDO事务实例
2011/07/03 PHP
ThinkPHP3.1新特性之Action参数绑定
2014/06/19 PHP
CodeIgniter框架实现的整合Smarty引擎DEMO示例
2019/03/28 PHP
求解开jscript.encode代码的asp函数
2007/02/28 Javascript
javascript 关闭IE6、IE7
2009/06/01 Javascript
IE 条件注释详解总结(附实例代码)
2009/08/29 Javascript
jQuery Ajax 实例全解析
2011/04/20 Javascript
jQuery实现的省市县三级联动菜单效果完整实例
2016/08/01 Javascript
JavaScript动态数量的文件上传控件
2016/11/18 Javascript
利用VUE框架,实现列表分页功能示例代码
2017/01/12 Javascript
基于Vue 2.0的模块化前端 UI 组件库小结
2017/12/21 Javascript
GOJS+VUE实现流程图效果
2018/12/01 Javascript
详解如何提升JSON.stringify()的性能
2019/06/12 Javascript
vue倒计时刷新页面不会从头开始的解决方法
2020/03/03 Javascript
[11:33]DAC2018 4.5SOLO赛决赛 MidOne vs Paparazi第二场
2018/04/06 DOTA
[01:51]开启你的城市传奇 完美世界城市挑战赛开始报名
2018/10/09 DOTA
python使用xlrd实现检索excel中某列含有指定字符串记录的方法
2015/05/09 Python
Python中字符串的格式化方法小结
2016/05/03 Python
Mac 上切换Python多版本
2017/06/17 Python
机器学习10大经典算法详解
2017/12/07 Python
python使用Plotly绘图工具绘制水平条形图
2020/03/25 Python
关于python3中setup.py小概念解析
2019/08/22 Python
python多进程(加入进程池)操作常见案例
2019/10/21 Python
python如何更新包
2020/06/11 Python
Python实现自动签到脚本功能
2020/08/20 Python
css3实现的下拉菜单效果示例
2014/01/22 HTML / CSS
英国床和浴室商场:Bed & Bath Emporium
2018/05/20 全球购物
SNIDEL官网:日本VIVI杂志人气少女第一品牌
2020/03/12 全球购物
大学生职业生涯规划书前言
2014/01/09 职场文书
安全生产宣传标语
2014/06/06 职场文书
幼儿园圣诞节活动总结
2015/05/06 职场文书
社区安置帮教工作总结2015
2015/05/20 职场文书
实习感想范文
2015/08/10 职场文书
LeetCode189轮转数组python示例
2022/08/05 Python
CSS 鼠标点击拖拽效果的实现代码
2022/12/24 HTML / CSS