yolov5训练加速–一个可能忽视的细节(mmdetection也一样),为什么显卡使用率老是为0?

本文仅讨论节省图片加载时间问题,这里面可能有一些容易忽视的细节。yolov5的训练参数里面有一个–cache,默认是ram,就是把解码后的图片保存在内存中。也可以是disk,就会把解码后的图片保存在硬盘上。

parser.add_argument('--cache', type=str, nargs='?', const='ram', help='--cache images in "ram" (default) or "disk"')

 解码后的图片就是numpy数组啦,保存为.npy文件

 这里可能有一个问题,保存在硬盘上有啥用?能加速吗?

接下来就稍微展开讨论一下。

一。现象

在训练的时候,有可能会发现显卡使用率不高,甚至有的时候显卡使率用老是为0(用nvidia-smi查看),然后刷地一下升高了,然后又较长时间为0,这时候可能会觉得奇怪,它到底在干啥呢?这个时候如果查看cpu使用率,往往发现cpu占用率一直很高,有几个逻辑cpu始终满负责(用top命令查看)

二。分析

训练的时候无非是几个步骤:

1.加载图片

2.图片预处理,数据增强,比如缩放,旋转,错切等等,还有yolov5常用的mosaic、mixup

3.正向反向,更新梯度等等要用显卡的地方

而1、2两步里面的加载图片其实是一个容易忽略的耗时步骤,加载图片的耗时即磁盘IO耗时和解码图片耗时,这两个耗时都跟图片大小有关系,图片越大,耗时就越长。而磁盘IO又跟你的磁盘性能有关系,磁盘性能相差可能天差地别,比如pcie4.0的SSD和机械硬盘就可能是10倍以上的速度差别,而解码图片这个只跟CPU有关系了,差别不会太大。

所以这里先回前面的问题保存在硬盘上有啥用?能加速吗?—–能,就算解码后图片文件比原图片文件更大,都能加速,只要你用的是高性能的SSD就行,因为它把CPU解码时间完全给省掉了。

另外,其实yolov5每训练一张图片,它不是只读了一张图,它起码会读4张图,因为mosaic数据增强是默认开启的,概率为1,它会把4张图拼成一张(随机中心点),这个暂不详述。然后如果开启了mixup,mixup又会再读4张图,跟之前的4张图做混合,那就是一次读8张图了。(当然如果mixup开的概率不大的话,那就不是每次都读8级图)。总之就是一次最多能读到8张图,那么读图时间成本就翻了8倍了。虽然你可能会说我CPU多核的,我SSD很快,但是你本来开一个较大的batch-size的时候,就已经充分用到了多核性能,现在再乘个8,就不够用啦。

这里要提一下mmdetection里的yolox,它默认是会用到mosaic加mixup的,并且没有概率设置,每次都读8张图,所以给人的感觉就是,这玩意儿训练怎么这么慢?我的显卡为什么一直是0,它在干啥?

注:原生的yolox是可以用缓存的,但mmdetection好像不行,如果你知道怎么用,请告诉我

三。先上结论

1.如果训练图片总量不大,或者服务器内存超高,即完全可以把解码后的图片放内存里,那就充分利用,直接加上–cache参数完事

2.如果读图片的时间不长,它的耗时只占总时间的很少一部分,没什么优化意义那就不用管它。比如:

(1)训练图片都很小,比如大多在200K以下

(2)模型比较大,导致显卡耗时占了大部分,比如用yolov5x的规模训练

(3)只开mosaic,没开mixup,甚至mosaic的概率还调小了

3.如果想节省读图时间,但是内存又装不下。但是你有一个高性能的SSD,那你就可以考虑把解码后的图片缓存在硬盘上。即用–cache disk参数

4.如果想节省读图时间,但是内存装不下,又没有SSD,那怎么办呢?凉拌!买一个SSD不就行了,买不起显卡,还买不起SSD吗,管够!不过还有一点

如果你的训练图片都很大,比如分辨率高,或者压缩率低,你都可以根据训练用到的分辨率直接把原图resize一下,保存为jpg格式,比如你训练的分辨率用的是640(yolov5默认就是640),那你就把图片resize到640(保持宽高比例)就行了,这样你的图片就变小了,那读图时间自然就少了。并且有可能变小之后你的内存装的下了,那速度就起飞了!

注:

(1)这里别担心我resize了之后,会不会影响训练质量,会不会导致原图的什么特征消失了,别想多了,你不resize,yolov5在训练的时候也会给你resize的,早做晚做都一样。但是你别训练分辨率用的1280,你给resize成640,那就真影响质量了。

(2)如果你用的不是yolo格式的标注文件(比如你用的就不是yolov5),用的是coco格式的、或者VOC格式的,简言之就是你的标注文件里面的物体框是绝对坐标的话,那你得把绝对坐标也给改一下。

四。实验

我其实是用mmdetection的yolox在训练cityscape数据集的时候,发现了这么个问题,cityscape的图片都是2M左右,分辨率是2048乘1024的,然后你再用一个小规模的模型来训练,比如yolox_s,那就会发现训练时间明显长于yolov5s(那是因为yolox开了mixup),而且貌似还没有把图片加内存里的功能。

