作者 | LORENZ KUHN 编译 | ronghuaiyang
简要介绍在PyTorch中加速深度学习模型训练的一些最小改动、影响最大的方法。我既喜欢效率又喜欢ML,所以我想我也可以把它写下来。
[1cycle由两个相同长度的步骤组成,一个是从较低的学习率到较高的学习率,另一个步骤是回到最低的学习速率。最大值应该是使用Learning Rate Finder选择的值,较低的值可以低十倍。然后,这个周期的长度应该略小于epochs的总数,并且,在训练的最后一部分,我们应该允许学习率减少超过最小值几个数量级。
torch.optim.lr_scheduler.CyclicLR
和torch.optim.lr_scheduler.OneCycleLR
。2. 在 DataLoader中使用多个workers和pinned memory
torch.utils.data.DataLoader
时,设置num_workers > 0
,而不是等于0,设置pin_memory=True
而不是默认值False
。详细解释:https://pytorch.org/docs/stable/data.html。num_workers
会增加CPU内存消耗。3. 最大化batch size
4. 使用自动混合精度
<section style="overflow-x: auto;padding: 16px;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;"><span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">import</span> torch<br /><span style="color: rgb(117, 113, 94);line-height: 26px;"># Creates once at the beginning of training</span><br />scaler = torch.cuda.amp.GradScaler()<br /><br /><span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">for</span> data, label <span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">in</span> data_iter:<br /> optimizer.zero_grad()<br /> <span style="color: rgb(117, 113, 94);line-height: 26px;"># Casts operations to mixed precision</span><br /> <span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">with</span> torch.cuda.amp.autocast():<br /> loss = model(data)<br /><br /> <span style="color: rgb(117, 113, 94);line-height: 26px;"># Scales the loss, and calls backward()</span><br /> <span style="color: rgb(117, 113, 94);line-height: 26px;"># to create scaled gradients</span><br /> scaler.scale(loss).backward()<br /><br /> <span style="color: rgb(117, 113, 94);line-height: 26px;"># Unscales gradients and calls</span><br /> <span style="color: rgb(117, 113, 94);line-height: 26px;"># or skips optimizer.step()</span><br /> scaler.step(optimizer)<br /><br /> <span style="color: rgb(117, 113, 94);line-height: 26px;"># Updates the scale for next iteration</span><br /> scaler.update()<br /></span></section>
5. 考虑使用另外的优化器
torch.optim.AdamW
。无论在误差还是训练时间上,AdamW都比Adam表现更好。6. 开启cudNN benchmarking
torch.backends.cudnn.benchmark = True
可能是有益的。这使得cudNN能够测试许多不同的卷积计算方法,然后使用最快的方法。7. 注意CPU和GPU之间频繁的数据传输
tensor.cpu()
和tensor.cuda()
频繁地将张量从GPU和CPU之间相互转换。对于.item()
和.numpy()
也是一样,用.detach()
代替。device=torch.device('cuda:0')
直接将它分配给你的GPU。.to(non_blocking=True)
可能会很有用,只要你没有任何同步点。8. 使用gradient/activation检查点
检查点的工作原理是用计算交换内存,并不是存储整个计算图的所有中间激活用于向后计算,检查点不保存中间的激活,而是在向后传递中重新计算它们。可以应用于模型的任何部分。 具体来说,在向前传递中, function
会以torch.no_grad()
的方式运行,也就是说,不存储中间激活。相反,正向传递保存输入和function
的参数。在向后传递中,将检索保存的输入和function
,并再次根据function
计算向前传递,然后跟踪中间的激活,再使用这些激活值计算梯度。
torch.utils.checkpoint
,需要想点办法才能实现的很好。9. 使用梯度累加
optimizer.step()
之前,在多个.backward()
中累积梯度。<section style="overflow-x: auto;padding: 16px;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;">model.zero_grad() <span style="color: rgb(117, 113, 94);line-height: 26px;"># Reset gradients tensors</span><br /><span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">for</span> i, (inputs, labels) <span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">in</span> enumerate(training_set):<br /> predictions = model(inputs) <span style="color: rgb(117, 113, 94);line-height: 26px;"># Forward pass</span><br /> loss = loss_function(predictions, labels) <span style="color: rgb(117, 113, 94);line-height: 26px;"># Compute loss function</span><br /> loss = loss / accumulation_steps <span style="color: rgb(117, 113, 94);line-height: 26px;"># Normalize our loss (if averaged)</span><br /> loss.backward() <span style="color: rgb(117, 113, 94);line-height: 26px;"># Backward pass</span><br /> <span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">if</span> (i+<span style="line-height: 26px;">1</span>) % accumulation_steps == <span style="line-height: 26px;">0</span>: <span style="color: rgb(117, 113, 94);line-height: 26px;"># Wait for several backward steps</span><br /> optimizer.step() <span style="color: rgb(117, 113, 94);line-height: 26px;"># Now we can do an optimizer step</span><br /> model.zero_grad() <span style="color: rgb(117, 113, 94);line-height: 26px;"># Reset gradients tensors</span><br /> <span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">if</span> (i+<span style="line-height: 26px;">1</span>) % evaluation_steps == <span style="line-height: 26px;">0</span>: <span style="color: rgb(117, 113, 94);line-height: 26px;"># Evaluate the model when we...</span><br /> evaluate_model() <span style="color: rgb(117, 113, 94);line-height: 26px;"># ...have no gradients accumulated</span><br /></span></section>
10. 对于多个GPU使用分布式数据并行
torch.nn.DistributedDataParallel
而不是torch.nn.DataParallel
。通过这样做,每个GPU将由一个专用的CPU核心驱动,避免了DataParallel
的GIL问题。11. 将梯度设为None而不是0
.zero_grad(set_to_none=True)
而不是.zero_grad()
。这样做会让内存分配器去处理梯度,而不是主动将它们设置为0。正如在文档中所说的那样,这会导致产生一个适度的加速,所以不要期待任何奇迹。12. 使用<span style="font-size: 20px;">.as_tensor()</span>
而不是 <span style="font-size: 20px;">.tensor()</span>
torch.tensor()
会拷贝数据,如果你有一个numpy数组,你想转为tensor,使用 torch.as_tensor()
或是 torch.from_numpy()
来避免拷贝数据。13. 需要的时候打开调试工具
14. 使用梯度剪裁
gradient = min(gradient, threshold)
)可以加速收敛。Hugging Face的Transformer实现是关于如何使用梯度剪裁以及其他的一些方法如AMP的一个非常干净的例子。torch.nn.utils.clip_grad_norm_
实现。我并不完全清楚哪个模型从梯度裁剪中获益多少,但它似乎对RNN、基于Transformer和ResNets架构以及一系列不同的优化器都非常有用。15. 在BatchNorm之前不使用bias
torch.nn.Conv2d(..., bias=False, ...)
。16. 在验证的时候关闭梯度计算
torch.no_grad()
。17. 对输入和batch使用归一化
-
你的输入归一化了吗? -
你是否在使用batch-normalization
来自评论的额外的技巧:使用 JIT融合point-wise的操作
<section style="overflow-x: auto;padding: 16px;background: rgb(39, 40, 34);color: rgb(221, 221, 221);display: -webkit-box;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;margin-left: 8px;margin-right: 8px;"><span style="font-size: 15px;"><span style="color: rgb(117, 113, 94);line-height: 26px;">@torch.jit.script</span><br /><span style="line-height: 26px;"><span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">def</span> <span style="color: rgb(166, 226, 46);font-weight: bold;line-height: 26px;">fused_gelu</span><span style="line-height: 26px;">(x)</span>:</span><br /> <span style="color: rgb(249, 38, 114);font-weight: bold;line-height: 26px;">return</span> x * <span style="line-height: 26px;">0.5</span> * (<span style="line-height: 26px;">1.0</span> + torch.erf(x / <span style="line-height: 26px;">1.41421</span>))<br /></span></section>
fused_gelu
的执行速度提高5倍。一些相关的资源
<section data-brushtype="text" style="padding-right: 0em;padding-left: 0em;white-space: normal;font-size: 16px;letter-spacing: 0.544px;color: rgb(62, 62, 62);font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;widows: 1;word-spacing: 2px;caret-color: rgb(255, 0, 0);text-align: center;"><strong style="color: rgb(0, 0, 0);font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;"><span style="letter-spacing: 0.5px;font-size: 14px;"><strong style="font-size: 16px;letter-spacing: 0.544px;"><span style="letter-spacing: 0.5px;">—</span></strong>完<strong style="font-size: 16px;letter-spacing: 0.544px;"><span style="letter-spacing: 0.5px;font-size: 14px;"><strong style="font-size: 16px;letter-spacing: 0.544px;"><span style="letter-spacing: 0.5px;">—</span></strong></span></strong></span></strong></section><pre style="color: rgb(86, 86, 86);font-size: 16px;letter-spacing: 1px;text-align: left;"><pre><section style="letter-spacing: 0.544px;white-space: normal;font-family: -apple-system-font, system-ui, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;"><section powered-by="xiumi.us"><section style="margin-top: 15px;margin-bottom: 25px;opacity: 0.8;"><section><section style="letter-spacing: 0.544px;"><section powered-by="xiumi.us"><section style="margin-top: 15px;margin-bottom: 25px;opacity: 0.8;"><section><section style="margin-bottom: 15px;padding-right: 0em;padding-left: 0em;color: rgb(127, 127, 127);font-size: 12px;font-family: sans-serif;line-height: 25.5938px;letter-spacing: 3px;text-align: center;"><span style="color: rgb(0, 0, 0);"><strong><span style="font-size: 16px;font-family: 微软雅黑;caret-color: red;">为您推荐</span></strong></span></section><p style="margin: 5px 16px;padding-right: 0em;padding-left: 0em;font-family: sans-serif;letter-spacing: 0px;opacity: 0.8;line-height: normal;text-align: center;">一个算法工程师的日常是怎样的?</p><p style="margin: 5px 16px;padding-right: 0em;padding-left: 0em;font-family: sans-serif;letter-spacing: 0px;opacity: 0.8;line-height: normal;text-align: center;">彻底搞懂机器学习中的正则化<br /></p><section style="margin-top: 5px;margin-bottom: 5px;padding-right: 0em;padding-left: 0em;min-height: 1em;font-family: sans-serif;letter-spacing: 0px;opacity: 0.8;line-height: normal;text-align: center;"><span style="font-size: 14px;">13个算法工程师必须掌握的PyTorch Tricks</span></section><section style="margin-top: 5px;margin-bottom: 5px;padding-right: 0em;padding-left: 0em;min-height: 1em;font-family: sans-serif;letter-spacing: 0px;opacity: 0.8;line-height: normal;text-align: center;"><span style="font-size: 14px;">吴恩达上新:生成对抗网络(GAN)专项课程</span></section><section style="margin-top: 5px;margin-bottom: 5px;padding-right: 0em;padding-left: 0em;min-height: 1em;font-family: sans-serif;letter-spacing: 0px;opacity: 0.8;line-height: normal;text-align: center;">从SGD到NadaMax,十种优化算法原理及实现</section></section></section></section></section></section></section></section></section>
本篇文章来源于: 深度学习这件小事
本文为原创文章,版权归知行编程网所有,欢迎分享本文,转载请保留出处!
内容反馈