|高性能PyTorch是如何炼成的?过来人吐血整理的10条避坑指南( 四 )


https://medium.com/huggingface/training-larger-batches-practical-tips-on-1-gpu-multi-gpu-distributed-setups-ec88c3e51255
https://medium.com/@theaccelerators/learn-pytorch-multi-gpu-properly-3eb976c030ee
https://towardsdatascience.com/how-to-scale-training-on-multiple-gpus-dae1041f49d2
建议 5: 如果你拥有两个及以上的 GPU
能节省多少时间很大程度上取决于你的方案 , 我观察到 , 在 4x1080Ti 上训练图像分类 pipeline 时 , 大概可以节约 20% 的时间 。 另外值得一提的是 , 你也可以用 nn.DataParallel 和 nn.DistributedDataParallel 来进行推断 。
关于自定义损失函数
编写自定义损失函数是一项很有趣的练习 , 我建议大家都不时尝试一下 。 提到这种逻辑复杂的损失函数 , 你要牢记一件事:它们都在 CUDA 上运行 , 你应该会写「CUDA-efficient」代码 。 「CUDA-efficient」意味着「没有 Python 控制流」 。 在 CPU 和 GPU 之间来回切换 , 访问 GPU 张量的个别值也可以完成这些工作 , 但是性能表现会很差 。
前段时间 , 我实现了一个自定义余弦嵌入损失函数 , 是从《Segmenting and tracking cell instances with cosine embeddings and recurrent hourglass networks》这篇论文中来的 , 从文本形式上看它非常简单 , 但实现起来却有些复杂 。
我编写的第一个简单实现的时候 , (除了 bug 之外)花了几分钟来计算单个批的损失值 。 为了分析 CUDA 瓶颈 , PyTorch 提供了一个非常方便的内置分析器 , 非常简单好用 , 提供了解决代码瓶颈的所有信息:
def test_loss_profiling(): loss = nn.BCEWithLogitsLoss() with torch.autograd.profiler.profile(use_cuda=True) as prof: input = torch.randn((8, 1, 128, 128)).cuda() input.requires_grad = True
target = torch.randint(1, (8, 1, 128, 128)).cuda().float()
for i in range(10): l = loss(input, target) l.backward() print(prof.key_averages().table(sort_by=''self_cpu_time_total''))
建议 9: 如果设计自定义模块和损失——配置并测试他们
在对最初的实现进行性能分析之后 , 就能够提速 100 倍 。 关于在 PyTorch 中编写高效张量表达式的更多信息 , 将在 Efficient PyTorch — Part 2 进行说明 。
时间 VS 金钱
最后但非常重要的一点 , 有时候投资功能更强大的硬件 , 比优化代码可能更有价值 。 软件优化始终是结果无法确定的高风险之旅 , 升级 CPU、RAM、GPU 或者同时升级以上硬件可能会更有效果 。 金钱和时间都是资源 , 二者的均衡利用是成功的关键 。
通过硬件升级可以更轻松地解决某些瓶颈 。
写在最后
懂得充分利用日常工具是提高熟练度的关键 , 尽量不要制造「捷径」 , 如果遇到不清楚的地方 , 请深入挖掘 , 总有机会发现新知识 。 正所谓「每日一省」:问问自己 , 我的代码还能改进吗?这种精益求精的信念和其他技能一样 , 都是计算机工程师之路的必备品 。
【|高性能PyTorch是如何炼成的?过来人吐血整理的10条避坑指南】原文链接:https://towardsdatascience.com/efficient-pytorch-part-1-fe40ed5db76c


推荐阅读