用PaddleDetection做一个完整的目标检测项目(上)

文章转载自:微信公众号:飞桨PaddlePaddle的微信文章
原文章中由于排版问题,导致文字遮挡,不便阅读,因此对文章格式稍作更改,增加了一些关键词加粗,便于后续阅读。

PaddleDetection 是百度飞桨推出的物体检测统一框架。支持现有的RCNN、SSD、YOLO等系列模型、支持 ResNet、ResNet-VD、ResNeXt、ResNeXt-VD、SENet、MobileNet、DarkNet等主干网络。针对不同的业务场景(性能、目标大小、准确率等)可以选择框架中的不同模块组合得到最适合的模型,实现任务。相比于tensorflow的Object_Detection,优势之一就是将YOLOv3这一目标检测的快速算法融合到了框架下。
文章将以一个Yolov3 识别水果的例子为说明,详解如何利用PaddleDetection完成一个项目。

项目用到的工具
硬件: Win10(RTX2060)笔记本、某品牌服务器(4*T4)工业相机(Hikvision)
软件: pycharm、VS2019。

目录:

1、环境部署

2、数据集准备

3、训练

4、训练过程可视化

5、模型导出

6、python进行单张/多张图片的预测

7、python+qt(给客户的演示demo)

8、C++进行单张预测(含编译简介)

9、C++预测代码封装成DLL、配合C#完成一个整体项目

10、扩展:关于PaddlePaddle代码数据读取的方式

用PaddleDetection做一个完整的目标检测项目(一)

  • 目录:
  • 01 环境部署
    • 1.1 安装PaddlePaddle
    • 1.2 安装COCO-API
    • 1.3 选择一个文件夹,下载PaddleDetection
    • 1.4 安装所需的Python其他依赖库
    • 1.5 确认测试通过
  • 02 数据集的准备
    • 2.1 数据标注:
    • 2.2 创建VOC数据集格式
  • 03 训练
    • 3.1 修改配置文件
    • 3.2 训练相关可选参数说明
    • 3.3 预训练模型来源
    • 3.4 常见错误
  • 04 训练过程可视化
  • 05 模型导出
  • 06 模型推断
    • 6.1 修改配置文件
    • 6.2 使用单张图片进行预测
      • 1)主要参数说明:
      • 2) 更多参数
      • 3)注意
    • 6.2 使用一个文件夹中图片进行预测

01 环境部署

1.1 安装PaddlePaddle

利用anaconda创建一个名字叫做paddle-detection的Paddle环境(备注:下文中命令提示窗口出现的(paddle)是指该项目环境的名称叫做paddle)。安装链接如下:PaddlePaddle

1.2 安装COCO-API

pip install Cython
pip install git+https://github.com/philferriere/cocoapi.git#subdirectory=PythonAPI

1.3 选择一个文件夹,下载PaddleDetection

第一种方式:直接从github官网上进行下载:PaddleDetection
用PaddleDetection做一个完整的目标检测项目(上)_第1张图片
第二种方式:使用git进行下载:

git clone https://github.com/PaddlePaddle/PaddleDetection.git

1.4 安装所需的Python其他依赖库

依赖库文档在requirements.txt中给出,可使用

pip install -r requirements.txt

如下图所示是requirements.txt文档中的内容。
用PaddleDetection做一个完整的目标检测项目(上)_第2张图片
如图为requirements文件中的内容,图中所示的均为PaddleDetection的依赖库。用PaddleDetection做一个完整的目标检测项目(上)_第3张图片
在下图所示内容中打 “cmd”,然后出现在该路径的命令提示符。
用PaddleDetection做一个完整的目标检测项目(上)_第4张图片
激活环境,并且 pip install -r requirements.txt
在这里插入图片描述
然后运行
在这里插入图片描述当显示Successfully…… ,基本上表示安装完成了,具体是否成功,下一步的测试验证。

1.5 确认测试通过

set PYTHONPATH=`pwd`<在这里插入地址>:$PYTHONPATH
python ppdet/modeling/tests/test_architectures.py

