高效使用PyTorch,一套PyTorch的最佳实践和代码风格,非常有用,错过就太可惜了!...

本文主要是介绍高效使用PyTorch,一套PyTorch的最佳实践和代码风格,非常有用,错过就太可惜了!...,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

点击上方“AI公园”,关注公众号,选择加“星标“或“置顶”


作者:IgorSusmelj

编译:ronghuaiyang

导读

PyTorch1.0之后,越来越多的人选择使用PyTorch,今天给大家介绍一个github项目,作者通过自己使用PyTorch的实际工程经验,总结出了一套非常有用的使用PyTorch的最佳实践,涉及到使用PyTorch的方方面面,看了之后非常有收获!


640?wx_fmt=png

不是PyTorch的官方风格指南。本文总结了使用PyTorch框架进行深度学习的一年多经验中的最佳实践。请注意,我们分享的经验大多来自研究和创业的视角。

这是一个开放的项目,欢迎其他合作者编辑和改进文档。

该文档有三个主要部分。首先,简要回顾一下Python中的最佳实践,然后介绍一些使用PyTorch的技巧和建议。最后,我们分享了一些使用其他框架的见解和经验,这些框架通常对我们改进工作流有帮助。

我们推荐使用Python 3.6+

根据我们的经验,我们推荐使用Python 3.6+,因为它具有以下特性,这些特性对于简洁的代码非常方便:

  • 从Python 3.6开始支持typing

  • 从Python 3.6开始支持f string

Python风格指南回顾

我们尝试遵循Python的谷歌样式指南。

请参考文档丰富的谷歌提供的python代码风格指南。

我们在此提供最常用规则的摘要:

命名规范

类型规范例子
Packages & Moduleslowerwithunderfrom prefetch_generator import BackgroundGenerator
ClassesCapWordsclass DataLoader
ConstantsCAPSWITHUNDERBATCH_SIZE=16
Instanceslowerwithunderdataset = Dataset
Methods & Functionslowerwithunder()def visualize_tensor()
Variableslowerwithunderbackground_color='Blue'

IDEs

代码编辑器

通常,我们推荐使用IDE,比如visual studio code或PyCharm。而VS Code在相对轻量级的编辑器中提供语法高亮和自动完成功能,PyCharm有许多用于处理远程集群的高级功能。

Jupyter Notebook vs Python Scripts

一般来说,我们建议使用 jupyter notebooks进行初步探索/尝试新的模型和代码。

如果你想在更大的数据集上训练模型,就应该使用Python脚本,因为在更大的数据集上,复现性更重要。

我们的推荐的工作流:

  1. 从jupyter notebook开始

  2. 研究数据和模型

  3. 在notebook的单元格中构建类/方法

  4. 将代码移动到python脚本

  5. 在服务器上进行训练/部署

