十大提高模型训练效率的PyTorch技巧,你都知道吗?

作者 | 啥都会一点的研究生  编辑 | 3D视觉开发者社区

期整理了十个有助于提高训练效率的PyTorch技巧,一起来学习吧

首先第一个,我们都知道学习率在很大程度上影响着模型训练的收敛速度与泛化性能

听到这句话有同学就有疑问了,收敛速度确实很直观能感受到,但这和泛化性能有什么关系?

首先,我们的模型训练目的是学习隐含在数据背后的规律,对于规律相似的数据集以外的数据,经过训练后也可以给出合适输出,这个能力被称为泛化能力

所以,当学习率影响了收敛,换言之就是这个模型训练不恰当,那么准召率随之下降,自然影响模型输出,即泛化性能差

当然这个知识点属于题外话,重点还是在学习率,近几年来,相比于固定的学习率,周期性学习率策略被证明是更有效训练方式

比如fast.ai经典的1 cycle学习率策略,该学习率调整大致如图所示

首先是两个等长的变换步骤,从最小学习率到最大学习率再回到最小值,最大值为最小值的十倍且整个迭代循环步长应略小于总epoch

在训练的最后部分应允许学习率下降超过最小值几个数量级

在这篇论文中谈到1 cycle策略实现了巨大加速,作者称之为superconvergence,一种可能的解释是有规律的改变学习率有助于更快越过鞍点

在Pytorch中已经有了相应的实现

此外,优化器也被提及是提高训练效率的有效方式,fast.ai指出,使用具有权重衰减而不是L2正则化的AdamW

在训练时间与出现错误的情况都优于Adam,同样的,pytorch已经集成该功能torch.optim.AdamW

第二个,batch size,同样很大程度影响训练效果,但其实它还是挺有争议的

通常情况下,使用GPU内存允许的最大batch size可能会加速训练

但是它可能会导致训练效果比小的batch size更差

值得一提的是,如果修改了batch size,还必须同步调整如学习率等超参

一个经验之谈是batch size加倍时学习率也需要加倍

OpenAI也有一篇关于不同大batch size所需收敛步骤数的论文可以参考

第三个,使用torch.utils.data.DataLoader时,num_workers默认为0

他的用处是告诉DataLoader使用多少个子进程进行数据加载

若设置为0,则每轮迭代不会有子进程将数据自主加载至内存,找不到batch数据再加载,所以速度很慢

当不为0时,dataloader将一次性创建多个子进程并分配属于各自的batch,每个子进程负责将batch加载进内存

这也就意味着找寻batch的速度变的很快,因为后面的迭代数据在前几轮就早已加载完毕

但是一个很明显的缺点,如果num_worker设置大,内存开销也随之变大,加重CPU负担

虽然num worker与CPU相关,但是一个常用的经验是设置为可用GPU数量的四倍,过大过小都将导致速度变慢

此外,如果你的硬件性能很好,可以将pin_memory设置为True,这样将数据加载至gpu上的速度更快

第四个,使用自动混合精度训练,也就是常说的AMP,Automatic Mixed Precision,在Pytorch1.6中已经集成了这个功能

其实现流程如下所示

他的主要思想是在某些操作下使用半精度FP16而不是一味的全部使用单精度FP32

AMP会自动决定应该以哪种格式执行什么操作,不损失精度的前提下达到更快训练速度与占用更小内存的目的

第五个,如果我们的模型结构固定且保持输入大小不变,将torch.backends.cudnn.benchmark设置为True可以加速训练

因为这会使得cudNN中自动调整参数的模块对不同计算卷积的方式进行benchmark测试,选择最优卷积方法

需要注意的是启动算法前期较慢,但跑起来之后很快,且若输入大小改变,每变一次则需要重新配置计算,会使得效率变低

点击小程序  观看视频版讲解 

第六个,关于分布式训练,按Pytorch官网所述,虽然torch.nn.DataParallel能够以最低的编码实现单机多卡并行,且只需要修改一行代码,很容易上手

但是通常无法提供最佳性能,因为它在每一次前向传播都会复制模型,并且其单进程多线程并行性自然会受到Python中GIL,也就是全局解释器锁的影响

Pytorch呢推介使用

torch.nn.parallel.DistributedDataParallel

使用多进程并行,因此模型副本之间没有GIL争用。此外,该模型在 DDP构建时进行broadcast,而不是在每次的前向传播时进行,这也有助于加快训练速度

第七个,使用梯度累加,其实这也是增大batch的另一种方式,核心思想是在调用 optimizer.step() 之前,在多个 .backward() 过程中累积梯度

训练时的实现过程如代码所示

首先重置梯度,在for循环遍历数据时,喂入模型进行前向传播,紧接着计算loss,除以累积步长也就是取均值,再反向传播,每当达到累计步长再进行优化器更新与重置梯度

其实刚知道这个策略时我也不太清楚是否有效,这种方法主要是为了规避GPU内存限制而设计,fast.ai论坛上关于这个的讨论似乎表明可以加速训练,大家可以私下试试

噢,此外,如果你是在进行验证阶段,千万要记得设置torch.no_grad()

第八个,说到了梯度累加,那么一定有梯度裁剪,最初用于避免 RNN 中的梯度爆炸,可以粗略的表示为gradient = min(gradient, threshold),达到加速收敛的目的

在 PyTorch 中,可以使用 torch.nn.utils.clip_grad_norm_实现

目前被证明对基于 Transformer 和 ResNets等架构非常有用

第九个,关于BatchNormalization需要知道的是,如果在二维卷积层后紧跟BN层,那么需要将torch.nn.Conv2d(..., bias=False, ...)中的bias设置为False

什么是batch normalization?

在早期神经网络训练时,只是对输入数据进行归一化处理,却忽视了中间层。虽然对输入数据进行了归一化,但数据经过 矩阵乘法以及非线性运算之后,数据分布很可能被改变

随着深度网络的多层运算之后,数据分布的变化将越来越大。如果能在网络的中间也进行归一化处理,对网络的训练起到改进作用

这种在神经网络中间层也进行归一化处理,就是批归一化Batch Normalization(BN)

所以BN的作用是将数据归一化至标准分布,所以添加bias并不会起到作用

但是若使用bias,将使得权重变大且降低效率

最后一些陋习需要提出来一起记住,比如避免频繁的使用tensor.cpu()与tensor.cuda()将tensor在GPU与CPU间来回转换

再是我们经常会将numpy数据转换至tensor,有的可能会使用torch.tensor(),这个操作虽然可以达到目的,但它将执行复制数据操作,效率低

可以使用torch.as_tensor() 或 torch.from_numpy() 避免这种情况。

版权声明:本文为奥比中光3D视觉开发者社区特约作者授权原创发布,未经授权不得转载,本文仅做学术分享,版权归原作者所有,若涉及侵权内容请联系删文。

3D视觉开发者社区是由奥比中光给所有开发者打造的分享与交流平台,旨在将3D视觉技术开放给开发者。平台为开发者提供3D视觉领域免费课程、奥比中光独家资源与专业技术支持。

加入【3D视觉开发者社区】学习行业前沿知识,赋能开发者技能提升!
加入【3D视觉AI开放平台】体验AI算法能力,助力开发者视觉算法落地!

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值