但是我家里的电脑上没有准备好cityscape的标注文件,我就不用它演示了。我正好有一个之前做一个实验用的超小数据集,训练集一共只有45张图,验证集只有5张图,但是里面的图片都蛮大的,都是2M多,正好用它来试试。之前的实验在如下文章中:

https://blog.csdn.net/ogebgvictor/article/details/128179019

 我的显卡是笔记本上的3080,为了增强对比效果,先把mixup打开,概率设为1(复制了一份超参数文件data/hyps/hyp.scratch-low-my.yaml)

注意,我笔记本上只有一个PCIE4.0的SSD,所以我的耗时主要不在磁盘IO上,而是在图片解码上

 1.啥也不缓存

python train.py –data earplug_data/dataset.yaml –cfg models/yolov5s.yaml –weights weights/yolov5s.pt –batch-size 8 –epochs 200  –name exp_earplug –hyp data/hyps/hyp.scratch-low-my.yaml 

耗时如下,45张图按理来说岂不是飞地一下就没了,这边却耗时在7、8秒左右一轮,而且明显会感觉进度条卡顿(时快,时卡那种,那个0秒的可能不太准)

 2.直接装内存里

python train.py –data earplug_data/dataset.yaml –cfg models/yolov5s.yaml –weights weights/yolov5s.pt –batch-size 8 –epochs 200  –name exp_earplug –hyp data/hyps/hyp.scratch-low-my.yaml  –cache 

速度起飞没的说,非常流畅

 3.缓存到硬盘里

python train.py –data earplug_data/dataset.yaml –cfg models/yolov5s.yaml –weights weights/yolov5s.pt –batch-size 8 –epochs 200  –name exp_earplug –hyp data/hyps/hyp.scratch-low-my.yaml  –cache disk 

速度也是很快,但是稍有卡顿,没有内存那么流畅,而且这边的0秒是个整数,我感觉它其实并不是0秒,可能0.5也算0秒了,跟2那个0秒是有差距的。

这里也证 明了之前关于保存在硬盘上有啥用?能加速吗?的回答,是正确的

 我PCIE4.0的SSD,读几个图片咋就会稍有卡顿?仔细看一下训练图片发现,图片2M多,解码后36M!

 其实解码图片的大小是固定的,就是宽*高*3,单位字节,看一下我这个图片的分辨率

 

 是36M吧,上面已经说到了解决办法,直接resize成640分辨率

4.resize后再用缓存

就像下面这样把图片都resize一下

import cv2 as cv
from pathlib import Path


def batch_resize(img_dir, dest_img_dir, size=640):
    img_dir = Path(img_dir)
    dest_img_dir = Path(dest_img_dir)
    dest_img_dir.mkdir(parents=True, exist_ok=True)

    for img_path in img_dir.glob('*.jpg'):
        img = cv.imread(str(img_path))
        h, w = img.shape[:2]
        ratio = size / max(h, w)
        img = cv.resize(img, None, fx=ratio, fy=ratio, interpolation=cv.INTER_LINEAR)
        cv.imwrite(str(dest_img_dir/img_path.name), img)
batch_resize(r'D:\workPython\yolov5-6.2\data\earplug_data\images\train',
             r'D:\workPython\yolov5-6.2\data\earplug_data\images_mini\train')
batch_resize(r'D:\workPython\yolov5-6.2\data\earplug_data\images\val',
             r'D:\workPython\yolov5-6.2\data\earplug_data\images_mini\val')

不过要注意的是,训练的时候你不能把图片目录指定为images_mini,你还是用得images,因为在yolov5代码里写死了,它由图片目录去找labels目录的时候只会把图片目录当成images来用。所以你自己改一下目录名,我就不帮你改了

改完了之后先啥都不缓存,直接试一下,命令同1.

python train.py –data earplug_data/dataset.yaml –cfg models/yolov5s.yaml –weights weights/yolov5s.pt –batch-size 8 –epochs 200  –name exp_earplug –hyp data/hyps/hyp.scratch-low-my.yaml 

发现速度居然跟 3.缓存到硬盘里比较相近!此处就不贴图了,耗时显示的都是0。其实相对于3,我这是节省了磁盘IO时间,因为我的图片resize之后都在100K以下,增加了一点点解码时间。

如果resize之后再用缓存,那速度更是快的飞起,跟2.直接装内存里差不多了。缓存文件只有900K。

 五。mmdetetion怎么办?

mmdetection好像不支持缓存(如果能的话请告诉我怎么用,我暂时没发现),所以办法即:

1.你给它加个缓存功能,可以参考yolov5,原生yolox的代码来看看怎么改,我暂时还没仔细研究

2.缓存到内存稍微麻烦一点,因为它涉及多进程的问题,但缓存到硬盘上那不是很简单吗,如果你不想涉及到多进程的问题,你直接找到它读图片的代码,好像在一个loading.py里面,把里面的逻辑改一下即可,改为如果存在npy,则加载它。然后呢你事先给先把图片缓存到硬盘上,然后再启动训练流程,就完全不会有多进程的问题啦。

3.如果你真的没有SSD,比如我用的服务器上就没有,那你可以先考虑把图片resize到训练分辨率,还是会节省一些时间的。

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(1)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2023年5月23日
下一篇 2023年5月23日

相关推荐