备注: 在paddle自带的文档中为export PYTHONPATH=pwd:$PYTHONPATH

export是Linux下的用法,在Windows下将exoprt改成set。

但是发现运行报错,报错内容如下:
用PaddleDetection做一个完整的目标检测项目(上)_第5张图片
错误提示发现缺少 “ppdet” 这个模块,ppdet(其实就是paddle detection的一个缩写),但是我们在项目的路径里面可以发现有这个模块,原因 就是,我们的代码所在的路径无法读取到该文件下的代码,因此,我们需要在代码中进行改进一下。
找到 ppdet/modeling/tests/test_architectures.py这个文件,然后添加红框所示的代码。
备注: 第二个红框里面的路径是我的项目路径。
用PaddleDetection做一个完整的目标检测项目(上)_第6张图片
出现如图所示的代码,表示运行成功。
用PaddleDetection做一个完整的目标检测项目(上)_第7张图片
至此,所有的运行环境已经配置成功。

02 数据集的准备

2.1 数据标注:

目前项目使用的数据集格式是VOC数据格式,使用labelimg作为标注工具,标注工具的下载安装见链接:labelimg

特别说明: 项目中使用的数据集是PaddleDetection提供的演示示例数据集,下文将通过使用该数据集来说明。
首先,下载该数据集,下载地址:水果数据集百度盘地址 提取码: vw3b
下载后如下图所示:
标注方式如下:

打开软件,并导入图片:
用PaddleDetection做一个完整的目标检测项目(上)_第8张图片
用PaddleDetection做一个完整的目标检测项目(上)_第9张图片
选取标注文件的保存路径:
用PaddleDetection做一个完整的目标检测项目(上)_第10张图片
点击Change Save Dir ,然后将标注文件保存在某一个路径下。

开始标注:点击Create\nRectBo—框选目标–命名(下图中命名为apple)–点击OK—点击Save(完成一张)–点击Next Image 标注下一张。
用PaddleDetection做一个完整的目标检测项目(上)_第11张图片
所有标注完成以后会生成很多的xml文件。
用PaddleDetection做一个完整的目标检测项目(上)_第12张图片
打开一个xml文件:
用PaddleDetection做一个完整的目标检测项目(上)_第13张图片
具体信息如下:
用PaddleDetection做一个完整的目标检测项目(上)_第14张图片

2.2 创建VOC数据集格式

在这里插入图片描述
其中Annotations存放标注生成的xml文件,JPEGImage存放图片,ImageSets存放对训练集和数据集的划分。ImageSet下有Main文件,Mian下需要建立一个label_list.txt。label_list.txt是指标注的目标的名称。其内容如下:
用PaddleDetection做一个完整的目标检测项目(上)_第15张图片
备注: train.txt、val.txt是根据下文中代码(该代码会命名为:get_list.py,放置在了数据集链接的文件里面)生成,分别是对训练集和验证集的划分:

接上文所述,是生成生成train.txt、val.txt的代码,该代码会将300张水果图片分成240张训练集和60张验证集。​

import os
import random

train_precent=0.7
xml="C:/Users/zhili/Desktop/fruit-detection/Annotations"
save="C:/Users/zhili/Desktop/fruit-detection/ImageSets/Main"
total_xml=os.listdir(xml)

num=len(total_xml)
tr=int(num*train_precent)
train=range(0,tr)

ftrain=open("C:/Users/zhili/Desktop/fruit-detection/ImageSets/Main/train.txt","w")
ftest=open("C:/Users/zhili/Desktop/fruit-detection/ImageSets/Main/test.txt","w")

for i in range(num):
    name=total_xml[i][:-4]+"\n"
    if i in train:
        ftrain.write(name)
    else:
        ftest.write(name)
ftrain.close()
ftest.close()

如下图是生成的train文件。
用PaddleDetection做一个完整的目标检测项目(上)_第16张图片

备注: 在Main文件夹中生成的train.txt文件和val.txt文件仅仅是对数据集的划分,还需要进一步的利用如下代码(create_list.py,该代码是paddle提供的)生成含有路径信息以及图像和xml文件一一对应的文件。

