炼丹日记01-ViTPose-S 从头训练遇到的所有坑,以及GPU训练速度调优的实践教程

前言

这是我炼丹日记的第一篇,这个专栏主要是分享我在浮现模型的时候遇到的问题和解决经验。过程中会穿插一点训练技巧和GPU训练基本原理的讲解,希望能帮助大家更好的理解训练过程,练出更SOTA的丹。

在这篇文章的最后是我总结的GPU训练速度调优的最佳实践,这里我直接先写出来:

后台运行防断线,
散热莫教温度蹿。
炼中关注炉火候,
功耗利用两相看。

工人数量取一半,(逻辑核心的一半)
核心线程莫占满。
批次大小非越满,
带宽瓶颈是大患。

吞吐高低见分晓,
万勿拼写错一环。
谨慎细查免心烦,
功成丹熟笑开颜。

这是我在实践中得到的血与泪的教训,如果你对我是怎么总结出来的感兴趣,可以继续往下读下去。

任务背景

之前我用docker配好了MMPose的基本环境,并且跑通了基本的demo用例,接下来我的工作就到了模型训练复现部分了,我要复现的模型是的是ViTPose-S,我给自己定的目标很明确,也很简单:让ViTPose-S模型,在COCO数据集上,以256x192的分辨率,跑210个epoch

这听起来工作量很小,但是实际上有非常多的坑,下面我就一一列出来,以便大家快速定位和debug。

坑1:环境不全!

依葫芦画瓢,跟着MMPose现有的配置文件把自己的配置文件写出来之后,按下回车训练命令后出现这样的报错:

ValueError: No module named 'mmpretrain'

这个坑的解决方法很简单,把这个库装上就行。

mim install "mmpretrain>=1.0.0rc8"

坑2:ssh连接中断会把训练进程也中断!

我这里连接的是我内网穿透之后的windows主机,显卡是4070。在服务器上训练的同学要注意这一点,如果你是ssh连到你的服务器,用python train.py --config这种格式启动进程,那么一旦你的ssh 会话断开,你的训练进程就死翘翘了

解决方案

解决这个问题的方法如下:

nohup python tools/train.py --configs

这样你的进程就会一直在后台运行,并且把输出重定向到nohup.out,就算ssh断开这个也会接着跑了。

tips:watch -n 3 tail nohup.out 可以在终端输出训练进度信息的最后三行,并且实时刷新

到这我以为坑已经差不多了,马上就可以开始愉快的训练了。然而,真正的噩梦才刚刚开始…

坑3:GPU干烧了!

训练愉快的跑了几十分钟,突然我nohup.out一动不动了。我赶紧跑回家看我的主机咋回事——居然自动关机了!

什么玩意?我好歹也是ROG枪神7Plus啊!跑个模型还能整崩溃?不信邪的我去看了日志,ohho, 原来是GPU过热,触发电脑防反,自动关机了!

当时我的电脑没有做任何散热措施,甚至连瓶盖大法都没有用,所以在训练的时候GPU温度会长期在85-90度,甚至以上。时间一久电脑就感觉有危险自动关机了。

解决方案

我个人的解决方案是直接斥巨资买了个风压式散热器(广告位招租),不得不说效果非常好,打开之后直接把我GPU从训练时候的90度干到60度了,散热问题完美解决,接下来就是要考虑我的显卡会不会感冒了。
在这里插入图片描述

tip: nvidia-smi -l 1这个命令可以每秒给你的GPU把一次脉,看看温度,功耗,利用率什么之类的,可以让你实时掌握GPU的状态如何。

坑3:训练的太慢!

解决散热问题之后,我以为就完事大吉,坐等训练结束就OK了——直到我看到如下画面:
在这里插入图片描述
注意这里面的eta信息,其代表预计训练结束还需要的时间,这个信息一出来我立刻两眼一黑:尼玛,怎么要训练15天?吞吐量只有20个样本每秒!这进度谁都接受不了啊!这个龟速铁定是哪里出了什么问题,于是我首先去刚才打开的nvidia-smi -l 1窗口监控GPU的性能,然后,我发现了几个有意思的点:

tips: 吞吐量计算公式:batch / mean(time), time代表一个batch跑完需要的时间。在上面的图片里面可以找到是5.32s左右,这时候我的batch是128,所以算出来的吞吐量就是20个样本每秒左右。

在这里插入图片描述

  1. GPU功耗低的吓人,满载是140W,而训练的时候只有30W左右
  2. GPU利用率(GPU-Util)像心电图一样跳,一会只有百分之几,一会又能给你蹦到100%

这个时候,结合我之前的cuda编程经验,我大概可以猜到可能是RTX 4070的PCIe带宽被巨大的数据量给喂撑了,也就是CPU-GPU之间的I/O瓶颈问题,我而且我知道这和batch_sizenum_workers的配置有很大关系,于是我开始了如下的实验

tips:
batch_size: 批处理大小。一次丢给GPU多少个样本进行计算。它直接影响显存占用和训练收敛效果。理论上,在显存允许范围内,batch_size越大,GPU并行处理的效率越高。
num_workers: 数据加载器(Dataloader)使用的子进程数。它负责在CPU上预处理数据(比如读取图片、数据增强等)。如果num_workers太小,CPU备菜的速度跟不上GPU吃饭的速度,GPU就会饿肚子(摸鱼),导致利用率低下。

