网络编程
位置:首页>> 网络编程>> Python编程>> Pytorch 高效使用GPU的操作

Pytorch 高效使用GPU的操作

作者:血_影  发布时间:2021-10-25 04:45:23 

标签:Pytorch,GPU

前言

深度学习涉及很多向量或多矩阵运算,如矩阵相乘、矩阵相加、矩阵-向量乘法等。深层模型的算法,如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,当然使用精度稍微低一点的数据类型有时也效果。

来源:https://blog.csdn.net/xxboy61/article/details/97973288

0
投稿

猜你喜欢

  • 在处理css的机制上,IE总是有很多让人吐血的举动,但对于他们现在的改进力度还是值得高兴的。就拿对伪类:hover的支持来说,IE7+终于添
  • 前言没有用过的东西,没有深刻理解的东西很难说自己会,而且被别人一问必然破绽百出。虽然之前有接触过python协程的概念,但是只是走马观花,这
  • 下面看下python调用函数加括号和不加括号的区别,具体代码如下所示; def  bracket(data):return dat
  • 在风起云涌的互联网浪潮中,产品迭代的速度越来越快。随着用户需求的激增,也不断带来了对设计师能力要求的提高。初入交互设计领域几年来,明显发现可
  • python可以方便地支持多线程。可以快速创建线程、互斥锁、信号量等等元素,支持线程读写同步互斥。美中不足的是,python的运行在pyth
  • 刚刚心血来潮,编了一个国际域名查询的功能页面,比较简单,没有做什么美化和修饰,主要利用了服务器端的XMLHTTP访问第三方服务器实现域名查询
  • 某天和一个产品经理聊起:以用户为中心是一个理想概念。经历了太多的项目,看到了太多的限制条件。而我向来不是一个有着设计洁癖的完美主义者。做为所
  • 安装python中文分词库jieba法1:Anaconda Prompt下输入conda install jieba法2:Terminal下
  • 这篇文章主要介绍了python的time模块和datetime模块实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参
  • 文章出处:https://blog.csdn.net/sdksdk0/article/details/80933444作者:朱培 
  • JavaScript/Dom中有很多很零碎的东西,让人总是感觉理解的有些“朦胧”,因此,有时候还是应该总结一下,对于Event对象,前两天看
  • Python面向对象编程(一)Python面向对象编程(二)Python面向对象编程(三)一、isinstance和issubclassty
  • 众所周知windows平台漏洞百出,补丁一个接一个,但总是补也补不净。我把我所知道的看asp源码的方法总结了一下,并且用c#写了个应用程序来
  • 编码规范Python 编码规范重要性的原因用一句话来概括就是:统一的编码规范可以提高开发效率。无论你是 编程者,还是 阅读者,好的规范能让你
  • 利用Python中的socket模块中的来实现UDP协议,这里写一个简单的服务器和客户端。为了说明网络编程中UDP的应用,这里就不写图形化了
  • 为方便用ipset 来管理防火墙,写了下面Ipset类来对Ip进行管理#!/usr/bin/env python# coding: utf-
  • 在DreamWeaver中编写CSS,这种编写习惯并不提倡,不过由于"可视化"和操作简便,使用的朋友依然很多,今天罗列一
  • python程序结构python“一切皆对象”,这是接触python听到最多的总结了。在python中最基层的单位应该就是对象了,对象需要靠
  • 数据库并行访问,也就是两个或两以上用户同时访问同一数据,这也是数据库引擎如何设计和实现适度反应所面临的最大问题。设计优良、性能卓越的数据库引
  • reload() 简介作用:用于重新载入之前载入的模块语法格式:reload(module)参数:module为模块对象,必须已经被加载返回
手机版 网络编程 asp之家 www.aspxhome.com