import osimport os.path as osp
import re
import random

devkit_dir = './'
years = ['2007', '2012']


def get_dir(devkit_dir,  type):
    return osp.join(devkit_dir, type)


def walk_dir(devkit_dir):
    filelist_dir = get_dir(devkit_dir, 'ImageSets/Main')
    annotation_dir = get_dir(devkit_dir, 'Annotations')
    img_dir = get_dir(devkit_dir, 'JPEGImages')
    trainval_list = []
    test_list = []
    added = set()

    for _, _, files in os.walk(filelist_dir):
        for fname in files:
            img_ann_list = []
            if re.match('train\.txt', fname):
                img_ann_list = trainval_list
            elif re.match('val\.txt', fname):
                img_ann_list = test_list
            else:
                continue
            fpath = osp.join(filelist_dir, fname)
            for line in open(fpath):
                name_prefix = line.strip().split()[0]
                if name_prefix in added:
                    continue
                added.add(name_prefix)
                ann_path = osp.join(annotation_dir, name_prefix + '.xml')
                img_path = osp.join(img_dir, name_prefix + '.jpg')
                assert os.path.isfile(ann_path), 'file %s not found.' % ann_path
                assert os.path.isfile(img_path), 'file %s not found.' % img_path
                img_ann_list.append((img_path, ann_path))

    return trainval_list, test_list


def prepare_filelist(devkit_dir, output_dir):
    trainval_list = []
    test_list = []
    trainval, test = walk_dir(devkit_dir)
    trainval_list.extend(trainval)
    test_list.extend(test)
    random.shuffle(trainval_list)
    with open(osp.join(output_dir, 'train.txt'), 'w') as ftrainval:
        for item in trainval_list:
            ftrainval.write(item[0] + ' ' + item[1] + '\n')

    with open(osp.join(output_dir, 'val.txt'), 'w') as ftest:
        for item in test_list:
            ftest.write(item[0] + ' ' + item[1] + '\n')


if __name__ == '__main__':
    prepare_filelist(devkit_dir, '.')

用PaddleDetection做一个完整的目标检测项目(上)_第17张图片
这个时候的新生成的train.txt的内容如下:
用PaddleDetection做一个完整的目标检测项目(上)_第18张图片
最终生成如下所示的数据集格式如下,其中label_list.txtMian中的label_list.txt一致。train.txt文件和val.txt是新生成的图像-xml的名称路径对应文件。

该完成后的数据集作者放置在了项目文件夹下:dataset/fruit下,并命名为该文件夹为fruit-detection,如下图所示。
数据集生成后就可以根据如下的命令进行训练。
用PaddleDetection做一个完整的目标检测项目(上)_第19张图片

03 训练

3.1 修改配置文件

在训练之前,首先了解模型的配置文件,如文章开头所述:PaddleDetection 是飞桨推出的物体检测统一框架。支持现有的RCNN、SSD、YOLO等系列模型、支持 ResNet、ResNet-VD、ResNeXt、ResNeXt-VD、SENet、MobileNet、DarkNet主干网络。而实现我们如何快速的完成不同模型的训练体验,就是需要依靠配置文件的切换。
如下所示就是该项目配置文件 (config文件) 的路径:
用PaddleDetection做一个完整的目标检测项目(上)_第20张图片

如图下图就是我们选取的深度学习的配置文件,我们选取yolov3_mobilenet_v1_fruit.yml做为本项目的训练配置文件。该配置文件是使用YOLO v3的模型结构,同时主干网络为 Mobilenrt V1
这些参数可以根据我们自己的数据情况进行设计。
如设置:
最大迭代步数:max_iters
预训练模型的来源:pretrain_weights
数据路径:dataset_dir
Batch_size的大小:batch_size
数据集类别:num_classes
用PaddleDetection做一个完整的目标检测项目(上)_第21张图片
需要着重关注点如下:需要根据自己的不同数据集进行训练。
用PaddleDetection做一个完整的目标检测项目(上)_第22张图片
用PaddleDetection做一个完整的目标检测项目(上)_第23张图片
用PaddleDetection做一个完整的目标检测项目(上)_第24张图片