解决方案

Round 1: 矛头指向num_workers

我一看我原本的num_workers: 我日,这怎么才2个?只有两个工人在把数据从CPU搬到GPU,这GPU能一直满载运行才怪!给GPU饿坏了要。

于是我决定:保持batch_size=128,我有16个物理核心,32个逻辑核心,所以先把num_workers设为16看看会咋样。

tips:
查看逻辑核心数命令:nproc

结果: 速度没啥变化。看来问题不在num_workers。

Round 2: 降低批次,解放带宽

既然问题目前还不在workers这里,我猜测是大batch_size和数据预处理共同阻塞了数据通路。

操作: 干脆把batch_size降到32,同时把num_workers也设为32,让CPU火力全开去备菜。

结果: 奇迹发生! 吞吐量飙升到 133.33 样本/秒!GPU功耗终于能上到100W以上,利用率在75%-100%之间稳定浮动。这说明小批量、高并发的数据准备策略,更适合我的硬件配置。

Round3: 开挂——混合精度训练

虽然这133样本/秒的吞吐量已经很快了,但是我还想更快一点,我琢磨来琢磨去,最终在MMPose里面看到了混合精度训练的介绍和手法:

tips: 混合精度训练:辨别需要使用全精度的步骤并在其中使用32位浮点(例如权重),而在其他地方使用16位浮点(梯度,中间结果)。

不过最重要的是它开启之后的效果:更少的CUDA MEMORY, 更快的训练速度, 而且GPU只要有tensor core 就能用!

我靠!这不就是免费的午餐吗?直接用上!

操作:训练命令里加上–amp参数。

结果: 再次见证奇迹! GPU功耗和利用率反而降低了,但训练速度不降反升!

继续操作:在amp继续开启的情况下,我斗胆把batch_size提高到64。

结果: 吞吐量达到 193.94 样本/秒!

tips: 自动混合精度训练(AMP)一种在训练时同时使用32位浮点(FP32)和16位浮点(FP16)的技术。FP16占用的显存更少,计算速度更快(尤其是在有Tensor Core的NVIDIA显卡上)。通过–amp,框架会自动将一部分计算切换到FP16,从而在不显著影响精度的情况下,实现显存减半,速度翻倍(理想情况)的神奇效果。这就是为什么功耗降低,速度反而提升的原因。

坑4:最终的考验——val阶段过不了!

正当我以为一切都将一帆风顺直到210个epoch结束时,一个幽灵般的问题出现了:训练(train)过程很顺利,但每当一个epoch结束,进入验证(validation)阶段时,我的服务器内存(是RAM,不是VRAM)就直接爆掉,然后所有进程都被系统无情地杀死。
在这里插入图片描述
我猜测是因为train阶段的32个worker进程没有被立即释放而val阶段又创建了自己的一套worker进程(我在dataloader配置里面写的都是32)。两拨worker加起来,直接把系统内存撑爆了。

解决方案

为了解决这个问题,我将train_dataloaderval_dataloadernum_workers都统一设置为16。这样即使两者同时存在,总worker数也只是32,等于我的逻辑核心数,内存压力会小很多。batch_size则继续保持为64。

结果: 吞吐量稳定在 204.70 样本/秒,验证阶段顺利通过!

最后的坑5:拼写错误

为了追求极致,我又试着把batch_size降回32(num_workers保持16), 我这么做的理由是考虑到我的GPU带宽不是很足,下批量的数据更容易让GPU等待时间更少一些,让GPU满载时间更多。

结果: 吞吐量达到了 216.79 样本/秒!
在这里插入图片描述
最快的一集,功耗稳定在85W左右,利用率周期性波动的现象也大大缓解。这很可能就是我的硬件(RTX 4070 + CPU)、架构和任务所能达到的“甜点”了。(实际上我也懒得再优化了)

在我以为终于可以迎来世界大和平的时候,最后一个KeyError跳了出来:KeyError: ‘cocosubset/AP’。原来是我在配置文件里写保存最佳模型的评价指标时,写错了名字…应该是’coco/AP’。

改完这个,训练终于、终于、终于在轨道上稳定地跑了起来。

炼丹经验总结(榨干GPU最佳实践)

回顾这段充满挣扎的经历,从最初简单的目标,到最后复杂的性能调优,我几乎把一个新手能犯的错误都犯了一遍。最终,我总结出如下的GPU训练时注意事项的打油诗:

后台运行防断线,
散热莫教温度蹿。
炼中关注炉火候,
功耗利用两相看。

工人数量取一半,(逻辑核心的一半)
核心线程莫占满。
批次大小非越满,
带宽瓶颈是大患。

吞吐高低见分晓,
万勿拼写错一环。
谨慎细查免心烦,
功成丹熟笑开颜。

这很明显不是我写的,是谁写的我就不多说了。希望这个文章能够帮到大家,让大家早日更快练出更加SOTA的模型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值