在训练轻量化模型时,经常发生的情况就是,明明 GPU 很闲,可速度就是上不去,用了多张卡并行也没有太大改善。
为了提高GPU服务器的资源利用率,针对各种不同的训练配置进行了速度测试,并尝试提出一些改善的办法。
1. 实验配置
1.1 服务器
服务器为4卡TITAN RTX,进行实验时停止了其他高资源消耗的进程。
1.2 基本配置
- Dataset:ImageNet
- Model:MobilenetV2
- Augmentation:RandomCrop,RandomFlip,Resize,Normalization
1.3 训练配置说明
1.3.1 DataLoader/Torchvision
使用torchvision原生的dataset.ImageFolder,加载 ImageNet 数据集;
使用torchvision原生的transforms进行数据增强。
1.3.2 DataLoader/Pytorch
继承torch.utils.data.Dataset,自定义数据集,并自定义增强操作。
由于torchvison原生使用PIL进行图片加载,经测试PIL读取图片的速度慢于OpenCV,因此自定义数据集中使用了OpenCV读取图片。
PIL加载图片后得到的是其自己的数据类型,而OpenCV或者skimage加载图片后得到numpy.array,因此transforms的方法也暂时无法通用。
1.3.3 DataLoader/DALI-sc
使用 NVIDIA-DALI,作为第三方 API 构建 dataloader。
sc(Single-Card):单卡模式,即只在'cuda:0'上构建加载数据的Pipeline,当存在多个GPU时,再将数据分发到各个 GPU 上进行推理计算。
1.3.4 DataLoader/DALI-mc
mc(Multi-Card):多卡模式,即在每个GPU上构建加载数据的Pipeline,每个GPU直接获取数据并进行推理计算。
1.4 参数指标说明
Dataloader:使用的数据加载模式;
Disk:数据集存储于何种类型的磁盘(机械硬盘/固态硬盘);
GPU:训练使用的 GPU 数量
Batch size:分到每一个 GPU 上的 Batch size;
Time(s):单项测试的持续时间,单位:秒;
Steps:测试持续时间内,运行的步数;
Samples/s:每秒钟处理的样本总数;
Speed:以 Torchvision/GPU=1/Batchsize=128 为基准,每秒处理样本数的倍数;
GPU Efficiency:以组内的单卡训练速度作为基准,每个 GPU 对于训练速度的贡献率;
其中 DALI-mc 由于没有单卡训练模式,GPU Efficiency 以 sc 模式的单卡训练结果作为基准。
2. 实验结果
实验结果如下图所示:
3. 结果分析
3.1 现象观察
3.1.1 HDD vs SSD
同等条件下,SSD 的结果要全面优于 HDD。
3.1.2 DALI vs torch/torchvision
在使用相同的磁盘介质条件下,DALI 相比 Pytorch 原生的Dataset/DataLoader,效率要高出很多。
可以看到,DALI 单卡模式在 HDD 下的效率,可以达到 torch/torchvision 在 SSD 下的效率水平。
3.1.3 单卡 vs 多卡
多卡的整体效率并不一定比单卡高很多,取决于训练所用的配置。
最差的情况下,使用多卡并不能实现加速效果。如 HDD 下,torch/torchvision使用多卡训练时,整体加速效果只有1.1-1.4倍之间。
3.2 原因分析
3.2.1 训练速度的瓶颈
根据实验结果的分析,轻量化模型训练速度的瓶颈大约会产生在如下几个环节:
- 数据读取时的磁盘 IO 性能;
- 图片解码和在线数据增强;
- CPU-GPU,GPU-GPU 的数据拷贝;
- Loss计算和反向传播。
而基于GPU的前向推理反而不是影响速度最关键的因素。
(1)在硬盘为 HDD 的情况下,torch/torchvision需要花费大量 CPU 资源在数据读取以及图片解码/增强的计算上,大大拖累了整体训练效率,可以看到 GPU 利用率经常处于较低的区域,甚至为0。
(2)当硬盘换成 SSD 后,IO 能力的瓶颈解决了,CPU 主要把算力花在图片解码/增强上,训练速度上了一个台阶。
(3)在硬盘依然为 HDD 的情况下,改用 DALI 生成 Dataloader,DALI 将部分的图片解码以及全部的在线增强操作都转移到 GPU 上进行,减轻了 CPU 的负担,训练速度也上了一个台阶。
(4)同时使用 DALI 和 SSD 后,CPU 的负荷更小了,训练速度又上了一个台阶,达到目前的最优。
3.2.2 如何使用并行才能加速
(1)根据3.2.1的分析,如果训练速度瓶颈在于 CPU 处理能力,比如磁盘 IO 太慢,或者图片解码/增强太慢,那么使用多卡并行并不能有效加速训练,因为GPU并不是整个Pipeline中最慢的那一环。
(2)当多卡并行可以加速时,卡也并不是越多越好,因为 GPU 之间,以及 CPU 和 GPU 之间的数据搬运都是有资源消耗的,需要平衡收益和支出。
3.2.3 Batch-Size 对速度的影响
(1)当训练速度瓶颈在于 CPU 处理能力时,改变 Batch-Size 对速度的影响不大。因为始终处于 GPU 等待 CPU 的状态,而 CPU 每次处理的批次大小,对整体速度没有太大影响。
(2)当 CPU 的瓶颈改善后,适当增大 Batch-Size ,可以提高训练的速度。因为更大的 Batch-Size 对于 GPU 更加速度友好。
3.2.4 DALI 单卡/多卡模式对速度的影响
(1)数据并行方法
在多GPU的情况下,有两种数据并行方法,一个是nn.DataParallel,内部的参数同步算法为 Parameter Server 方法。这种方法有一张主卡,统一做数据分发和回收,进行loss计算和反向传播,GPU的计算速度瓶颈主要在主卡上。
而官方推荐更新的nn.parallel.DistributedDataParallel,内部的参数同步算法为 Ring AllReduce 方法。这种方法,各个 GPU 的地位是相同的,负载更均衡,理论上速度也更快。
(2)本实验中涉及数据并行时,目前使用的都是nn.DataParallel。
当DALI处于多卡模式(即每张卡各自建立数据加载的 Pipeline)时,通过重载nn.DataParallel的forward方法,取消数据搬运的过程,直接在各卡上进行推理。
(3)速度实测
理论上 DALI 的多卡模式会更快,因为多个 GPU 在同时进行图片解码/增强操作,而单卡模式只在一个 GPU 上进行。
而实验结果显示,在 SSD 上,DALI 的单卡和多卡并没有明显差距,单卡还略微快一些;而在 HDD 上,DALI 的单卡要比多卡快不少。
由此推论,多个 GPU 同时解码/增强,对于数据读取的压力比较大,可能反而拖慢了整体效率。
但该结论对于nn.parallel.DistributedDataParallel不一定成立,下一步会进行相关的测试。
3.2.5 显存开销
(1)使用 DALI 时,显存开销明显要更大一些。也很好理解,因为更多的操作在 GPU 上进行。相当于一种空间换时间的方式。对于轻量化模型来说,显存空间一般不是太大的问题。
(2)DALI 单卡模式时,主卡的显存开销会明显比其他卡多出一大截,因为数据都缓存在主卡中;而多卡模式时,每张卡都缓存了一份数据,因此显存开销相比不使用 DALI 时都会大很多。
(3)由于nn.DataParallel使用的 Parameter Server 方法原理,显存负载并不均衡,主卡会占用更多显存,但由于本次实验使用的 MobilenetV2 较小,这种不均衡并不明显,几乎可以忽略。
4. 训练建议
针对轻量化模型的训练加速,总结如下几点建议:
- 尽量使用 SSD 替代 HDD;
- 尽量使用 DALI 替代 torch/torchvision 原生 API;
- 目前来看,DALI 使用单GPU加载数据,再分发进行数据并行,效果也足够好;
- 卡的数量并不与训练速度成正比,优先解决CPU和IO的瓶颈限制。
5. 后记
下图是使用 DALI 同时驱动4张GPU进行训练的资源消耗情况,应该属于比较理想了。
如果不是公司的卡比较壕,如换成2080TI之类的游戏卡,可能就奔着4*99%去了。
接下来预期会进一步研究的点包括:
nn.parallel.DistributedDataParallel在单机多卡情况下的使用- NVIDIA 的另一个加速库 apex 的使用