Jupyter NotebookPython Scripts
+ Exploration+ Running longer jobs without interruption
+ Debugging+ Easy to track changes with git
- Can become a huge file- Debugging mostly means rerunning the whole script
- Can be interrupted (don't use for long training)
- Prone to errors and become a mess

常用的库:

NameDescriptionUsed for
torchBase Framework for working with neural networkscreating tensors, networks and training them using backprop
torchvisiontododata preprocessing, augmentation, postprocessing
Pillow (PIL)Python Imaging LibraryLoading images and storing them
NumpyPackage for scientific computing with PythonData preprocessing & postprocessing
prefetch_generatorLibrary for background processingLoading next batch in background during computation
tqdmProgress barProgress during training of each epoch
torchsummaryKeras summary for PyTorchDisplays network, it's parameters and sizes at each layer
tensorboardxTensorboard without tensorflowLogging experiments and showing them in tensorboard

文件结构

不要把所有层和模型都放在同一个文件中。最佳实践是将最终的网络分离到一个单独的文件中(network .py),并将层、损失和操作符保存在各自的文件中(layers.py,loss.py,ops.py)。完成的模型(由一个或多个网络组成)应该在一个具有其名称的文件中引用(例如yolov3.py,DCGAN.py)

主例程、各自的训练脚本和测试脚本应该只从具有模型名称的文件中导入。

使用PyTorch构建神经网络

我们建议将网络分解为更小的可重用部分。网络是一个神经网络。模块由操作或其他神经网络组成。模块作为构建块。损失函数也是nn.Module。因此,可以直接集成到网络中。

继承自nn.Module的类,必须有一个forward方法来实现相应层或操作的前向。

nn.module可以在输入数据上使用self.net(input),这就是使用了 call()方法来通过模块提供输入。

output = self.net(input)

PyTorch的一个简单的网络

对于单输入单输出的简单网络,请使用以下模式:

class ConvBlock(nn.Module):	def __init__(self):	super(ConvBlock, self).__init__()	block = [nn.Conv2d(...)]	block += [nn.ReLU()]	block += [nn.BatchNorm2d(...)]	self.block = nn.Sequential(*block)	def forward(self, x):	return self.block(x)	
class SimpleNetwork(nn.Module):	def __init__(self, num_resnet_blocks=6):	super(SimpleNetwork, self).__init__()	# here we add the individual layers	layers = [ConvBlock(...)]	for i in range(num_resnet_blocks):	layers += [ResBlock(...)]	self.net = nn.Sequential(*layers)	def forward(self, x):	return self.net(x)

请注意以下几点:

  • 我们重用简单的循环构建块,如ConvBlock,它由相同的循环模式(卷积、激活、归一化)组成,并将它们放入单独的nn.Module中

  • 我们建立一个所需层的列表,最后使用nn.Sequential()将它们转换成一个模型。我们在list对象之前使用*操作符来展开它。

  • 在前向传递中,我们只是通过模型运行输入

在PyTorch中使用带有跳跃连接的网络

class ResnetBlock(nn.Module):	def __init__(self, dim, padding_type, norm_layer, use_dropout, use_bias):	super(ResnetBlock, self).__init__()	self.conv_block = self.build_conv_block(...)	def build_conv_block(self, ...):	conv_block = []	conv_block += [nn.Conv2d(...),	norm_layer(...),	nn.ReLU()]	if use_dropout:	conv_block += [nn.Dropout(...)]	conv_block += [nn.Conv2d(...),	norm_layer(...)]	return nn.Sequential(*conv_block)	def forward(self, x):	out = x + self.conv_block(x)	return out

在这里,实现了一个ResNet block的跳跃连接。PyTorch允许在向前传递期间进行动态操作。

在PyTorch使用多个输出的网络

对于一个需要多个输出的网络,例如使用一个预先训练好的VGG网络构建感知机loss,我们使用以下模式:

class Vgg19(torch.nn.Module):	def __init__(self, requires_grad=False):	super(Vgg19, self).__init__()	vgg_pretrained_features = models.vgg19(pretrained=True).features	self.slice1 = torch.nn.Sequential()	self.slice2 = torch.nn.Sequential()	self.slice3 = torch.nn.Sequential()	for x in range(7):	self.slice1.add_module(str(x), vgg_pretrained_features[x])	for x in range(7, 21):	self.slice2.add_module(str(x), vgg_pretrained_features[x])	for x in range(21, 30):	self.slice3.add_module(str(x), vgg_pretrained_features[x])	if not requires_grad:	for param in self.parameters():	param.requires_grad = False	def forward(self, x):	h_relu1 = self.slice1(x)	h_relu2 = self.slice2(h_relu1)        	h_relu3 = self.slice3(h_relu2)        	out = [h_relu1, h_relu2, h_relu3]	return out

请注意以下事项:

  • 我们使用torchvision提供的预训练模型。

  • 我们把网络分成三个部分。每个切片由来自预训练模型的层组成。

  • 我们将冻结的网络设置成requires_grad = False

  • 返回一个包含切片的三个输出的列表

自定义Loss

即使PyTorch已经有很多标准的损失函数,有时也需要创建自己的损失函数。为此,需要创建一个单独的文件 losses.py ,然后扩展 nn.Module类创建自定义损失函数:

class CustomLoss(torch.nn.Module):	def __init__(self):	super(CustomLoss,self).__init__()	def forward(self,x,y):	loss = torch.mean((x - y)**2)	return loss

训练模型的推荐代码结构

注意,我们使用了以下模式:

  • 我们使用从prefetch_generator中的BackgroundGenerator加载下一个batch的数据

  • 我们使用tqdm来监控训练进度,并显示计算效率。这有助于我们发现数据加载管道中的瓶颈。

# import statements	
import torch	
import torch.nn as nn	
from torch.utils import data	
...	
# set flags / seeds	
torch.backends.cudnn.benchmark = True	
np.random.seed(1)	
torch.manual_seed(1)	
torch.cuda.manual_seed(1)	
...	
# Start with main code	
if __name__ == '__main__':	# argparse for additional flags for experiment	parser = argparse.ArgumentParser(description="Train a network for ...")	...	opt = parser.parse_args() 	# add code for datasets (we always use train and validation/ test set)	data_transforms = transforms.Compose([	transforms.Resize((opt.img_size, opt.img_size)),	transforms.RandomHorizontalFlip(),	transforms.ToTensor(),	transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))	])	train_dataset = datasets.ImageFolder(	root=os.path.join(opt.path_to_data, "train"),	transform=data_transforms)	train_data_loader = data.DataLoader(train_dataset, ...)	test_dataset = datasets.ImageFolder(	root=os.path.join(opt.path_to_data, "test"),	transform=data_transforms)	test_data_loader = data.DataLoader(test_dataset ...)	...	# instantiate network (which has been imported from *networks.py*)	net = MyNetwork(...)	...	# create losses (criterion in pytorch)	criterion_L1 = torch.nn.L1Loss()	...	# if running on GPU and we want to use cuda move model there	use_cuda = torch.cuda.is_available()	if use_cuda:	net = net.cuda()	...	# create optimizers	optim = torch.optim.Adam(net.parameters(), lr=opt.lr)	...	# load checkpoint if needed/ wanted	start_n_iter = 0	start_epoch = 0	if opt.resume:	ckpt = load_checkpoint(opt.path_to_checkpoint) # custom method for loading last checkpoint	net.load_state_dict(ckpt['net'])	start_epoch = ckpt['epoch']	start_n_iter = ckpt['n_iter']	optim.load_state_dict(ckpt['optim'])	print("last checkpoint restored")	...	# if we want to run experiment on multiple GPUs we move the models there	net = torch.nn.DataParallel(net)	...	# typically we use tensorboardX to keep track of experiments	writer = SummaryWriter(...)	# now we start the main loop	n_iter = start_n_iter	for epoch in range(start_epoch, opt.epochs):	# set models to train mode	net.train()	...	# use prefetch_generator and tqdm for iterating through data	pbar = tqdm(enumerate(BackgroundGenerator(train_data_loader, ...)),	total=len(train_data_loader))	start_time = time.time()	# for loop going through dataset	for i, data in pbar:	# data preparation	img, label = data	if use_cuda:	img = img.cuda()	label = label.cuda()	...	# It's very good practice to keep track of preparation time and computation time using tqdm to find any issues in your dataloader	prepare_time = start_time-time.time()	# forward and backward pass	optim.zero_grad()	...	loss.backward()	optim.step()	...	# udpate tensorboardX	writer.add_scalar(..., n_iter)	...	# compute computation time and *compute_efficiency*	process_time = start_time-time.time()-prepare_time	pbar.set_description("Compute efficiency: {:.2f}, epoch: {}/{}:".format(	process_time/(process_time+prepare_time), epoch, opt.epochs))	start_time = time.time()	# maybe do a test pass every x epochs	if epoch % x == x-1:	# bring models to evaluation mode	net.eval()	...	#do some tests	pbar = tqdm(enumerate(BackgroundGenerator(test_data_loader, ...)),	total=len(test_data_loader)) 	for i, data in pbar:	...	# save checkpoint if needed	...

在PyTorch使用多GPU训练

PyTorch中有两种使用多个gpu进行训练的模式。

从我们的经验来看,这两种模式都是有效的。然而,第一个方法的结果是代码更好、更少。由于gpu之间的通信更少,第二种方法似乎具有轻微的性能优势。

分割每个网络的batch

最常见的一种方法是简单地将所有“网络”的batch分配给各个gpu。

因此,如果一个模型运行在一个批处理大小为64的GPU上,那么它将运行在两个GPU上,每个GPU的批处理大小为32。这可以通过使用nn.DataParallel(model)自动完成。

将所有的网络打包进一个super网络,并把输入batch分割

这种模式不太常用。实现这种方法的repository在pix2pixHD implementation by Nvidia

该做的和不该做的

避免在nn.Module的forward方法找那个使用Numpy代码

Numpy运行在CPU上,比torch代码慢。由于torch的开发思路与numpy相似,所以大多数numpy函数已经得到了PyTorch的支持。

从main代码中分离DataLoader

数据加载管道应该独立于你的主训练代码。PyTorch使用后台来更有效地加载数据,并且不会干扰主训练过程。

不要在每一次迭代中打印日志结果

通常我们训练我们的模型数千个迭代。因此,每n步记录损失和其他结果就足以减少开销。特别是,在训练过程中,将中间结果保存为图像可能非常耗时。

使用命令行参数

使用命令行参数在代码执行期间设置参数(批处理大小、学习率等)非常方便。跟踪实验参数的一个简单方法是打印从parse_args接收到的字典:

...	
# saves arguments to config.txt file	
opt = parser.parse_args()	
with open("config.txt", "w") as f:	f.write(opt.__str__())	
...

可能的话,使用.detach()将张量从图中释放出来

PyTorch跟踪所有涉及张量的操作,以实现自动微分。使用.detach()防止记录不必要的操作。

使用.item()打印标量数据

你可以直接打印变量,但是建议使用variable.detach()variable.item()。在早期的PyTorch版本< 0.4中,必须使用.data访问一个变量的张量。

在nn.Module中使用函数调用而不是直接用forward

下面这两种方式是不一样的:

output = self.net.forward(input)	
# they are not equal!	
output = self.net(input)

FAQ

  1. 如何让实验可复现?

我们建议在代码开头设置以下种子:

np.random.seed(1)	
torch.manual_seed(1)	
torch.cuda.manual_seed(1)
  1. 如何进一步提升训练和推理速度?

在Nvidia GPUs上,你可以在代码的开头添加以下行。这将允许cuda后端在第一次执行时优化你的图。但是,要注意,如果改变网络输入/输出张量的大小,那么每次发生变化时,图都会被优化。这可能导致运行非常慢和内存不足错误。只有当输入和输出总是相同的形状时才设置此标志。通常情况下,这将导致大约20%的改善。

torch.backends.cudnn.benchmark = True
  1. 使用tqdm + prefetch_generator模式计算效率的最佳值是什么?

这取决于使用的机器、预处理管道和网络大小。在一个1080Ti GPU上使用SSD硬盘,我们看到一个几乎为1.0的计算效率,这是一个理想的场景。如果使用浅(小)网络或慢速硬盘,这个数字可能会下降到0.1-0.2左右,这取决于你的设置。

  1. 即使我没有足够的内存,我如何让batch size > 1?

在PyTorch中,我们可以很容易地实现虚拟batch sizes。我们只是不让优化器每次都更新参数,并把batch_size个梯度加起来。

...	
# in the main loop	
out = net(input)	
loss = criterion(out, label)	
# we just call backward to sum up gradients but don't perform step here	
loss.backward() 	
total_loss += loss.item() / batch_size	
if n_iter % batch_size == batch_size-1:	# here we perform out optimization step using a virtual batch size	optim.step()	optim.zero_grad()	print('Total loss: ', total_loss)	total_loss = 0.0	
...
  1. 在训练过程中如何调整学习率?

我们可以直接使用实例化的优化器得到学习率,如下所示:

...	
for param_group in optim.param_groups:	old_lr = param_group['lr']	new_lr = old_lr * 0.1	param_group['lr'] = new_lr	print('Updated lr from {} to {}'.format(old_lr, new_lr))	
...
  1. 在训练中如何使用一个预训练的模型作为损失(没有后向传播)

如果你想使用一个预先训练好的模型,如VGG来计算损失,但不训练它(例如在style-transfer/GANs/Auto-encoder中的感知损失),你可以使用以下模式:

...	
# instantiate the model	
pretrained_VGG = VGG19(...)	
# disable gradients (prevent training)	
for p in pretrained_VGG.parameters():  # reset requires_grad	p.requires_grad = False	
...	
# you don't have to use the no_grad() namespace but can just run the model	
# no gradients will be computed for the VGG model	
out_real = pretrained_VGG(input_a)	
out_fake = pretrained_VGG(input_b)	
loss = any_criterion(out_real, out_fake)	
...
  1. 在PyTorch找那个为什么要用.train()* 和 .eval()?

这些方法用于将BatchNorm2dDropout2d等层从训练模式设置为推理模式。每个模块都继承自nn.Module有一个名为istrain的属性。.eval().train()只是简单地将这个属性设置为True/ False。有关此方法如何实现的详细信息,请参阅PyTorch中的module代码。

  1. 我的模型在推理过程中使用了大量内存/如何在PyTorch中正确运行推理模型?

确保在代码执行期间没有计算和存储梯度。你可以简单地使用以下模式来确保:

with torch.no_grad():	# run model here	out_tensor = net(in_tensor)
  1. 如何微调预训练模型?

在PyTorch你可以冻结层。这将防止在优化步骤中更新它们。

# you can freeze whole modules using	
for p in pretrained_VGG.parameters():  # reset requires_grad	p.requires_grad = False
  1. 什么时候用Variable(...)?

从PyTorch 0.4开始Variable和Tensor就合并了,我们不用再显式的构建Variable对象了。

  1. PyTorch在C++上比Python快吗?

C++版本的速度快10%

  1. TorchScript / JIT可以加速代码吗?

Todo...

  1. PyTorch代码使用cudnn.benchmark=True会变快吗?

根据我们的经验,你可以获得约20%的加速。但是,第一次运行模型需要相当长的时间来构建优化的图。在某些情况下(前向传递中的循环、没有固定的输入形状、前向中的if/else等等),这个标志可能会导致内存不足或其他错误。

  1. 如何使用多GPUs训练?

Todo...

  1. PyTorch中的.detach()是怎么工作的?

如果从计算图中释放一个张量,这里有一个很好的图解:http://www.bnikolic.co.uk/blog/pytorch-detach.html


640?wx_fmt=png— END—

英文原文:https://github.com/IgorSusmelj/pytorch-styleguide

640?wx_fmt=jpeg

请长按或扫描二维码关注本公众号

喜欢的话,请给我个好看吧640?wx_fmt=gif


这篇关于高效使用PyTorch,一套PyTorch的最佳实践和代码风格,非常有用,错过就太可惜了!...的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1080844

相关文章

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

高效录音转文字:2024年四大工具精选!

在快节奏的工作生活中,能够快速将录音转换成文字是一项非常实用的能力。特别是在需要记录会议纪要、讲座内容或者是采访素材的时候,一款优秀的在线录音转文字工具能派上大用场。以下推荐几个好用的录音转文字工具! 365在线转文字 直达链接:https://www.pdf365.cn/ 365在线转文字是一款提供在线录音转文字服务的工具,它以其高效、便捷的特点受到用户的青睐。用户无需下载安装任何软件,只