3.2 训练相关可选参数说明

在将config文件配置好了以后,就可以进行训练

set PYTHONPATH=$PYTHONPATH:.
set CUDA_VISIBLE_DEVICES=0(默认只有一个GPU) 
python -u tools/train.py -c configs/yolov3_mobilenet_v1_fruit.yml --use_tb=True --tb_log_dir=tb_fruit_dir/scalar --eval

运行如下命令,即可开始训练
用PaddleDetection做一个完整的目标检测项目(上)_第25张图片
出现如下图所示现象,可以表示为训练正常,可等待训练结束。
用PaddleDetection做一个完整的目标检测项目(上)_第26张图片

关于训练命令的阐述:

-c configs/yolov3_mobilenet_v1_fruit.yml 用来指定配置文件

–use_tb 是否使用tb-paddle记录数据,进而在TensorBoard中显示,默认值是False

–tb_log_dir 指定 tb-paddle 记录数据的存储路径

–eval 是否边训练边测试

备注:
关于–eval参数的使用:
在训练中交替执行评估, 评估在每个snapshot_iter时开始。每次评估后还会评出最佳mAP模型保存到best_model文件夹下,建议训练时候使用该参数,可以使得完成训练后快速找到最好的模型。

可选参数列表
以下列表可以通过 –help 查看

FLAG 支持脚本 用途 默认值 备注
-c ALL 指定配置文件 None
-o ALL 设置配置文件里的参数内容 None 使用-o配置相较于-c选择的配置文件具有更高的优先级。例如:-o use_gpu=False max_iter=10000
-r/–resume_checkpoint train 从某一检查点恢复训练 None -r output/faster_rcnn_r50_1x/10000
–eval train 是否边训练边测试 False
–output_eval train/eval 编辑评测保存json路径 当前路径 –output_eval ./json_result
–fp16 train 是否使用混合精度训练模式 False 需使用GPU训练
–loss_scale train 设置混合精度训练模式中损失值的缩放比例 8.0 需先开启–fp16后使用
–json_eval eval 是否通过已存在的bbox.json或者mask.json进行评估 False json文件路径在–output_eval中设置
–output_dir infer 输出推断后可视化文件 ./output –output_dir output
–draw_threshold infer 可视化时分数阈值 0.5 –draw_threshold 0.7
–infer_dir infer 用于推断的图片文件夹路径 None
–infer_img infer 用于推断的图片路径 None 相较于–infer_dir具有更高优先级
–use_tb train/infer 是否使用tb-paddle记录数据,进而在TensorBoard中显示 False

Fine-tune其他任务
使用预训练模型fine-tune其他任务时,可采用如下两种方式:

  1. 在YAML配置文件中设置finetune_exclude_pretrained_params
  2. 在命令行中添加-o finetune_exclude_pretrained_params对预训练模型进行选择性加载。
python -u tools/train.py -c configs/faster_rcnn_r50_1x.yml \
                       -o pretrain_weights=output/faster_rcnn_r50_1x/model_final/ \
                          finetune_exclude_pretrained_params=['cls_score','bbox_pred']

提示:

  1. CUDA_VISIBLE_DEVICES 参数可以指定不同的GPU。例如: export CUDA_VISIBLE_DEVICES=0,1,2,3. GPU计算规则可以参考 FAQ
  2. 若本地未找到数据集,将自动下载数据集并保存在~/.cache/paddle/dataset中。
  3. 预训练模型自动下载并保存在〜/.cache/paddle/weights中。
  4. 模型checkpoints默认保存在output中,可通过修改配置文件中save_dir进行配置。
  5. RCNN系列模型CPU训练在PaddlePaddle 1.5.1及以下版本暂不支持。

混合精度训练
通过设置 --fp16 命令行选项可以启用混合精度训练。目前混合精度训练已经在Faster-FPN, Mask-FPN 及 Yolov3 上进行验证,几乎没有精度损失(小于0.2 mAP)。
建议使用多进程方式来进一步加速混合精度训练。示例如下。

