目录
PaddleDetection是一个基于PaddlePaddle的目标检测端到端开发套件,可用于版面分析等通过视觉特征进行检测、分类、(分割)、 关键点检测的任务( 2D关键点检测),配合clas完成 属性识别等
数据准备
数据处理模块
PaddleDetection的数据处理模块的所有代码逻辑在ppdet/data/
中,数据处理模块用于加载数据并将其转换成适用于物体检测模型的训练、评估、推理所需要的格式。 数据处理模块的主要构成如下架构所示:
ppdet/data/
├── reader.py # 基于Dataloader封装的Reader模块
├── source # 数据源管理模块
│ ├── dataset.py # 定义数据源基类,各类数据集继承于此
│ ├── coco.py # COCO数据集解析与格式化数据
│ ├── voc.py # Pascal VOC数据集解析与格式化数据
│ ├── widerface.py # WIDER-FACE数据集解析与格式化数据
│ ├── category.py # 相关数据集的类别信息
├── transform # 数据预处理模块
│ ├── batch_operators.py # 定义各类基于批量数据的预处理算子
│ ├── op_helper.py # 预处理算子的辅助函数
│ ├── operators.py # 定义各类基于单张图片的预处理算子
│ ├── gridmask_utils.py # GridMask数据增强函数
│ ├── autoaugment_utils.py # AutoAugment辅助函数
├── shm_utils.py # 用于使用共享内存的辅助函数
数据源管理模块-source
- 基类DetDataSet
数据集定义在source
目录下,其中dataset.py
中定义了数据集的基类DetDataSet
, 所有的数据集均继承于基类,DetDataset
基类里定义了如下等方法:
当一个数据集类继承自DetDataSet
,那么它只需要实现parse_dataset
函数即可。parse_dataset根据数据集设置的数据集根路径dataset_dir
,图片文件夹image_dir
, 标注文件路径anno_path
取出所有的样本,并将其保存在一个列表roidbs
中,每一个列表中的元素为一个样本xxx_rec(比如coco_rec或者voc_rec),用dict表示,dict中包含样本的image, gt_bbox, gt_class等字段。COCO和Pascal-VOC数据集中的xxx_rec的数据结构定义如下:
xxx_rec = {
'im_file': im_fname, # 一张图像的完整路径
'im_id': np.array([img_id]), # 一张图像的ID序号
'h': im_h, # 图像高度
'w': im_w, # 图像宽度
'is_crowd': is_crowd, # 是否是群落对象, 默认为0 (VOC中无此字段)
'gt_class': gt_class, # 标注框标签名称的ID序号
'gt_bbox': gt_bbox, # 标注框坐标(xmin, ymin, xmax, ymax)
'gt_poly': gt_poly, # 分割掩码,此字段只在coco_rec中出现,默认为None
'difficult': difficult # 是否是困难样本,此字段只在voc_rec中出现,默认为0
}
xxx_rec中的内容也可以通过DetDataSet的data_fields参数来控制,即可以过滤掉一些不需要的字段,但大多数情况下不需要修改,按照configs/datasets
中的默认配置即可。
此外,在parse_dataset函数中,保存了类别名到id的映射的一个字典cname2cid
。在coco数据集中,会利用COCO API从标注文件中加载数据集的类别名,并设置此字典。在voc数据集中,如果设置use_default_label=False
,将从label_list.txt
中读取类别列表,反之将使用voc默认的类别列表。
数据预处理模块-transform
数据增强算子
PaddleDetection中支持了种类丰富的数据增强算子,有单图像数据增强算子与批数据增强算子两种方式,您可选取合适的算子组合使用。单图像数据增强算子定义在transform/operators.py
中,已支持的单图像数据增强算子详见下表:
批数据增强算子定义在transform/batch_operators.py
中, 目前支持的算子列表如下:
几点说明:
- 数据增强算子的输入为sample或者samples,每一个sample对应上文所说的
DetDataSet
输出的roidbs中的一个样本,如coco_rec或者voc_rec - 单图像数据增强算子(Mixup, Cutmix等除外)也可用于批数据处理中。但是,单图像处理算子和批图像处理算子仍有一些差异,以RandomResize和BatchRandomResize为例,RandomResize会将一个Batch中的每张图片进行随机缩放,但是每一张图像Resize之后的形状不尽相同,BatchRandomResize则会将一个Batch中的所有图片随机缩放到相同的形状。
- 除BatchRandomResize外,定义在
transform/batch_operators.py
的批数据增强算子接收的输入图像均为CHW形式,所以使用这些批数据增强算子�前请先使用Permute进行处理。如果用到Gt2xxxTarget算子,需要将其放置在靠后的位置。NormalizeBox算子建议放置在Gt2xxxTarget之前。将这些限制条件总结下来,推荐的预处理算子的顺序为
- XXX: {}
- ...
- BatchRandomResize: {...} # 如果不需要,可以移除,如果需要,放置在Permute之前
- Permute: {} # 必须项
- NormalizeBox: {} # 如果需要,建议放在Gt2XXXTarget之前
- PadBatch: {...} # 如果不需要可移除,如果需要,建议放置在Permute之后
- Gt2XXXTarget: {...} # 建议与PadBatch放置在最后的位置
自定义数据增强算子
如果需要自定义数据增强算子,那么您需要了解下数据增强算子的相关逻辑。数据增强算子基类为定义在transform/operators.py
中的BaseOperator
类,单图像数据增强算子与批数据增强算子均继承自这个基类。完整定义参考源码,以下代码显示了BaseOperator
类的关键函数: apply和__call__方法
class BaseOperator(object):
...
def apply(self, sample, context=None):
return sample
def __call__(self, sample, context=None):
if isinstance(sample, Sequence):
for i in range(len(sample)):
sample[i] = self.apply(sample[i], context)
else:
sample = self.apply(sample, context)
return sample
__call__方法为BaseOperator
的调用入口,接收一个sample(单图像)或者多个sample(多图像)作为输入,并调用apply函数对一个或者多个sample进行处理。大多数情况下,你只需要继承BaseOperator
重写apply方法或者重写__call__方法即可,如下所示,定义了一个XXXOp继承自BaseOperator,并注册:
@register_op
class XXXOp(BaseOperator):
def __init__(self,...):
super(XXXImage, self).__init__()
...
# 大多数情况下只需要重写apply方法
def apply(self, sample, context=None):
...
省略对输入的sample具体操作
...
return sample
# 如果有需要,可以重写__call__方法,如Mixup, Gt2XXXTarget等
# def __call__(self, sample, context=None):
# ...
# 省略对输入的sample具体操作
# ...
# return sample
大多数情况下,只需要重写apply方法即可,如transform/operators.py
中除Mixup和Cutmix外的预处理算子。对于批处理的情况一般需要重写__call__方法,如transform/batch_operators.py
的预处理算子。
Reader
Reader相关的类定义在reader.py
, 其中定义了BaseDataLoader
类。BaseDataLoader
在paddle.io.DataLoader
的基础上封装了一层,其具备paddle.io.DataLoader
的所有功能,并能够实现不同模型对于DetDataset
的不同需求,如可以通过对Reader进行设置,以控制DetDataset支持Mixup, Cutmix等操作。除此之外,数据预处理算子通过Compose
类和BatchCompose
类组合起来分别传入DetDataset
和paddle.io.DataLoader
中。 所有的Reader类都继承自BaseDataLoader
类,具体可参见源码。
相关配置及运行
Dataset、Reader配置
- Dataset配置
关于Dataset的配置文件存在于configs/datasets
文件夹。比如COCO数据集的配置文件如下:
metric: COCO # 目前支持COCO, VOC, OID, WiderFace等评估标准
num_classes: 80 # num_classes数据集的类别数,不包含背景类
TrainDataset:
!COCODataSet
image_dir: train2017 # 训练集的图片所在文件相对于dataset_dir的路径
anno_path: annotations/instances_train2017.json # 训练集的标注文件相对于dataset_dir的路径
dataset_dir: dataset/coco #数据集所在路径,相对于PaddleDetection路径
data_fields: ['image', 'gt_bbox', 'gt_class', 'is_crowd'] # 控制dataset输出的sample所包含的字段,注意此为TrainDataset独有的且必须配置的字段
EvalDataset:
!COCODataSet
image_dir: val2017 # 验证集的图片所在文件夹相对于dataset_dir的路径
anno_path: annotations/instances_val2017.json # 验证集的标注文件相对于dataset_dir的路径
dataset_dir: dataset/coco # 数据集所在路径,相对于PaddleDetection路径
TestDataset:
!ImageFolder
anno_path: annotations/instances_val2017.json # 标注文件所在路径,仅用于读取数据集的类别信息,支持json和txt格式
dataset_dir: dataset/coco # 数据集所在路径,若添加了此行,则`anno_path`路径为`dataset_dir/anno_path`,若此行不设置或去掉此行,则`anno_path`路径即为`anno_path`
在PaddleDetection的yml配置文件中,使用!
直接序列化模块实例(可以是函数,实例等),上述的配置文件均使用Dataset进行了序列化。
注意: 请运行前自行仔细检查数据集的配置路径,在训练或验证时如果TrainDataset和EvalDataset的路径配置有误,会提示自动下载数据集。若使用自定义数据集,在推理时如果TestDataset路径配置有误,会提示使用默认COCO数据集的类别信息
- Reader配置
不同模型专用的Reader定义在每一个模型的文件夹下,如yolov3的Reader配置文件定义在configs/yolov3/_base_/yolov3_reader.yml
。一个Reader的示例配置如下:
worker_num: 2
TrainReader:
sample_transforms:
- Decode: {}
...
batch_transforms:
...
batch_size: 8
shuffle: true
drop_last: true
use_shared_memory: true
EvalReader:
sample_transforms:
- Decode: {}
...
batch_size: 1
TestReader:
inputs_def:
image_shape: [3, 608, 608]
sample_transforms:
- Decode: {}
...
batch_size: 1
你可以在Reader中定义不同的预处理算子,每张卡的batch_size以及DataLoader的worker_num等。
运行
在PaddleDetection的训练、评估和测试运行程序中,都通过创建Reader迭代器。Reader在ppdet/engine/trainer.py
中创建。下面的代码展示了如何创建训练时的Reader
from ppdet.core.workspace import create
# build data loader
self.dataset = cfg['TrainDataset']
self.loader = create('TrainReader')(selfdataset, cfg.worker_num)
相应的预测以及评估时的Reader与之类似,具体可参考ppdet/engine/trainer.py
源码。
COCO数据集
标注格式
COCO数据标注是将所有训练图像的标注都存放到一个json文件中。数据以字典嵌套的形式存放。
json文件中包含以下key:
- info,表示标注文件info。
- licenses,表示标注文件licenses。
- images,表示标注文件中图像信息列表,每个元素是一张图像的信息。如下为其中一张图像的信息:
{
'license': 3, # license
'file_name': '000000391895.jpg', # file_name
# coco_url
'coco_url': 'http://images.cocodataset.org/train2017/000000391895.jpg',
'height': 360, # image height
'width': 640, # image width
'date_captured': '2013-11-14 11:18:45', # date_captured
# flickr_url
'flickr_url': 'http://farm9.staticflickr.com/8186/8119368305_4e622c8349_z.jpg',
'id': 391895 # image id
}
- annotations,表示标注文件中目标物体的标注信息列表,每个元素是一个目标物体的标注信息。如下为其中一个目标物体的标注信息:
{
'segmentation': # 物体的分割标注
'area': 2765.1486500000005, # 物体的区域面积
'iscrowd': 0, # iscrowd
'image_id': 558840, # image id
'bbox': [199.84, 200.46, 77.71, 70.88], # bbox [x1,y1,w,h]
'category_id': 58, # category_id
'id': 156 # image id
}
文件结构
dataset_detection/crop/
├── annotations
│ ├── instance_train.json
│ ├── instance_val.json
├── train_data
│ ├── img_detail...
│ ├── img_invoice...
│ │ ...
├── test_data
│ ├── img_detail...
│ ├── img_invoice...
│ │ ...
在source/coco.py
中定义并注册了COCODataSet
数据集类,其继承自DetDataSet
,并实现了parse_dataset方法,调用COCO API加载并解析COCO格式数据源roidbs
和cname2cid
,具体可参见source/coco.py
源码。将其他数据集转换成COCO格式可以参考用户数据转成COCO数据
Pascal VOC数据集
主要由xml文件和image文件组成,其组织结构如下所示:
dataset/voc/
├── trainval.txt
├── test.txt
├── label_list.txt (optional)
├── VOCdevkit/VOC2007
│ ├── Annotations
│ ├── 001789.xml
│ │ ...
│ ├── JPEGImages
│ ├── 001789.jpg
│ │ ...
│ ├── ImageSets
│ | ...
├── VOCdevkit/VOC2012
│ ├── Annotations
│ ├── 2011_003876.xml
│ │ ...
│ ├── JPEGImages
│ ├── 2011_003876.jpg
│ │ ...
│ ├── ImageSets
│ │ ...
在source/voc.py
中定义并注册了VOCDataSet
数据集,它继承自DetDataSet
基类,并重写了parse_dataset
方法,解析VOC数据集中xml格式标注文件,更新roidbs
和cname2cid
。将其他数据集转换成VOC格式可以参考用户数据转成VOC数据
自定义数据集
如果COCODataSet和VOCDataSet不能满足你的需求,可以通过自定义数据集的方式来加载你的数据集。只需要以下两步即可实现自定义数据集
- 新建
source/xxx.py
,定义类XXXDataSet
继承自DetDataSet
基类,完成注册与序列化,并重写parse_dataset
方法对roidbs
与cname2cid
更新:
from ppdet.core.workspace import register, serializable
#注册并序列化
@register
@serializable
class XXXDataSet(DetDataSet):
def __init__(self,
dataset_dir=None,
image_dir=None,
anno_path=None,
...
):
self.roidbs = None
self.cname2cid = None
...
def parse_dataset(self):
...
省略具体解析数据逻辑
...
self.roidbs, self.cname2cid = records, cname2cid
- 在
source/__init__.py
中添加引用:
from . import xxx
from .xxx import *
完成以上两步就将新的数据源XXXDataSet
添加好了,你可以参考配置及运行实现自定义数据集的使用。
配置文件说明
以ppyolo_r50vd_dcn_1x_coco.yml
为例,这个模型由五个子配置文件组成:
- 数据配置文件
coco_detection.yml
# 数据评估类型
metric: COCO
# 数据集的类别数
num_classes: 80
# TrainDataset
TrainDataset:
!COCODataSet
# 图像数据路径,相对 dataset_dir 路径,os.path.join(dataset_dir, image_dir)
image_dir: train2017
# 标注文件路径,相对 dataset_dir 路径,os.path.join(dataset_dir, anno_path)
anno_path: annotations/instances_train2017.json
# 数据文件夹
dataset_dir: dataset/coco
# data_fields
data_fields: ['image', 'gt_bbox', 'gt_class', 'is_crowd']
EvalDataset:
!COCODataSet
# 图像数据路径,相对 dataset_dir 路径,os.path.join(dataset_dir, image_dir)
image_dir: val2017
# 标注文件路径,相对 dataset_dir 路径,os.path.join(dataset_dir, anno_path)
anno_path: annotations/instances_val2017.json
# 数据文件夹,os.path.join(dataset_dir, anno_path)
dataset_dir: dataset/coco
TestDataset:
!ImageFolder
# 标注文件路径,相对 dataset_dir 路径
anno_path: annotations/instances_val2017.json
- 优化器配置文件
optimizer_1x.yml
# 总训练轮数
epoch: 405
# 学习率设置
LearningRate:
# 默认为8卡训学习率
base_lr: 0.01
# 学习率调整策略
schedulers:
- !PiecewiseDecay
gamma: 0.1
# 学习率变化位置(轮数)
milestones:
- 243
- 324
# Warmup
- !LinearWarmup
start_factor: 0.
steps: 4000
# 优化器
OptimizerBuilder:
# 优化器
optimizer:
momentum: 0.9
type: Momentum
# 正则化
regularizer:
factor: 0.0005
type: L2
- 数据读取配置文件
ppyolo_reader.yml
# 每张GPU reader进程个数
worker_num: 2
# 训练数据
TrainReader:
inputs_def:
num_max_boxes: 50
# 训练数据transforms
sample_transforms:
- Decode: {}
- Mixup: {alpha: 1.5, beta: 1.5}
- RandomDistort: {}
- RandomExpand: {fill_value: [123.675, 116.28, 103.53]}
- RandomCrop: {}
- RandomFlip: {}
# batch_transforms
batch_transforms:
- BatchRandomResize: {target_size: [320, 352, 384, 416, 448, 480, 512, 544, 576, 608], random_size: True, random_interp: True, keep_ratio: False}
- NormalizeBox: {}
- PadBox: {num_max_boxes: 50}
- BboxXYXY2XYWH: {}
- NormalizeImage: {mean: [0.485, 0.456, 0.406], std: [0.229, 0.224, 0.225], is_scale: True}
- Permute: {}
- Gt2YoloTarget: {anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]], anchors: [[10, 13], [16, 30], [33, 23], [30, 61], [62, 45], [59, 119], [116, 90], [156, 198], [373, 326]], downsample_ratios: [32, 16, 8]}
# 训练时batch_size
batch_size: 24
# 读取数据是否乱序
shuffle: true
# 是否丢弃最后不能完整组成batch的数据
drop_last: true
# mixup_epoch,大于最大epoch,表示训练过程一直使用mixup数据增广
mixup_epoch: 25000
# 是否通过共享内存进行数据读取加速,需要保证共享内存大小(如/dev/shm)满足大于1G
use_shared_memory: true
# 评估数据
EvalReader:
# 评估数据transforms
sample_transforms:
- Decode: {}
- Resize: {target_size: [608, 608], keep_ratio: False, interp: 2}
- NormalizeImage: {mean: [0.485, 0.456, 0.406], std: [0.229, 0.224, 0.225], is_scale: True}
- Permute: {}
# 评估时batch_size
batch_size: 8
# 测试数据
TestReader:
inputs_def:
image_shape: [3, 608, 608]
# 测试数据transforms
sample_transforms:
- Decode: {}
- Resize: {target_size: [608, 608], keep_ratio: False, interp: 2}
- NormalizeImage: {mean: [0.485, 0.456, 0.406], std: [0.229, 0.224, 0.225], is_scale: True}
- Permute: {}
# 测试时batch_size
batch_size: 1
- 模型配置文件
ppyolo_r50vd_dcn.yml
# 模型结构类型
architecture: YOLOv3
# 预训练模型地址
pretrain_weights: https://paddledet.bj.bcebos.com/models/pretrained/ResNet50_vd_ssld_pretrained.pdparams
# norm_type
norm_type: sync_bn
# 是否使用ema
use_ema: true
# ema_decay
ema_decay: 0.9998
# YOLOv3
YOLOv3:
# backbone
backbone: ResNet
# neck
neck: PPYOLOFPN
# yolo_head
yolo_head: YOLOv3Head
# post_process
post_process: BBoxPostProcess
# backbone
ResNet:
# depth
depth: 50
# variant
variant: d
# return_idx, 0 represent res2
return_idx: [1, 2, 3]
# dcn_v2_stages
dcn_v2_stages: [3]
# freeze_at
freeze_at: -1
# freeze_norm
freeze_norm: false
# norm_decay
norm_decay: 0.
# PPYOLOFPN
PPYOLOFPN:
# 是否coord_conv
coord_conv: true
# 是否drop_block
drop_block: true
# block_size
block_size: 3
# keep_prob
keep_prob: 0.9
# 是否spp
spp: true
# YOLOv3Head
YOLOv3Head:
# anchors
anchors: [[10, 13], [16, 30], [33, 23],
[30, 61], [62, 45], [59, 119],
[116, 90], [156, 198], [373, 326]]
# anchor_masks
anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
# loss
loss: YOLOv3Loss
# 是否使用iou_aware
iou_aware: true
# iou_aware_factor
iou_aware_factor: 0.4
# YOLOv3Loss
YOLOv3Loss:
# ignore_thresh
ignore_thresh: 0.7
# downsample
downsample: [32, 16, 8]
# 是否label_smooth
label_smooth: false
# scale_x_y
scale_x_y: 1.05
# iou_loss
iou_loss: IouLoss
# iou_aware_loss
iou_aware_loss: IouAwareLoss
# IouLoss
IouLoss:
loss_weight: 2.5
loss_square: true
# IouAwareLoss
IouAwareLoss:
loss_weight: 1.0
# BBoxPostProcess
BBoxPostProcess:
decode:
name: YOLOBox
conf_thresh: 0.01
downsample_ratio: 32
clip_bbox: true
scale_x_y: 1.05
# nms 配置
nms:
name: MatrixNMS
keep_top_k: 100
score_threshold: 0.01
post_threshold: 0.01
nms_top_k: -1
background_label: -1
- 运行时置文件
runtime.yml
# 是否使用gpu
use_gpu: true
# 日志打印间隔
log_iter: 20
# save_dir
save_dir: output
# 模型保存间隔时间
snapshot_epoch: 1
模型开发
新增模型算法
新增网络结构和配置文件,参考新增模型算法
训练
修改配置文件
- 修改预训练模型路径
pretrain_wights
,输出模型路径weights
,评估epoch数snapshot_epoch
- 修改配置文件中的数据配置、类别数
metric: COCO
# 类别数
num_classes: 5
TrainDataset:
!COCODataSet
# 修改为你自己的训练数据目录
image_dir: train_data
# 修改为你自己的训练数据标签文件
anno_path: annotations/instance_train.json
# 修改为你自己的训练数据根目录
dataset_dir: /data/ocr/images/dataset_detection/crop/
data_fields: ['image', 'gt_bbox', 'gt_class', 'is_crowd']
EvalDataset:
!COCODataSet
# 修改为你自己的验证数据目录
image_dir: test_data
# 修改为你自己的验证数据标签文件
anno_path: annotations/instance_val.json
# 修改为你自己的验证数据根目录
dataset_dir: /data/ocr/images/dataset_detection/crop/
TestDataset:
!ImageFolder
# 修改为你自己的测试数据标签文件
anno_path: annotations/instance_val.json
dataset_dir: /data/ocr/images/dataset_detection/crop/
训练
- 单卡训练
# 训练日志会自动保存到 log 目录中
export CUDA_VISIBLE_DEVICES=0 #windows和Mac下不需要执行该命令
python3 tools/train.py \
-c configs/picodet/legacy_model/application/layout_analysis/picodet_lcnet_x1_0_layout.yml \
--eval
- 多卡训练,通过–gpus参数指定卡号
export CUDA_VISIBLE_DEVICES=0,1,2,3 #windows和Mac下不需要执行该命令
python3 -m paddle.distributed.launch --gpus '0,1,2,3' tools/train.py \
-c configs/picodet/legacy_model/application/layout_analysis/picodet_lcnet_x1_0_layout.yml \
--eval
- 多机多卡训练
- 不同机器的ip信息需要用逗号隔开,可以通过
ifconfig
或者ipconfig
查看 - 不同机器之间需要做免密设置,且可以直接ping通,否则无法完成通信。
- 不同机器之间的代码、数据与运行命令或脚本需要保持一致,且所有的机器上都需要运行设置好的训练命令或者脚本。最终
ip_list
中的第一台机器的第一块设备是trainer0,以此类推。 - 不同机器的起始端口可能不同,建议在启动多机任务前,在不同的机器中设置相同的多机运行起始端口,命令为
export FLAGS_START_PORT=17000
,端口值建议在10000~20000
之间。
- 不同机器的ip信息需要用逗号隔开,可以通过
ip_list="10.127.6.17,10.127.5.142,10.127.45.13,10.127.44.151"
fleetrun \
--ips=${ip_list} \
--selected_gpu 0,1,2,3,4,5,6,7 \
tools/train.py -c configs/ppyoloe/ppyoloe_crn_s_300e_coco.yml \
--eval &>logs.txt 2>&1 &
- Fine-tune其他任务
使用预训练模型fine-tune其他任务时,可以直接加载预训练模型,形状不匹配的参数将自动忽略
export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7
# 如果模型中参数形状与加载权重形状不同,将不会加载这类参数
python -m paddle.distributed.launch --gpus 0,1,2,3,4,5,6,7 tools/train.py -c configs/yolov3/yolov3_mobilenet_v1_roadsign.yml -o pretrain_weights=output/model_final
- 模型恢复训练
由于一些原因导致训练中断,用户可以使用-r
的命令恢复训练
export CUDA_VISIBLE_DEVICES=0 #windows和Mac下不需要执行该命令
python tools/train.py -c configs/yolov3/yolov3_mobilenet_v1_roadsign.yml -r output/faster_rcnn_r50_1x_coco/10000
--eval
表示训练的同时,进行评估, 评估过程中默认将最佳模型,保存为 output/picodet_lcnet_x1_0_layout/best_accuracy
- FGD蒸馏训练
PaddleDetection支持了基于FGD(Focal and Global Knowledge Distillation for Detectors)蒸馏的目标检测模型训练过程,FGD蒸馏分为两个部分Focal
和Global
。Focal
蒸馏分离图像的前景和背景,让学生模型分别关注教师模型的前景和背景部分特征的关键像素;Global
蒸馏部分重建不同像素之间的关系并将其从教师转移到学生,以补偿Focal
蒸馏中丢失的全局信息。
更换数据集,修改【TODO】配置中的数据配置、类别数,具体可以参考4.1。启动训练:
# 单卡训练
export CUDA_VISIBLE_DEVICES=0
python3 tools/train.py \
-c configs/picodet/legacy_model/application/layout_analysis/picodet_lcnet_x1_0_layout.yml \
--slim_config configs/picodet/legacy_model/application/layout_analysis/picodet_lcnet_x2_5_layout.yml \
--eval
-c
: 指定模型配置文件。
--slim_config
: 指定压缩策略配置文件。
评估、测试
- 评估
# GPU 评估, weights 为待测权重
python3 tools/eval.py \
-c configs/picodet/legacy_model/application/layout_analysis/picodet_lcnet_x1_0_layout.yml \
-o weights=./output/picodet_lcnet_x1_0_layout/best_model
- 测试
python3 tools/infer.py \
-c configs/picodet/legacy_model/application/layout_analysis/picodet_lcnet_x1_0_layout.yml \
-o weights='output/picodet_lcnet_x1_0_layout/best_model.pdparams' \
--infer_img='docs/images/layout.jpg' \
--output_dir=output_dir/ \
--draw_threshold=0.5
--infer_img
: 推理单张图片,也可以通过--infer_dir
推理文件中的所有图片。--output_dir
: 指定可视化结果保存路径。--draw_threshold
:指定绘制结果框的NMS阈值。
导出、压缩、推理
导出
python3 tools/export_model.py \
-c configs/picodet/legacy_model/application/layout_analysis/picodet_lcnet_x1_0_layout.yml \
-o weights=output/picodet_lcnet_x1_0_layout/best_model \
--output_dir=output_inference/
- 如无需导出后处理,请指定:
-o export.benchmark=True
(如果-o已出现过,此处删掉-o) - 如无需导出NMS,请指定:
-o export.nms=False
模型压缩
为了进一步对模型进行优化,PaddleDetection提供了基于PaddleSlim进行模型压缩的完整教程和benchmark。目前支持的方案:
- 裁剪
- 量化
- 蒸馏
- 联合策略
- 更多关于模型压缩的文档,请参考模型压缩文档。
推理
python3 deploy/python/infer.py \
--model_dir=output_inference/picodet_lcnet_x1_0_layout/ \
--image_file=docs/images/layout.jpg \
--device=GPU
--device
:指定GPU、CPU设备
模型推理完成,会看到以下log输出
------------------------------------------
----------- Model Configuration -----------
Model Arch: PicoDet
Transform Order:
--transform op: Resize
--transform op: NormalizeImage
--transform op: Permute
--transform op: PadStride
--------------------------------------------
class_id:0, confidence:0.9921, left_top:[20.18,35.66],right_bottom:[341.58,600.99]
class_id:0, confidence:0.9914, left_top:[19.77,611.42],right_bottom:[341.48,901.82]
class_id:0, confidence:0.9904, left_top:[369.36,375.10],right_bottom:[691.29,600.59]
class_id:0, confidence:0.9835, left_top:[369.60,608.60],right_bottom:[691.38,736.72]
class_id:0, confidence:0.9830, left_top:[369.58,805.38],right_bottom:[690.97,901.80]
class_id:0, confidence:0.9716, left_top:[383.68,271.44],right_bottom:[688.93,335.39]
class_id:0, confidence:0.9452, left_top:[370.82,34.48],right_bottom:[688.10,63.54]
class_id:1, confidence:0.8712, left_top:[370.84,771.03],right_bottom:[519.30,789.13]
class_id:3, confidence:0.9856, left_top:[371.28,67.85],right_bottom:[685.73,267.72]
save result to: output/layout.jpg
Test iter 0
------------------ Inference Time Info ----------------------
total_time(ms): 2196.0, img_num: 1
average latency time(ms): 2196.00, QPS: 0.455373
preprocess_time(ms): 2172.50, inference_time(ms): 11.90, postprocess_time(ms): 11.60
- Model:模型结构
- Transform Order:预处理操作
- class_id、confidence、left_top、right_bottom:分别表示类别id、置信度、左上角坐标、右下角坐标
- save result to:可视化版面分析结果保存路径,默认保存到./output文件夹
- Inference Time Info:推理时间,其中preprocess_time表示预处理耗时,inference_time表示模型预测耗时,postprocess_time表示后处理耗时
常见问题
往期的FAQ