YOLOv5项目介绍

YOLOv5 项目教程


作者:elfin  资料来源:YOLOv5


1、前言

YOLOv5项目地址ultralytics/yolov5

​ 项目自发布以来,直到现在仍然在不断改进模型、项目。作者的更新频率很大,很多问题都能够及时解决,当然问题也很多!到写稿此时,项目的device参数仍然无法正常工作,查看源码,作者的代码写的GPU设备控制比较复杂,修改源码也没有解决,可能我里解决就差一步了吧!在项目提交bug后,得到作者的及时回应,但是最后仍然没有解决。难道使用GPU必须从GPU:0开始吗?查看bug请点击

  • ONNXOpen Neural Network Exchange

    开发式神经网络交换格式!

    它是深度学习模型的一种标准,可使模型在不同框架间转移

  • TensorRT

    英伟达官方提供的GPU版深度学习模型加速平台,可以和Tensor Serving一起部署模型,实现模型的快速运算,其特点是性能高、资源占用少。

1.1 模型训练

$ python train.py  --batch-size 64 --data coco.yaml --weights yolov5l.pt --device 0,1

使用 --device 0,1指定多个GPU是没有用的!会衰减计算速度。推荐多GPU使用数据并行的方法。

$ python -m torch.distributed.launch --nproc_per_node 2 train.py --batch-size 64 --data coco.yaml --weights yolov5x.pt
  • --nproc_per_node: 指定你的GPU节点数;

  • --batch-size:这里的批量是总的批量,实际每个GPU的的batch为 64/32.

    这里的处理方式和keras是一样的,采用数据并行可以提高我们的计算速度。

$ python -m torch.distributed.launch --nproc_per_node 2 train.py --batch-size 64 --data coco.yaml --cfg yolov5x.yaml --weights '' --device 2,3

数据并行运算的时候可以指定要使用的GPU: --device 2,3

$ python -m torch.distributed.launch --nproc_per_node 2 train.py --batch-size 64 --data coco.yaml --cfg yolov5x.yaml --weights '' --sync-bn

SyncBatchNorm多个GPU之间进行批量标准化!

​ 下面介绍将模型转为ONNX模型,使用TensorRT部署……


2、yolov5模型转ONNX模型

2.1 环境准备

$ git clone https://github.com/ultralytics/yolov5  # clone repo
$ cd yolov5
$ pip install -r requirements.txt  # base requirements
$ pip install onnx>=1.7.0  # for ONNX export
$ pip install coremltools==4.0  # for CoreML export

2.2 输出已训练的模型为ONNX模型

$ python models/export.py --weights yolov5x.pt --img 640 --batch 1  # export at 640x640 with batch size 1

export.py输出了三个模型,这里我们主要查看onnx模型!

使用https://netron.app/可以查看我们生成的ONNX模型结构。

文件中代码:

print('\nStarting ONNX export with onnx %s...' % onnx.__version__)
f = opt.weights.replace('.pt', '.onnx')  # filename
torch.onnx.export(model, img, f, verbose=False, opset_version=12, input_names=['images'],
                  output_names=['classes', 'boxes'] if y is None else ['output'])
# Checks
onnx_model = onnx.load(f)  # load onnx model
onnx.checker.check_model(onnx_model)  # check onnx model
# print(onnx.helper.printable_graph(onnx_model.graph))  # print a human readable model
print('ONNX export success, saved as %s' % f)
except Exception as e:
print('ONNX export failure: %s' % e)

torch.onnx.export()模型转换关键函数

关于torch.onnx的介绍见:https://pytorch.org/docs/stable/onnx.html


3、Test Time Augmentation (TTA)

添加命令行参数--augment实现测试、预测时的性能增强!

3.1 Test Normally

$ python test.py --weights yolov5x.pt --data coco.yaml --img 640

3.2 Test with TTA

$ python test.py --weights yolov5x.pt --data coco.yaml --img 832 --augment

3.3 Inference with TTA

$ python detect.py --weights yolov5s.pt --img 832 --source ./inference/images/ --augment

4、Model Ensembling模型嵌入

