Обучение PP-PicoDet для детектирования объектов

 Что это за модель

PicoDet - это модель для слабых вычислительных систем, которая заявляется, как более качественная и быстрая альтернатива YoloX и другим моделям. Вот ссылка:

https://gitee.com/paddlepaddle/PaddleDetection/blob/release/2.3/configs/picodet/README.md

Собственно вот ее результаты:

2024-02-08_17-25-03

Ну и сравнение с известными моделями:

2024-02-08_17-25-57

Т.е., если сравнить YoloX-Nano и PicoDet-S по mAP0.5-0.96, то это 25.8 против 30.7 для разрешения 416x416. Существенный прирост. 

Посмотрим далее, как обучить эту модель, поскольку обучение не такое простое, как к примеру в YoloX (про YOLOX пример обучения напишу в другой раз).

 Настройка среды

Я для упрощения решил использовать docker. Сначала проверяем версию CUDA на машине, она должна быть больше или равной требуемой.

2024-01-19_16-29-36

Затем запускаем docker Paddle в фоне (он сначала загрузится):

sudo docker run -d --runtime=nvidia -it --entrypoint "" -v "/home/ubuntu:/paddle" -p 8888:8888 paddlepaddle/paddle:2.6.0-gpu-cuda11.2-cudnn8.2-trt8.0 /bin/bash

После чего заходим в докер:

docker exec -it 90b73013182c   /bin/bash

и устанавливаем Юпитер:

pip install notebook

Далее запускаем юпитер в фоне:

jupyter notebook --ip 0.0.0.0 --allow-root --port 8888 &

Он покажет нужный токен, по которому вы можете зайти на сервер в браузере, а консоль можно закрыть

Создаем новый ноутбук и в нем заходим в основную директорию:

%cd /paddle

Проверяем, работает ли среда paddle:

!python -c "import paddle; print(paddle.__version__)"

У меня выдало 2.6.0

PaddleDetection

В докере нет модуля PaddleDetection, его нужно устанавливать самостоятельно. Сначала клонируем репозиторий:

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

Затем устанавливаем

%cd PaddleDetection

!pip install -r requirements.txt

Уже на этом этапе у меня часть модулей не установилась. Но я пошел дальше. 

!python setup.py install

Здесь тоже было несколько красных строк ошибок. Как следствие - часть модулей не установилось и я поставил вручную.

!pip install -U lazy_loader

!pip install pyparsing

!pip install cycler

!pip install kiwisolver

!pip install matplotlib

!pip install joblib

!pip install threadpoolctl

!pip install pytz

Проверяем работоспособность:

!python ppdet/modeling/tests/test_architectures.py

Warning: Unable to use numba in PP-Tracking, please install numba, for example(python3.7): `pip install numba==0.56.4`Warning: Unable to use numba in PP-Tracking, please install numba, for example(python3.7): `pip install numba==0.56.4`W0208 10:47:51.617843  1193 gpu_resources.cc:119] Please NOTE: device: 0, GPU Compute Capability: 8.6, Driver API Version: 12.0, Runtime API Version: 11.2W0208 10:47:51.637682  1193 gpu_resources.cc:164] device: 0, cuDNN Version: 8.1........----------------------------------------------------------------------
Ran 7 tests in 6.534s
OK

Наконец тест удачен

 Подготовка выборки и настройка обучающих файлов

У меня была некоторая база из 1 класса и изображений 320x320 в формате YOLO. Сначала надо сконвертировать в COCO. Создаем каталоги:

!mkdir ./dataset/

!mkdir ./dataset/images

!mkdir ./dataset/images/train2017

!mkdir ./dataset/images/val2017

!mkdir ./dataset/images/annotations

Копируем туда изображения:

