训练模型时 遇到速度过慢时的深思 & 速度提升 (From GPU & CPU)

本文探讨了在训练模型时如何解决速度过慢问题,包括GPU使用情况监控、单机多卡并行训练中的DataParallel和DistributedDataParallel选择,以及使用半精度训练、优化batch_size和CPUdataloader参数以减少IO操作的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GPU

查看GPU使用情况 配置

nvidia_smi

单机多卡并行训练

torch.nn.DataParallel

在使用多GPU的时候, 使用os.environ[‘CUDA_VISIBLE_DEVICES’]来限制使用的GPU个数
例如我要使用第0和第3编号的GPU, 那么只需要在程序中设置:

os.environ['CUDA_VISIBLE_DEVICES'] = '0,3'

Note:
这个参数的设定要保证在模型加载到gpu上之前, 一般在程序开始的时候就设定好这个参数

之后, 将模型加载到多GPU上面

如果是模型:

model = nn.DataParallel(model)
model = model.cuda()

如果是数据:

inputs = inputs.cuda()
labels = labels.cuda()

pytorch官网给的示例代码

model = Model(input_size, output_size)
if torch.cuda.device_count() > 1:
    print("Let's use", torch.cuda.device_count(), "GPUs!")
    # dim = 0 [30, xxx] -> [10, ...], [10, ...], [10, ...] on 3 GPUs
    model = nn.DataParallel(model)

model.to(device)

DataParallel的内部代码:

class DataParallel(Module):
    def __init__(self, module, device_ids=None, output_device=None, dim=0):
        super(DataParallel, self).__init__()

        if not torch.cuda.is_available():
            self.module = module
            self.device_ids = []
            return

        if device_ids is None:
            device_ids = list(range(torch.cuda.device_count()))
        if output_device is None:
            output_device = device_ids[0]

截取其中一部分代码,如果不设定好要使用的device_ids的话, 程序会自动找到这个机器上面可以用的所有的显卡, 然后用于训练.
但是因为前面使用os.environ['CUDA_VISIBLE_DEVICES']限定了这个程序可以使用的显卡, 所以这个地方程序如果自己获取的话, 获取到的其实就是我们上面设定的那几个显卡.

深入考究
使用os.environ['CUDA_VISIBLE_DEVICES']对可以使用的显卡进行限定之后, 显卡的实际编号和程序看到的编号应该是不一样的

例如
上面我们设定的是os.environ['CUDA_VISIBLE_DEVICES']="0,2",但是程序看到的显卡编号应该被改成了'0,1', 也就是说程序所使用的显卡编号实际上是经过了一次映射之后才会映射到真正的显卡编号上面的,
例如
这里的程序看到的1对应实际的2

平衡DataParallel带来的显存使用不平衡的问题

这个问题其实讨论的也比较多了, 官方给的解决方案就是使用 DistributedDataParallel来代替 DataParallel
(实际上DistributedDataParallel显存分配的也不是很平衡), 但是从某些角度来说, DataParallel使用起来确实比较方便, 而且最近使用 DistributedDataParallel 遇到一些小问题. 所以这里提供一个解决显存使用不平衡问题的方案:

  1. 首先这次的解决方案来自transformer-XL的官方代码: https://github.com/kimiyoung/transformer-xl

  2. 然后我将其中的平衡GPU显存的代码提取了出来(原代码好像有点小问题)放到了github上面:https://github.com/Link-Li/Balanced-DataParallel

这里的代码是原作者继承了 DataParallel 类之后进行了改写:

class BalancedDataParallel(DataParallel):
    def __init__(self, gpu0_bsz, *args, **kwargs):
        self.gpu0_bsz = gpu0_bsz
        super().__init__(*args, **kwargs)
 ...

这个 BalancedDataParallel 类使用起来和 DataParallel 类似, 下面是一个示例代码:

my_net = MyNet()
my_net = BalancedDataParallel(gpu0_bsz // acc_grad, my_net, dim=0).cuda()

第一个参数是第一个GPU要分配多大的batch_size

如果使用了梯度累积, 那么这里传入的是每次进行运算的实际batch_size大小.

比如:
你在3个GPU上面跑代码, 但是一个GPU最大只能跑3条数据, 但是因为0号GPU还要做一些数据的整合操作, 于是0号GPU只能跑2条数据, 这样一算, 你可以跑的大小是2+3+3=8, 于是你可以设置下面的这样的参数:

batch_szie = 8
gpu0_bsz = 2
acc_grad = 1
my_net = MyNet()
my_net = BalancedDataParallel(gpu0_bsz // acc_grad, my_net, dim=0).cuda()

这个时候突然想跑个batch size是16的怎么办呢, 那就是4+6+6=16了, 这样设置累积梯度为2就行了:

batch_szie = 16
gpu0_bsz = 4
acc_grad = 2
my_net = MyNet()
my_net = BalancedDataParallel(gpu0_bsz // acc_grad, my_net, dim=0).cuda()

torch.nn.parallel.DistributedDataParallel

pytorch的官网建议使用DistributedDataParallel来代替DataParallel

因为DistributedDataParallel比DataParallel运行的更快

然后 显存分屏的更加均衡
且DistributedDataParallel 功能更加强悍
例如
分布式的模型(一个模型太大, 以至于无法放到一个GPU上运行, 需要分开到多个GPU上面执行).
只有DistributedDataParallel支持分布式的模型像单机模型那样可以进行多机多卡的运算.

先设定好os.environ[‘CUDA_VISIBLE_DEVICES’]
然后再进行下面的步骤.

因为DistributedDataParallel是支持多机多卡的, 所以这个需要先初始化一下:

torch.distributed.init_process_group(backend='nccl', init_method='tcp://localhost:23456', rank=0, world_size=1)

第一个参数pytorch支持的通讯后端, 这里单机多卡, 这个就是走走过场.
第二个参数各个机器之间通讯的方式,这里是单机多卡, 设置成localhost, 后面的端口找一个空着没用的就OK.
**rank**标识主机和从机, 这里就一个主机, 设置成0就OK.
**world_size**是标识使用几个主机, 这里就一个主机, 设置成1就OK, 设置多了代码不允许.

其实如果是使用单机多卡的情况下, 根据pytorch的官方代码distributeddataparallel, 是直接可以使用下面的代码的:

torch.distributed.init_process_group(backend="nccl")
model = DistributedDataParallel(model) # device_ids will include all GPU devices by default

Note:
如果使用这句代码, 直接在pycharm或者别的编辑器中,是没法正常运行的
因为这个需要在shell的命令行中运行, 如果想要正确执行这段代码, 假设这段代码的名字是main.py, 可以使用如下的方法进行(参考1 参考2):

python -m torch.distributed.launch main.py

如果使用了argparse, 一定要在参数里面加上–local_rank, 否则运行还是会出错的

之后就和使用DataParallel很类似了.

model = model.cuda()
model = nn.parallel.DistributedDataParallel(model)

Note: 这里要先将model加载到GPU 然后才能使用DistributedDataParallel进行分发
之后的使用和DataParallel就基本一样了

多机多gpu训练

在单机多gpu可以满足的情况下, 绝对不建议使用多机多gpu进行训练
经过测试, 发现多台机器之间传输数据的时间非常慢, 主要是因为测试的机器可能只是千兆网卡, 再加上别的一些损耗, 网络的传输速度跟不上, 导致训练速度实际很慢.

Reference

pytorch/examples/imagenet/main.py

Distributed-VGG-F

使用半精度训练

更好的显卡,更轻的模型

batch_size

增大 batch size 提高epoch速度
但是收敛速度也会变慢
需要再适当升高学习率

CPU

查看占用率
top命令

top -bn 1 -i -c

在这里插入图片描述

data loader

线程数 arg 增加
多线程加载数据
dataloader 的param.

num_works=4

如果数据集很小,使用多线程加载数据可能会更慢,因为多线程有一定的开销。
在这种情况下,最好使用单线程读取数据。

减少日志IO操作频率

文件的IO操作,这会导致GPU得不到连续性使用,整体速度特别慢。

使用pin_memory和num_workers

总结

有的时候模型训练慢并不是因为显卡不行或者模型太大,而是在跑模型过程中有一些其他的操作导致速度很慢
如:IO操作

  1. GPU 升级
  2. 硬盘读取,重点观察数据是否量大且容量较小
  3. data loader 线程数arg 增加
  4. 观察CPU占用率
  5. 内存占用率
  6. IO速度等

Reference

https://zhuanlan.zhihu.com/p/86441879

当你想通过TensorRT对YOLOv5这样的深度学习模型进行性能优化,特别是为了提高推理速度,确实需要一些技术准备和步骤。作为初学者,你可以按照以下步骤进行: 1. **安装依赖**: - 首先,你需要安装必要的库,包括TensorRT、PyTorch及其版本兼容的trtorch模块。你可以在TensorRT官方文档中找到兼容的版本指南。 2. **导出模型**: 使用YOLOv5的`torch2trt`工具将PyTorch模型转换为TensorRT可以理解的格式。这通常涉及到保存原始模型,然后运行`torch2trt`命令行工具或编写脚本来进行转换。 ```bash python -m torch2trt.YOLOv5 --weights yolov5s.pt --save-engine yolov5s.trt ``` 3. **配置 TensorRT**: 调整TensorRT的一些参数,如最大_workspace_size,允许的内存使用量等,以适应你的硬件资源。你可能需要调整这些设置以获得最佳性能。 4. **验证与测试**: 将转换后的引擎加载到新的环境中,并用测试数据集验证精度是否有所下降。同,对比原模型和TRT加速后的模型在推理间上的差异。 5. **性能分析**: 如果发现性能提升不够理想,可能需要检查模型是否进行了有效的量化,或者查看是否有优化的空间(比如调整模型金字塔结构)。 6. **持续学习**: 网络上有很多关于如何优化TensorRT性能的教程和案例,例如GitHub上就有许多社区贡献者分享的经验。阅读他们的实践可以帮助你解决遇到的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cmy_CTO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值