还记得决策树吗?而现在我们一般使用随机森林、LightGBM等模型。Ensembling的思想在深度学习模型中同样适应,这里我们对yolov5的不同模型进行融合,结果发现,预测显著性明显提升。

4.1 Test Normally

$ python test.py --weights yolov5x.pt --data coco.yaml --img 640

4.2 Ensemble Test

$ python test.py --weights yolov5x.pt yolov5l.pt --data coco.yaml --img 640

4.3 Ensemble Inference

$ python detect.py --weights yolov5x.pt yolov5l.pt --img 640 --source ./inference/images/

这里测试结果为:齐达内是人的概率从0.8上涨到0.9!


5、Pruning/Sparsity Tutorial 剪枝/稀疏教程

5.1 Test Normally

$ python test.py --weights yolov5x.pt --data coco.yaml --img 640
Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.492 < -------- baseline mAP
Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=100 ] = 0.676
Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=100 ] = 0.534
Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.318
Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.541
Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.633
Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=  1 ] = 0.376
Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 10 ] = 0.616
Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.670 < -------- baseline mAR
Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.493
Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.723
Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.812

5.2 Test with 30% Pruned Model

我们现在用一个修剪过的模型重复上面的测试,设置修剪30%,这里我们需要修改test.py文件的torch_utils.prune(model, 0.3)