python -m paddle.distributed.launch --selected_gpus 0,1,2,3,4,5,6,7 tools/train.py --fp16 -c configs/faster_rcnn_r50_fpn_1x.yml

3.3 预训练模型来源

关于预训练模型的来源:
在本案例中,预训练模型是通过一个链接方式在训练开始时候加载进行下载的,其实飞桨提供了丰富的预训练模型库,具体链接为:
MODEL_ZOO

用PaddleDetection做一个完整的目标检测项目(上)_第27张图片

3.4 常见错误

训练过程出现的一个错误:
作者使用的电脑是笔记本,显卡型号是RTX2060 显存为6G。在训练过程中出现如下错误。
用PaddleDetection做一个完整的目标检测项目(上)_第28张图片
通过命令提示符中发现,竟然出现了(7184, 7184, 3)这样大的图片,而本身数据像素多数为1000*1000左右。解决改问题的方式有两个:

第一个是修改配置文件中的bufsize这个参数,将该值由128改成64(备注,PaddleDetection新更新的配置文件文档中,已经将该值设置成为32),即可正常运行。在这里解释一下:PaddleDetection YOLOv3系列模型训练时,由于图像增强等预处理方式较多,默认会开启多进程加速,子进程完成图像读取,图像增强等预处理后,会将输出结果放到一个队列里面等待模型训练时获取,bufsize这个参数即为该队列的最大长度,该队列存储在内存中,若机器内存较小并且队列长度bufsize设置得较大,就会有上述报错,报错内容为内存不够,无法给队列继续分配内存。这个时候只要把bufsize调小一些保存内存足够放下队列即可。

第二个方式是修改配置文件中的ratio的参数,将该值由4.0改成了2.0,即可正常运行。PaddleDetection中设置了数据增强功能:在RandomExpand这种数据增强里,首先新建一张(原图大小 * ratio)大小的空白图片,然后在这张空白图片上随机找个位置,把原图贴上去,之后再把这张新图片缩放成原图大小。当然如果显存足够大的话,也可以按照默认值来。
用PaddleDetection做一个完整的目标检测项目(上)_第29张图片

04 训练过程可视化

训练期间可以通过tensorboard实时观察loss和精度值,启动命令如下:

tensorboard --logdir tb_fruit_dir/scalar

在这里插入图片描述
用PaddleDetection做一个完整的目标检测项目(上)_第30张图片

05 模型导出

当我们训练完成后,在项目文件的output中可以看到我们生成的模型文件,在代码中我们设置模型每迭代200步保存一次,训练总共有20000步,因此会生成100个训练文件,由于我们使用 --eval参数进行边训练边测试,因此我们可以获得训练过程中最好的模型文件,我们将做好的模型进行导出。
用PaddleDetection做一个完整的目标检测项目(上)_第31张图片
执行如下命令:

python tools/export_model.py -c configs/yolov3_mobilenet_v1_fruit.yml --output_dir=./inference_model -o weights=output/yolov3_mobilenet_v1_fruit/best_mode

最终导出如下所示:
在这里插入图片描述
用PaddleDetection做一个完整的目标检测项目(上)_第32张图片
用PaddleDetection做一个完整的目标检测项目(上)_第33张图片
特别备注 :目前导出的模型使用是为了后面C++预测的,python预测目前不支持加载导出的模型。

06 模型推断

PaddleDetection给出的模型预测代码是infer.py。该文件在项目文件夹tools里面。改代码提供了两种预测方式第一种是单张图片预测,第二种是以一个文件夹中的图片进行预测。
通过设置不同的预测方式达到自己的目的。如下,使用 –infer_img=demo/xxx.jpg 为单张图片进行预测,使用 –infer_dir=demo 为用一个文件夹进行预测。其中demo是放置测试图片的地方

6.1 修改配置文件

推断使用的配置文件与训练不同,格式可以参考tools/cpp_demo.yml文件,更改后重命名为yolov3_mobilenet_v1_fruit.yml,保存到out文件夹下。

# demo for cpp_infer.py

