1.不同版本torch结果不一样的原因是各个版本的随机种子不一样
2.做过并行计算或者操作系统的同学可能知道,GPU的计算方式一般是异步的。异步运算不像同步运算那样是按照顺序一步一步来,异步是同时进行的,异步计算中,两种不一样的操作可能会发生同时触发的情况,这是处理两者间的前后关系、依赖关系或者冲突关系就比较重要了。
有一个众所周知的小技巧,在执行训练程序的时候将环境变量CUDA_LAUNCH_BLOCKING=1设为1(强制同步)可以准确定位观察到我们显存操作的错误代码行数。
“CUDA_LAUNCH_BLOCKING=1” 是一个用于控制 CUDA 并行计算的环境变量设置。
在 CUDA 中,GPU 的计算任务通常是异步执行的,即主线程会在将计算任务提交给 GPU 后立即继续执行后续的代码,而不会等待 GPU 完成计算。这种异步执行方式可以提高程序的性能,但有时也会导致调试和错误排查变得困难。
通过将环境变量 “CUDA_LAUNCH_BLOCKING” 设置为 “1”,可以将 GPU 计算任务改为同步执行,也就是在主线程中等待 GPU 完成计算后再继续执行后续的代码。这样可以方便地进行调试和错误排查,因为代码的执行顺序更加可控,但也可能带来一定的性能损失。
在调试或排查问题时,将 “CUDA_LAUNCH_BLOCKING” 设置为 “1” 可以帮助开发人员捕获和跟踪 GPU 计算任务的执行过程,以便更好地理解程序的行为和调试潜在问题。然而,对于正常的生产环境和性能优化目的,一般不建议将其设置为 “1”,而是保持默认的异步执行方式。
3. 在 DataLoader 中使用多个 worker 和页锁定内存
当使用 torch.utils.data.DataLoader 时,设置 num_workers > 0,而不是默认值 0,同时设置 pin_memory=True,而不是默认值 False。
参考文档:https://pytorch.org/docs/stable/data.html
来自 NVIDIA 的高级 CUDA 深度学习算法软件工程师 Szymon Micacz 就曾使用四个 worker 和页锁定内存(pinned memory)在单个 epoch 中实现了 2 倍的加速。人们选择 worker 数量的经验法则是将其设置为可用 GPU 数量的四倍,大于或小于这个数都会降低训练速度。请注意,增加 num_workers 将增加 CPU 内存消耗。
4. 把 batch 调到最大
把 batch 调到最大是一个颇有争议的观点。一般来说,如果在 GPU 内存允许的范围内将 batch 调到最大,你的训练速度会更快。但是,你也必须调整其他超参数,比如学习率。一个比较好用的经验是,batch 大小加倍时,学习率也要加倍。
5. 使用自动混合精度(AMP)
PyTorch 1.6 版本包括对 PyTorch 的自动混合精度训练的本地实现。这里想说的是,与单精度 (FP32) 相比,某些运算在半精度 (FP16) 下运行更快,而不会损失准确率。AMP 会自动决定应该以哪种精度执行哪种运算。这样既可以加快训练速度,又可以减少内存占用。
6. 考虑使用另一种优化器
AdamW 是由 fast.ai 推广的一种具有权重衰减(而不是 L2 正则化)的 Adam,在 PyTorch 中以 torch.optim.AdamW 实现。AdamW
似乎在误差和训练时间上都一直优于 Adam。Adam 和 AdamW 都能与上面提到的 1Cycle 策略很好地搭配。
7. cudNN 基准
如果你的模型架构保持不变、输入大小保持不变,设置 torch.backends.cudnn.benchmark = True。
8. 小心 CPU 和 GPU 之间频繁的数据传输
当频繁地使用 tensor.cpu() 将张量从 GPU 转到 CPU(或使用 tensor.cuda() 将张量从 CPU 转到 GPU)时,代价是非常昂贵的。item() 和 .numpy() 也是一样可以使用. detach() 代替。
如果你创建了一个新的张量,可以使用关键字参数 device=torch.device( cuda:0 ) 将其分配给 GPU。
如果你需要传输数据,可以使用. to(non_blocking=True),只要在传输之后没有同步点。
9. 使用分布式数据并行进行多 GPU 训练
加速分布式训练可能有很多方法,但是简单的方法是使用 torch.nn.DistributedDataParallel 而不是 torch.nn.DataParallel。这样一来,每个 GPU 将由一个专用的 CPU 核心驱动,避免了 DataParallel 的 GIL 问题。
10. 设置梯度为 None 而不是 0
梯度设置为. zero_grad(set_to_none=True) 而不是 .zero_grad()。这样做可以让内存分配器处理梯度,而不是将它们设置为 0。正如文档中所说,将梯度设置为 None 会产生适度的加速,但不要期待奇迹出现。注意,这样做也有缺点,详细信息请查看文档。
11 使用. as_tensor() 而不是. tensor()
torch.tensor() 总是会复制数据。如果你要转换一个 numpy 数组,使用 torch.as_tensor() 或 torch.from_numpy() 来避免复制数据。
12. 在 BatchNorm 之前关闭 bias
在开始 BatchNormalization 层之前关闭 bias 层。对于一个 2-D 卷积层,可以将 bias 关键字设置为 False:torch.nn.Conv2d(…, bias=False, …)。
13. 如果单步调试的过程中无法跳入环境本身的库函数(即使是在库函数里面设置的断点也无法跳入)
只需要修改: “justMyCode”: true, 变成"justMyCode": false,
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
“version”: “0.2.0”,
“configurations”: [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"justMyCode": false,
"cwd":"/idas/users/songmingchen/PMSF"
}
]
}
14.若数据集太大导致读写数据很慢:
看磁盘读写占用率:
iostat -x 1 10
发现磁盘读写影响着模型训练的速度,磁盘读写要保持在一个正常值范围内(在80以下,不是红色的输出),如果磁盘读写利用率过高,会导致多个程序读写磁盘数据的时候产生拥堵,反而会降低模型训练的速度,所以要在磁盘读写速度的大小和batch之间做一个权衡,找到适合磁盘读写利用率的batch size大小。