原文连接:https://efficientdl.com/faster-deep-learning-in-pytorch-a-guide/

目录

  • 写在开头
    • 1. 考虑使用不同的学习率变化策略(Consider using a different learning rate schedule)
    • 2. 在DataLoader中使用多线程并且固定内存(Use multiple workers and pinned memory in DataLoader)
    • 3. BatchSize最大化(Max out the batch size)
    • 4. 使用自动混合精度(Use Automatic Mixed Precision (AMP))
    • 5. 使用不同的优化器(Consider using a different optimizer)
    • 6. 打开cudNN基准测试(Turn on cudNN benchmarking)
    • 7.防止CPU和GPU之间频繁的传输数据(Beware of frequently transferring data between CPUs and GPUs)
    • 8. 使用梯度/激活检查点(Use gradient/activation checkpointing)
    • 9. 使用梯度累积(Use gradient accumulation)
    • 10. 使用DistributedDataParallel进行多GPU训练(Use DistributedDataParallel for multi-GPU training)
    • 11. 将梯度设置为None而不是0(Set gradients to None rather than 0)
    • 12. 使用 .as_tensor 而不是 .tensor()(Use .as_tensor rather than .tensor())
    • 13. 如果不需要,请关闭调试API(Turn off debugging APIs if not needed)
    • 14. 使用梯度剪裁(Use gradient clipping)
    • 15. 在BatchNorm之前关闭偏置(Turn off bias before BatchNorm)
    • 16. 在验证过程中关闭梯度计算(Turn off gradient computation during validation)
    • 17. 使用输入和批次归一化(Use input and batch normalization)
    • 18. 数据变换 (用于数据增强) 可成为速度提升的另一个来源。一些只使用简单 Python 语句的变换可以通过使用 [numba](https://zhuanlan.zhihu.com/p/78882641) 包来加速。
    • 19. 注意排查速度慢的瓶颈是什么

写在开头

在这篇文章中,我将概述一些加速Pytorch深度学习模型训练的最低投入,最大的影响方式。对于每种方法,我将简要总结这个想法,尝试估算预期的加速并讨论一些局限性。我将专注于传达最重要的部分,并指向每个部分的进一步资源。大多数情况下,我将重点关注可以直接在Pytorch内进行的更改,而无需引入其他库,我会假设您正在训练GPU上的模型。
以下建议按照提高速度效果从高到低排序

  1. 考虑使用不同的学习率变化策略(Consider using a different learning rate schedule)
  2. 在DataLoader中使用多线程并且固定内存(Use multiple workers and pinned memory in
    DataLoader)
  3. BatchSize最大化(Max out the batch size)
  4. 使用自动混合精度(Use Automatic Mixed Precision (AMP))
  5. 使用不同的优化器(Consider using a different optimizer)
  6. 打开cudNN基准测试(Turn on cudNN benchmarking)
  7. 防止CPU和GPU之间频繁的传输数据(Beware of frequently transferring data between
    CPUs and GPUs)
  8. 使用梯度/激活检查点(Use gradient/activation checkpointing)
  9. 使用梯度累积(Use gradient accumulation)
  10. 使用DistributedDataParallel进行多GPU训练(Use DistributedDataParallel for
    multi-GPU training)
  11. 将梯度设置为None而不是0(Set gradients to None rather than 0)
  12. 使用 .as_tensor 而不是 .tensor()(Use .as_tensor rather than .tensor())
  13. 如果不需要,请关闭调试API(Turn off debugging APIs if not needed)
  14. 使用梯度剪裁(Use gradient clipping)
  15. 在BatchNorm之前关闭偏置(Turn off bias before BatchNorm)
  16. 在验证过程中关闭梯度计算(Turn off gradient computation during validation)
  17. 使用输入和批次归一化(Use input and batch normalization)

此外还有笔者自己平时在实验中的一些建议
18. 数据变换 (用于数据增强) 可成为速度提升的另一个来源。一些只使用简单 Python 语句的变换可以通过使用 numba 包来加速。
19. 注意排查速度慢的瓶颈是什么。

1. 考虑使用不同的学习率变化策略(Consider using a different learning rate schedule)

选择的学习率时间表对收敛速度以及模型的泛化性能有很大影响。
Leslie Smith提出的周期性学习速率(CLR)以及 1cycle 策略可以令复杂模型的训练迅速完成。
论文连接:
Cyclical Learning Rates for Training Neural Networks
Super-Convergence: Very Fast Training of Neural Networks Using Large Learning Rates
1cycle策略的学习率表如下

[1循环包括]两个相等长度的步骤,一个从较低的学习率到更高的步骤,而不是回到最小值。最大值应该是通过学习率查找器选择的值,较低的值可能会降低十倍。然后,该周期的长度应略低于epochs的总数,在训练的最后一部分中,我们应允许学习率降低到最低限度的数量级。
在最好的情况下,与传统的相比,这个策略实现了大规模的提速。不过有一个缺点,它们引入了一些额外的超参数。
为什么这样做有效?一种可能的解释是,定期增加学习率有助于更快地穿越损失函数中的鞍点。
PyTorch提供了两种方法的实现:
torch.optim.lr_scheduler.CyclicLR、torch.optim.lr_scheduler.OneCycleLR
这些调度程序的一个缺点是他们引入了许多其他超参数。下面的文章和仓库提供了一个很好的概述和实施,以了解如何找到良好的超参数,包括上述学习率查找器。
文章:Hyper-parameter Tuning Techniques in Deep Learning(需要开VPN)
仓库:https://github.com/davidtvs/pytorch-lr-finder

2. 在DataLoader中使用多线程并且固定内存(Use multiple workers and pinned memory in DataLoader)

使用时torch.utils.data.DataLoader,请设置num_workers > 0,而不是默认值0,和pin_memory=True,而不是默认值False。
英伟达高级工程师Szymon Micacz使用了4个工作程序和固定内存,在单个训练时期内将速度提高了两倍。
需要注意的是,在选择worker数量时,建议将设置为可用GPU数量的四倍。worker数量的多和少都会导致速度变慢,数量越多还会增加CPU内存消耗。

3. BatchSize最大化(Max out the batch size)

这是一个有争议的点。但是,通常,似乎使用最大的批量大小,您的GPU内存许可将加速您的培训(例如,请参阅Nvidia的Szymon Migacz)。请注意,如果修改批处理大小,您还必须调整其他超参数,例如学习率。一项经验法则是将学习率翻倍,使学习率加倍。
但是,使用大批量尺寸的缺点之一是,它们可能导致解决方案比受过较小批次训练的解决方案更糟。

4. 使用自动混合精度(Use Automatic Mixed Precision (AMP))

PyTorch 1.6版本就包括了对 PyTorch 的自动混合精度训练的本地实现。
与其他地方使用的单精度(FP32)相比,某些操作可以在半精度(FP16)上运行得更快,并且不会损失准确性。
随后,让AMP自动决定应以什么样的格式执行操作,这样既可以加快训练速度,也可以减少内存占用。
在最好的情况下,AMP的使用如下:

import torch
# Creates once at the beginning of training
scaler = torch.cuda.amp.GradScaler()for data, label in data_iter:optimizer.zero_grad()# Casts operations to mixed precisionwith torch.cuda.amp.autocast():loss = model(data)# Scales the loss, and calls backward()# to create scaled gradientsscaler.scale(loss).backward()# Unscales gradients and calls# or skips optimizer.step()scaler.step(optimizer)# Updates the scale for next iterationscaler.update()

代码来自:https://pytorch.org/docs/stable/amp.html#gradient-scaling
有研究者发现,在NVIDIA V100 GPU上对一些常见的语言和视觉模型进行基准测试时,使用AMP要比常规的FP32训练的速度提升2倍,最高可提升5.5倍。
目前,只有CUDA ops 可以通过这种方式进行自动广播。具体的限制和细节见如下文档
文档:https://pytorch.org/docs/stable/amp.html#op-eligibility

5. 使用不同的优化器(Consider using a different optimizer)

比如AdamW,AdamW是带有权重衰减(而不是L2正则化)的Adam,它在错误实现、训练时间都胜过Adam。
AdamW,Adam都可以结合1Cycle policy使用。
此外,还有一些非本地的优化器值得关注,比如,LARS和LAMB。
NVIDA的APEX实现了一些常见优化器(比如Adam)的融合版本,比如Adam。与Adam的PyTorch实现相比,它避免了多次进出GPU内存的过程,产生了5%左右的速度提升。

6. 打开cudNN基准测试(Turn on cudNN benchmarking)

如果你的模型架构保持固定,输入大小保持不变,则可以设置torch.backends.cudnn.benchmark = True,启动 cudNN 自动调整器。
它将对cudNN中计算卷积的多种不同方法进行基准测试,以获得最佳的性能指标。
这里有一个警告是,如果您最大化上述批处理大小,则这种自动调用可能会变得非常慢。

7.防止CPU和GPU之间频繁的传输数据(Beware of frequently transferring data between CPUs and GPUs)

请谨慎使用tensor.cpu()tensor.cuda(),这些将消耗很多的时间,同样的.item().numpy()也是一样。这些可以使用.detach()代替。
如果正在创建一个张量,就可以使用关键字参数device=torch.device(‘cuda:0’)直接将其分配给你的GPU。

8. 使用梯度/激活检查点(Use gradient/activation checkpointing)

检查点的工作原理,是用计算换取内存。检查点部分不是讲整个计算图的所有中间激活都存储起来向后计算,而不是保存中间激活,在后传中重新计算。
它可以应用到模型的任何部分。
具体来说,在前向传递中,函数将以torch.no_grad()的方式运行,即不存储中间的激活。相反,前向传递会保存输入元组和函数参数。
在后向传递中,检索保存的输入和函数,然后再次对函数进行前向传递计算,现在跟踪中间激活,使用这些激活值计算梯度。
虽然这可能会略微增加你在给定批量大小下的运行时间,但你会显著减少你的内存占用。这反过来又会让你进一步增加你所使用的批次大小,提高GPU的利用率。
虽然检查点本地用作torch.utils.checkpoint,但似乎确实需要一些思考和精力才能正常实施。Priya Goyal有一个很好的教程,展示了检查点的一些关键方面。
教程:https://github.com/prigoyal/pytorch_memonger/blob/master/tutorial/Checkpointing_for_PyTorch_models.ipynb

9. 使用梯度累积(Use gradient accumulation)

另一种增加批次大小的方法是在调用optimizer.step()之前,在多个.backward()通道中累积梯度。
具体实施过程如下:

model.zero_grad()                                   # Reset gradients tensors
for i, (inputs, labels) in enumerate(training_set):predictions = model(inputs)                     # Forward passloss = loss_function(predictions, labels)       # Compute loss functionloss = loss / accumulation_steps                # Normalize our loss (if averaged)loss.backward()                                 # Backward passif (i+1) % accumulation_steps == 0:             # Wait for several backward stepsoptimizer.step()                            # Now we can do an optimizer stepmodel.zero_grad()                           # Reset gradients tensorsif (i+1) % evaluation_steps == 0:           # Evaluate the model when we...evaluate_model()                        # ...have no gradients accumulated

参考:https://medium.com/huggingface/training-larger-batches-practical-tips-on-1-gpu-multi-gpu-distributed-setups-ec88c3e51255
这个方法主要是为了规避GPU内存限制而开发的,但不清楚是否有额外的.backward()循环之间的权衡。

10. 使用DistributedDataParallel进行多GPU训练(Use DistributedDataParallel for multi-GPU training)

这个方法比较常见,如果具体来看的话可能需要单独写一篇文章,但一个简单的方法是使用 torch.nn.DistributedDataParallel 而不是 torch.nn.DataParallel。
这样做可以让每个GPU将由一个专门的CPU核驱动,避免了DataParallel的GIL问题。

11. 将梯度设置为None而不是0(Set gradients to None rather than 0)

使用.zero_grad(set_to_none=True)而不是.zero_grad()
这样做会让内存分配器来处理梯度,而不是主动将它们设置为0,这样会适度加速。
注意,这样做并不是没有副作用的。细节请见文档

12. 使用 .as_tensor 而不是 .tensor()(Use .as_tensor rather than .tensor())

torch.tensor() 总是复制数据。如果你有一个要转换的 numpy 数组,使用 torch.as_tensor() 或 torch.from_numpy() 来避免复制数据。

13. 如果不需要,请关闭调试API(Turn off debugging APIs if not needed)

Pytorch提供了很多调试工具,例如autograd.profilerautograd.grad_checkautograd.anomaly_detection,确保在需要的时候使用它们,不需要时将其关闭,否则他们会拖慢你的训练速度。

14. 使用梯度剪裁(Use gradient clipping)

剪裁梯度,可以加速加速收敛。最初是用来避免RNNs中的梯度爆炸,可以使用torch.nn.utils.clipgrad_norm来实现。
目前尚不清楚哪些模型能靠梯度剪裁能够加速多少,但它似乎对RNNs、基于 Transformer 和 ResNets 的架构以及一系列不同的优化器都非常有用。
huggingface的transformer代码中有很好的使用举例。

15. 在BatchNorm之前关闭偏置(Turn off bias before BatchNorm)

这是一个非常简单的方法,在BatchNormalization图层之前关闭图层的偏置。
对于二维卷积层,可以通过将bias关键字设置为False:来完成torch.nn.Conv2d(…, bias=False, …)

16. 在验证过程中关闭梯度计算(Turn off gradient computation during validation)

在验证期间设置torch.no_grad()

17. 使用输入和批次归一化(Use input and batch normalization)

您可能已经这样做了,但是您可能需要仔细检查:

  • 是否对输入进行了归一化操作
  • 是否已经使用了batch normalization

18. 数据变换 (用于数据增强) 可成为速度提升的另一个来源。一些只使用简单 Python 语句的变换可以通过使用 numba 包来加速。

19. 注意排查速度慢的瓶颈是什么

除了GPU和CPU等硬件的问题导致训练速度慢之外,也要注意排查磁盘IO的问题。可以输出网络的数据加载时间和网络训练时间来判断。
此外,频繁的向LOG文件中写入日志也将导致训练时间打折扣,建议间隔多个iter写入一次,比如50个iter写入一次log文件。

【阅读笔记】针对PyTorch提高神经网络训练速度的方法—《Here are 17 ways of making PyTorch training faster – what did I miss?》相关推荐

  1. 训练神经网络的详细步骤,提高神经网络训练速度

    Hopfield 神经网络有哪几种训练方法 人工神经网络模型主要考虑网络连接的拓扑结构.神经元的特征.学习规则等.目前,已有近40种神经网络模型,其中有反传网络.感知器.自组织映射.Hopfield网 ...

  2. 影响神经网络训练速度的因素使得神经网络训练速度加快的方法

    在深度学习任务中,虽然可以通过堆叠参数.设计更复杂的结构来提高模型的表征能力,但这也会导致模型的计算量增加,训练时间延长.所以,模型的预测有多好并不是唯一的考虑因素,训练时间长会导致成本的上升. 一. ...

  3. 菜鸟笔记-DuReader阅读理解基线模型代码阅读笔记(八)—— 模型训练-训练

    系列目录: 菜鸟笔记-DuReader阅读理解基线模型代码阅读笔记(一)--数据 菜鸟笔记-DuReader阅读理解基线模型代码阅读笔记(二)-- 介绍及分词 菜鸟笔记-DuReader阅读理解基线模 ...

  4. python自训练神经网络_tensorflow学习笔记之简单的神经网络训练和测试

    本文实例为大家分享了用简单的神经网络来训练和测试的具体代码,供大家参考,具体内容如下 刚开始学习tf时,我们从简单的地方开始.卷积神经网络(CNN)是由简单的神经网络(NN)发展而来的,因此,我们的第 ...

  5. situ课题组工作站神经网络训练速度测评

    situ课题组工作站神经网络训练速度测评 测试模型:Unet的一个语义分割模型,3000张图片(训练集2430张,验证集270张,测试集300张): 模型参考: U-Net编程实战--CT影像的肿瘤分 ...

  6. 【深度学习基础知识 - 21】加快模型训练速度的方法

    在深度学习任务中,虽然可以通过堆叠参数.设计更复杂的结构来提高模型的表征能力,但这也会导致模型的计算量增加,训练时间延长,大大降低模型的产出效率.这篇文章根据博主的经验简单介绍一些加快模型训练速度的方 ...

  7. edge浏览器运行不流畅怎么办 提高edge浏览器速度的方法

    edge浏览器是win10系统的专用浏览器,它代替了慢吞吞的IE,很多人觉得edge浏览器运行速度快,但是有时候浏览某些网站的时候会不流畅,所以怎么提高edge浏览器的运行速度呢,小编就来教教大家. ...

  8. Win8怎么加快桌面图标刷新速度?提高图标刷新速度的方法解析

    本文来自BX教程网www.benxitj.com:整理编辑 - 原文出处 www.benxitj.com/Article/?11804/62.html 转载注明 window8 很多时候由于各种内存或 ...

  9. 论文阅读笔记:为什么深度神经网络的训练无论多少次迭代永远有效?可能类内分布已经坍缩为一个点,模型已经崩溃为线性分类器

    论文阅读笔记:Prevalence of neural collapse during the terminalphase of deep learning training,深度学习训练末期普遍的神 ...

最新文章

  1. php指向mysql路径_编译php 但不知道--with-mysql指向哪个路径
  2. 人工智能改变未来教育的5大方式
  3. 2018年科技公司融资纪录十大排行榜!中国四家公司上榜
  4. Android开发--图形图像与动画(三)--Animation效果的XML实现
  5. web项目部署服务器后无响应,Spring4.1.0 - SpringMVC第一步,Tomcat服务器端正常,客户端无法响应JSP页面问题处理...
  6. C# 三种方式实现Socket数据接收(经典)
  7. 阿里P8架构师谈:分布式系统全局唯一ID简介、特点、5种生成方式
  8. python3-Anaconda3 基本使用
  9. 深度学习(三十一)基于深度矩阵分解的属性表征学习
  10. 可变参数列表来实现printf函数的输出
  11. 《2017微信春节数据报告》出炉 初一到初五微信红包收发总量达到460亿个
  12. webserver java_java实现简单webserver(分析+源码)
  13. 有加密狗的软件怎样实现全网络电脑用_加密狗应用领域有哪些?为您揭开7大行业软件保护的奥秘...
  14. 如何成为优秀的技术主管-管理篇
  15. 小程序计算当前定位与店铺距离
  16. USB数据采集卡 USB1208LS、1608FS DAQami 软件功能有哪些
  17. python适合创业吗-python创业
  18. 绘制微信字体大小调节器
  19. 软件工程期末试题及答案
  20. openwrt编译教程(第一章)

热门文章

  1. tlias 教学过程_构建科学的“教学评价体系”,传智专修学院拉近教师与学生的距离...
  2. new Proxy()代理
  3. 关于AD之PCB各层的简单说明
  4. php连接sqlserver数据库
  5. 谷歌浏览器自定义快捷搜索引擎
  6. hdu 5454 Excited Database(线段树)
  7. ElementUI日期转为“yyyy-MM-dd“格式
  8. easypermission坑_Android 权限管理(原生、EasyPermissions、RxPermissions)-阿里云开发者社区...
  9. 咸鱼Micropython—GPIO
  10. 活猫还是死猫?| 薛定谔的猫 | 儿童故事