!cp ./mydataset/train/*.jpg ./dataset/images/train2017

!cp ./mydataset/valid/*.jpg ./dataset/images/val2017

Устанавливаем утилиту для конвертации:

!pip install globox

И конвертируем:

from globox import *

from pathlib import Path

 

def convert(path,names_file,save_file):

  annotations = AnnotationSet.from_yolo_v5(

    folder=path,

    image_folder=path)

  annotations.save_coco(save_file,auto_ids=True)

 

convert('./mydataset/train/','classes.names','./dataset/images/annotations/train.json')

convert('./mydataset/valid/','classes.names','./dataset/images/annotations/valid.json')

Далее в папке ./configs/datasets/ я создал файл my_detection.yml, описывающий мой датасет:

metric: COCO

num_classes: 1

 

TrainDataset:

  name: COCODataSet

  image_dir: train2017

  anno_path: annotations/train.json

  dataset_dir: dataset/images

  data_fields: ['image', 'gt_bbox', 'gt_class', 'is_crowd']

 

EvalDataset:

  name: COCODataSet

  image_dir: val2017

  anno_path: annotations/valid.json

  dataset_dir: dataset/images

  allow_empty: true

 

TestDataset:

  name: ImageFolder

  anno_path: annotations/valid.json # also support txt (like VOC's label_list.txt)

  dataset_dir: dataset/images # if set, anno_path will be 'dataset_dir/anno_path'

После чего использовал picodet_s_320_coco_lcnet.yml для создания собственного настроечного файла обучения:

config = """

_BASE_: [

  '../datasets/my_detection.yml',

  '../runtime.yml',

  '_base_/picodet_v2.yml',

  '_base_/optimizer_300e.yml',

  '_base_/picodet_320_reader.yml',

]

 

pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/legendary_models/PPLCNet_x0_75_pretrained.pdparams

weights: output/picodet_s_320_coco/best_model

find_unused_parameters: True

use_ema: true

epoch: 200

snapshot_epoch: 5

 

LCNet:

  scale: 0.75

  feature_maps: [3, 4, 5]

 

LCPAN:

  out_channels: 96

 

PicoHeadV2:

  conv_feat:

    name: PicoFeat

    feat_in: 96

    feat_out: 96

    num_convs: 2

    num_fpn_stride: 4

    norm_type: bn

    share_cls_reg: True

    use_se: True

  feat_in_chan: 96

 

TrainReader:

  batch_size: 64

 

LearningRate:

  base_lr: 0.32

  schedulers:

  - !CosineDecay

    max_epochs: 300

  - !LinearWarmup

    start_factor: 0.1

    steps: 200

"""

 

with open("./configs/picodet/picodet_s_320_coco_lcnet_my.yml", 'w') as f:

    f.write(config)

Наконец можно перейти к обучению.

 Тренировка и экспорт модели

Запуск обучения:

!python tools/train.py -c configs/picodet/picodet_s_320_coco_lcnet_my.yml --eval

Конечный результат обучения и ключевые показатели:

2024-02-08_18-04-05

Далее нужно получить саму модель. Это делается так:

!python tools/export_model.py -c configs/picodet/picodet_s_320_coco_lcnet_my.yml \

              -o weights=output/best_model.pdparams --output_dir=inference_model

Однако тут меня встретила ошибка

RuntimeError: Can't call main_program when full_graph=False. Use paddle.jit.to_static(full_graph=True) instead.

Благодаря ссылке https://github.com/PaddlePaddle/PaddleDetection/commit/3e8b1076f5483b5eae4fce79738ce5f14f27a412

исправил trainer.py, добавив full_graph = True (Как может быть такое в релизном проекте?????)

2024-02-08_17-03-34

После чего модель была получена.

Далее получаем ONNX.

Устанавливаем нужные компоненты:

!pip install onnx

!pip install paddle2onnx

!pip install onnx-simplifier

Экспортируем модель:

!paddle2onnx --model_dir inference_model/picodet_s_320_coco_lcnet_my/ \

            --model_filename model.pdmodel  \

            --params_filename model.pdiparams \

            --opset_version 11 \

            --save_file picodet_s_320_coco.onnx

Упрощаем ее:

!python -m onnxsim picodet_s_320_coco.onnx picodet_s_320_coco_processed.onnx

Что выдает такой результат:

2024-02-08_18-09-55

Ну и можно посмотреть саму модель в Netron:

2024-02-08_18-11-13

С использованием в TensorRT есть проблемы из-за внешних динамических слоев:

[6] Invalid Node - p2o.TopK.0

This version of TensorRT only supports input K as an initializer. Try applying constant folding on the model using Polygraphy: https://github.com/NVIDIA/TensorRT/tree/master/tools/Polygraphy/examples/cli/surgeon/02_folding_constants

Но это уже другая история