首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

PyTorch 指南:17个技巧让你的深度学习模型训练变得飞快!

如果你正在 pytorch 中训练深度学习模型,那么如何能够加快模型训练速度呢?

在本文中,我会介绍一些改动最小、影响最大的在pytorch中加速深度学习模型的方法。对于每种方法,我会对其思路进行简要介绍,然后预估提升速度并讨论其限制。我会把我认为重要的部分强调介绍,并在每个部分展示一些实例。接下来我将假设你正在使用GPU训练模型,这些方法基本不需要导入其他的库,只需要再pytorch内进行更改即可。

以下是我根据预估的加速效果对不同方法的排序:

考虑使用其他的学习率调整计划

在DataLoader中使用多个辅助进程并页锁定内存

最大化batch大小

使用自动混合精度AMP

考虑不同的优化器

打开cudNN基准

当心CPU与GPU之间的数据传输

使用梯度/激活检查点

使用梯度累积

多GPU分布式训练

将梯度设置为None而不是0

使用.as_tensor()而不是.tensor()

只在需要的时候打开debugging模式

使用梯度裁剪

在BatchNorm之前忽略偏差

验证时关闭梯度计算

规范化输入和批处理

1.考虑使用其他的学习率调整计划

在训练中使用的学习率调整计划会极大影响收敛速率以及模型泛化能力。

Leslie N. Smith 提出了循环学习率和1Cycle  学习率方法,然后由 fast.ai 的 Jeremy Howard 和 Sylvain Gugger 推广了。总的来说,1Cycle  学习速率方法如下图所示:

在最好的情况下,与传统的学习率策略相比,这种策略可以实现巨大的加速—— Smith称之为“超级收敛”。例如,使用1Cycle策略,在ImageNet上减少了ResNet-56训练迭代数的10倍,就可以匹配原始论文的性能。该策略似乎在通用架构和优化器之间运行得很好。

2. 在DataLoader中使用多个辅助进程并页锁定内存

根据上述方法,Szymon Micacz在四个 worker 和页锁定内存的情况下,在单个epoch中实现了 2 倍加速。

3.最大化batch大小

一直以来,人们对于调大batch没有定论。一般来说,在GPU内存允许的情况下增大batch将会增快训练速度,但同时还需要调整学习率等其他超参数。根据经验,batch大小加倍时,学习率也相应加倍。

然而也要注意,较大的batch会降低模型泛化能力,反之亦然。

4. 使用自动混合精度AMP

PyTorch1.6支持本地自动混合精度训练。与单精度 (FP32) 相比,一些运算在不损失准确率的情况下,使用半精度 (FP16)速度更快。AMP能够自动决定应该以哪种精度执行哪种运算,这样既可以加快训练速度,又减少了内存占用。

AMP的使用如下所示:

目前,只有CUDA支持上述方式,查看本文档了解更多信息。

5. 考虑不同的优化器

AdamW是由fast.ai提出的具有权重衰减(而非 L2 正则化)的Adam, PyTorch中通过torch.optim.AdamW实现。在误差和训练时间上,AdamW都优于Adam。查看此文章了解为什么权重衰减使得Adam产生更好效果。

Adam和AdamW都很适合前文提到的1Cycle策略。

NVIDA的APEX对Adam等常见优化器进行优化融合,相比PyTorch中的原始Adam,由于避免了GPU内存之间的多次传递,训练速度提升约 5%。

6. 打开cudNN基准

至于提速效果,Szymon Migacz在前向卷积时提速70%,在同时向前和后向卷积时提升了27%。

注意,如果你想要根据上述方法最大化批大小,该自动调整可能会非常耗时。

7. 当心CPU与GPU之间的数据传输

通过tensor.cpu()可以将张量从GPU传输到CPU,反之使用tensor.cuda(),但这样的数据转化代价较高。 .item()和.numpy()的使用也是如此,建议使用.detach()。

如果要创建新的张量,使用关键字参数device=torch.device('cuda:0')将其直接分配给GPU。

最好使用.to(non_blocking=True)传输数据,确保传输后没有任何同步点即可。

另外Santosh Gupta的SpeedTorch也值得一试,尽管其加速与否尚不完全清除。

8.使用梯度/激活检查点

检查点通过将计算保存到内存来工作。检查点在反向传播算法过程中并不保存计算图的中间激活,而是在反向传播时重新计算,其可用于模型的任何部分。

具体来说,在前向传播中,function以torch.no_grad()方式运行,不存储任何中间激活。相反,前向传递将保存输入元组和function参数。在反向传播时,检索保存的输入和function,并再次对function进行正向传播,记录中间激活,并使用这些激活值计算梯度。

因此,对于特定的批处理大小,这可能会稍微增加运行时间,但会显着减少内存消耗。反过来,你可以进一步增加批处理大小,从而更好地利用GPU。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20210126A04VO700?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券