use_python_inference: true # whether to use python inference
mode: FP16 # trt_fp32, trt_fp16, trt_int8, fluid
arch: YOLO # YOLO, SSD, RCNN, RetinaNet
min_subgraph_size: 3 # need 3 for YOLO arch

# visualize the predicted image
metric: COCO # COCO, VOC
draw_threshold: 0.5

Preprocess:
- type: Resize
  target_size: 608
  max_size: 640
- type: Normalize
  mean:
  - 0.485
  - 0.456
  - 0.406
  std:
  - 0.229
  - 0.224
  - 0.225
  is_scale: True
- type: Permute
  to_bgr: False
- type: PadStride
  stride: 0 # set 32 on FPN and 128 on RetinaNet

6.2 使用单张图片进行预测

测试的结果保存在output这个文件夹中

python -u tools/infer.py --model_path=output/model/ --config_path=out/yolov3_mobilenet_v1_fruit.yml --infer_img=demo/orange_71.jpg --visualize

1)主要参数说明:

  • model_path: inference_model保存路径
  • config_path: 参数配置、数据预处理配置文件,注意不是训练时的配置文件
  • infer_img: 待预测图片
  • visualize: 是否保存可视化结果,默认保存路径为output/

2) 更多参数

更多参数可在tools/cpp_demo.yml中查看,主要参数:

  • use_python_inference:若为true,使用fluid.io.load_inference_model接口,走训练引擎分支预测。
  • mode:支持fluid、trt_fp32、trt_fp16、trt_int8,当use_python_inference为false时起作用。fluid是通过预测引擎分支预测,trt_fp32、trt_fp16、 trt_int8是通过预测引擎分支预测,后端基于TensorRT的FP32、FP16精度。
  • min_subgraph_size:当设置mode采用TensorRT时,注意设置此参数。设置与模型arch相关,对部分arch需要调大该参数,一般设置为40适用于所有模型。适当的 调小min_subgraph_size会对预测有加速效果,例如YOLO中该参数可设置为3。
  • Preprocess:数据预处理配置,一般来说顺序为Resize -> Normalize -> Permute,对于FPN模型还需配置PadStride。不同模型的数据预处理参考训练配置 中的TestReader部分。

3)注意

基于TensorRT预测,数据预处理Resize设置的shape必须保持与模型导出时shape大小一致。
预处理中PadStride为输入图片右下角填充0,默认设置stride为0,即不对输入图片做padding操作。模型中包含FPN结构时,stride应设置为32。模型为RetinaNet系列模型时,stride应设置为128.
PaddlePaddle默认的GPU安装包(<=1.7),是不支持基于TensorRT进行预测,如果想基于TensorRT加速预测,需要自行编译,详细可参考预测库编译教程。

特别注意: 类似这种在命令提示符下输入时候,一定注意自己输入过程中的出现的多余空格问题:如下图所示,weights=后面出现了一个多的空格,导致模型文件读不到。
用PaddleDetection做一个完整的目标检测项目(上)_第34张图片
用PaddleDetection做一个完整的目标检测项目(上)_第35张图片
用PaddleDetection做一个完整的目标检测项目(上)_第36张图片

6.2 使用一个文件夹中图片进行预测

测试的结果保存在infer_output这个文件夹中:

python -u tools/infer.py -c configs/yolov3_mobilenet_v1_fruit.yml -o weights=output/yolov3_mobilenet_v1_fruit/best_model --infer_img=demo/test --output_dir=infer_output/testout

用PaddleDetection做一个完整的目标检测项目(上)_第37张图片
用PaddleDetection做一个完整的目标检测项目(上)_第38张图片

说明:

1、该文章多数内容来源于PaddleDetection的文档,链接
PaddleDetection的文档

2、该文章运行环境为Windows10 ,在原有文档基础上修改了一些Linux的用法。增加了创建VOC数据集以及如何划分训练集和验证集。

3、该文章写作过程中,受到了高松鹤同学、百度飞桨同学的大力帮助。

如果您加入官方 QQ 群,您将遇上大批志同道合的深度学习同学。官方 QQ 群:703252161。

你可能感兴趣的:(PaddlePaddle)