$ python test.py --weights yolov5x.pt --data coco.yaml --img 640
Average Precision  (AP) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.462 < -------- lower mAP
Average Precision  (AP) @[ IoU=0.50      | area=   all | maxDets=100 ] = 0.649
Average Precision  (AP) @[ IoU=0.75      | area=   all | maxDets=100 ] = 0.505
Average Precision  (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.293
Average Precision  (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.507
Average Precision  (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.602
Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=  1 ] = 0.361
Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets= 10 ] = 0.590
Average Recall     (AR) @[ IoU=0.50:0.95 | area=   all | maxDets=100 ] = 0.643 < -------- lower mAR
Average Recall     (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.465
Average Recall     (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.691
Average Recall     (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.786

在结果中我们可以观察到,我们的模型在剪枝后达到了30%的稀疏性,这意味着30%的模型权重参数nn.Conv2d层等于0。测试时间(在P100 GPU上)保持不变,而模型的AP和AR分数略有降低。


6、 Hyperparameter Evolution超参数调优

超参数对模型的影响是很大的,但是具体是如何影响的,通过严格的数学推导我们是做不到的。科学家在如何获取最优参数的道路上做了很多尝试,遗传算法(GA)就是比较出名的存在。

6.1 初始化超参数

YOLOv5有大约25个用于各种训练设置的超参数。这些是在/data目录的yaml文件中定义的。更好的初始猜测将产生更好的最终结果,因此在演化之前正确初始化这些值是很重要的。如果有疑问,只需使用默认值,这是优化YOLOv5在COCO数据集的配置参数。

6.2 定义适应性函数

适应性是我们追求最大化的价值。在YOLOv5中,我们将默认适应度函数定义为度量的加权组合:mp@0.5贡献了10%、mp@0.5:0.95占其余90%。您可以根据需要调整这些值,也可以使用默认的适应度定义。

def fitness(x): 
    # Returns fitness (for use with results.txt or evolve.txt) 
    w = [0.0, 0.0, 0.1, 0.9]  # weights for [P, R, mAP@0.5, mAP@0.5:0.95] 
    return (x[:, :4] * w).sum(1) 

6.3 遗传算法演变

evolution是在一个我们寻求改进的基本场景下进行的。本例中的基本场景是使用预训练的yolov5对COCO128进行10个时期的微调。基本场景训练命令是:

$ python train.py --epochs 10 --data coco128.yaml --weights yolov5s.pt --cache

为了进化特定于这个场景的超参数,从我们在第6.1节中定义的初始值开始,并最大化第6.2节中定义的适应度,增加--evolve

# Single-GPU
python train.py --epochs 10 --data coco128.yaml --weights yolov5s.pt --cache --evolve

# Multi-GPU
for i in 0 1 2 3; do
  nohup python train.py --epochs 10 --data coco128.yaml --weights yolov5s.pt --cache --evolve --device $i > evolve_gpu_$i.log &
done

# Multi-GPU bash while (not recommended)
for i in 0 1 2 3; do
  nohup "$(while true; do python train.py ... --evolve --device $i; done)" > evolve_gpu_$i.log &
done

​ 主要的遗传算子是交叉变异。在这项工作中,使用了变异,以90%的概率和0.04的方差,根据所有前几代最好的父母的组合来创造新的后代。结果记录在yolov5中/evolve.txt,每一代都保存着最适合的后代于yolov5/runs/evolve/hyp_evolved.yaml中。

​ 我们建议至少300代进化才能获得最佳结果。请注意,进化通常是昂贵和耗时的,因为基本场景需要数百次训练,可能需要数百或数千个小时的GPU训练。

项目超参数进化说明


7、迁移学习冻结yolov5层

​ 本指南解释了如何在迁移学习时冻结YOLOv5层。迁移学习是一种有用的方法,可以快速地在新数据上重新训练模型,而不必重新训练整个网络。相反,一部分初始权重被冻结在原位,其余权重用于计算损失,并由优化器更新。这需要比正常训练更少的资源,并且允许更快的训练时间,尽管它也可能导致最终训练精度的降低。

Transfer Learning with Frozen Layers

7.1 冻结骨干网络

# Freeze 
freeze = []  # parameter names to freeze (full or partial) 
for k, v in model.named_parameters(): 
    v.requires_grad = True  # train all layers 
    if any(x in k for x in freeze): 
        print('freezing %s' % k) 
        v.requires_grad = False

查看模型的参数名:

for k, v in model.named_parameters():
    print(k)

# Output
model.0.conv.conv.weight
model.0.conv.bn.weight
model.0.conv.bn.bias
model.1.conv.weight
model.1.bn.weight
model.1.bn.bias
model.2.cv1.conv.weight
model.2.cv1.bn.weight
...
model.23.m.0.cv2.bn.weight
model.23.m.0.cv2.bn.bias
model.24.m.0.weight
model.24.m.0.bias
model.24.m.1.weight
model.24.m.1.bias
model.24.m.2.weight
model.24.m.2.bias

发现模型的backbone是0~9层,所以在freeze中添加这几层的name。

freeze = ['model.%s.' % x for x in range(10)]  # parameter names to freeze (full or partial)

7.2 冻结全部yolov5的layers

这个简单,你可以将name全部放入freeze中,也可以设置所以层v.requires_grad = False

7.3 总结

​ 冻结yolov5的backbone之后,GPU的占用率下降了20%,显存占用下降了40%+。训练其他数据即的性能指标下降了0.0021%,这种性能损失还是可以接受的。

既然训练非backbone部分训练时间更快,GPU占用、显存占用更少,是不是可以先训练非backbone部分,再一起训练呢?


8、TensorRT模型加速

yolov5里面涉及的一些结构在TensorRT里面还没有支持,这些结构要加速就只能自己用C++实现。使用C++编程对很多人来说都是非常麻烦的事情,这里借鉴TensorRTx项目进入这个领域。

TensorRTx旨在通过TensorRT网络定义API实现流行的深度学习网络。我们知道,TensorRT有内置的解析器,包括caffeparser、uffparser、onnxparser等,但当我们使用这些解析器时,常常会遇到一些“不支持的操作或层”问题,特别是一些最新的模型正在使用新型的层。

​ 我们为什么不跳过所有的解析器呢?我们只需要使用TensorRT网络定义API来构建整个网络,并不复杂。

​ 这个项目是为了熟悉TensorRT API,也为了分享和向社区学习。

​ 所有模型首先在pytorch/mxnet/tensorflow中实现,并导出一个权重文件xxx.wts然后使用TensorRT进行权值加载、网络定义和推理。一些pytorch实现可以在我的repo Pytorchx中找到,其余的来自热门的开源实现。

posted @ 2020-12-24 13:42  巴蜀秀才  阅读(7787)  评论(0编辑  收藏  举报