ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PaddleDetection 模型算法开发实战:以 YOLOv3 为例的组件化建模与配置指南

PaddleDetection 模型算法开发实战:以 YOLOv3 为例的组件化建模与配置指南 PaddleDetection 模型算法开发实战以 YOLOv3 为例的组件化建模与配置指南【免费下载链接】PaddleDetectionObject Detection toolkit based on PaddlePaddle. It supports object detection, instance segmentation, multiple object tracking and real-time multi-person keypoint detection.项目地址: https://gitcode.com/gh_mirrors/pa/PaddleDetectionPaddleDetection 将目标检测模型拆解为 backbone、neck、head、loss、后处理等标准化组件通过注册 配置驱动的方式让开发者无需编写训练循环与数据加载代码即可组装新模型。本文以单阶段检测器 YOLOv3 为主线完整讲解如何在ppdet/modeling下从零创建模型组件、如何通过configs/yolov3下的配置文件串联起网络结构、优化器与数据读取模块并深入源码剖析register、__shared__、__inject__等核心机制帮助你快速构建属于自己的检测算法。1. 总体认识配置驱动的模块化建模PaddleDetection 中每个模型家族对应一个独立目录。以 YOLOv3 为例其通用配置文件位于 configs/yolov3/yolov3_darknet53_270e_coco.yml该文件本身几乎不包含模型实现细节而是通过_BASE_机制引用多个基础配置_BASE_: [ ../datasets/coco_detection.yml, # 所有模型共享的数据集配置文件 ../runtime.yml, # 运行时配置如是否使用 GPU、日志等级等 _base_/optimizer_270e.yml, # 优化器相关配置 _base_/yolov3_darknet53.yml, # YOLOv3 网络结构配置文件 _base_/yolov3_reader.yml, # YOLOv3 Reader数据读取模块配置 ] # 此处定义的配置会覆盖上述文件中同名配置项 snapshot_epoch: 5 weights: output/yolov3_darknet53_270e_coco/model_final可见配置文件中的模块被清晰地划分为优化器optimizer、网络结构architecture和数据读取reader三大类外加公共的数据集配置与运行时配置。PaddleDetection 内部集成了丰富的优化器、学习率调整策略与预处理算子因此大多数场景下你不需要编写优化器与 reader 相关代码只需在配置文件中声明即可——新增一个模型的核心工作就是构建网络结构。所有网络结构都在 ppdet/modeling 目录下以组件形式定义和组合ppdet/modeling/ ├── architectures │ ├── faster_rcnn.py # Faster RCNN 模型 │ ├── ssd.py # SSD 模型 │ ├── yolo.py # YOLOv3 / PP-YOLO 系列模型 │ │ ... ├── heads # 检测头模块 │ ├── xxx_head.py # 定义各种检测头 │ ├── roi_extractor.py # 检测区域ROI特征提取 ├── backbones # 骨干网络模块 │ ├── resnet.py # ResNet 网络 │ ├── mobilenet.py # MobileNet 网络 │ │ ... ├── losses # 损失函数模块 │ ├── xxx_loss.py # 定义并注册各种损失函数 ├── necks # 特征融合模块 │ ├── xxx_fpn.py # 定义各种 FPN 模块 ├── proposal_generator # anchor proposal 生成与匹配模块 │ ├── anchor_generator.py # anchor 生成模块 │ ├── proposal_generator.py # proposal 生成模块 │ ├── target.py # anchor proposal 匹配函数 │ ├── target_layer.py # anchor proposal 匹配函数 ├── tests # 单元测试模块 │ ├── test_xxx.py # 对网络中的算子与模块结构做单元测试 ├── ops.py # 封装各种 PaddlePaddle 目标检测常用算子/组件 ├── layers.py # 封装并注册各种 PaddlePaddle 目标检测公共组件/算子 ├── bbox_utils.py # 封装与边界框box相关的函数 ├── post_process.py # 封装并注册后处理相关模块 └── shape_spec.py # 定义模块输出 shape 的类 ShapeSpec其中 architectures/meta_arch.py 定义了所有检测架构的基类BaseArcharchitectures目录如faster_rcnn.py、yolo.py中的类是最终组成完整模型的顶层架构。下面以 YOLOv3 为例逐步讲解每个组件的创建方法。2. 创建模型结构以 YOLOv3 为例2.1 创建 Backbone骨干网络所有骨干网络代码位于ppdet/modeling/backbones目录下。以 DarkNet 为例创建 ppdet/modeling/backbones/darknet.pyimport paddle.nn as nn from ppdet.core.workspace import register, serializable register serializable class DarkNet(nn.Layer): __shared__ [norm_type] def __init__(self, depth53, return_idx[2, 3, 4], norm_typebn, norm_decay0.): super(DarkNet, self).__init__() # 省略具体网络层构建 def forward(self, inputs): # 省略前向计算逻辑 pass property def out_shape(self): # 省略返回各输出特征图的通道数信息 pass然后在 backbones/init.py 中注册引用from . import darknet from .darknet import *要点说明为了能在 YAML 配置文件中灵活配置网络所有 backbone 节点都需要像上例那样在ppdet.core.workspace中通过register注册同时serializable可以让 backbone 支持序列化。所有 backbone 需继承paddle.nn.Layer并实现forward函数此外还要实现out_shape属性以定义输出特征图的通道信息。__shared__用于实现配置参数的全局共享这类参数可以被所有已注册模块如 backbone、neck、head、loss共享。以norm_type为例在实际源码中 darknet.py 通过__shared__ [norm_type]声明训练配置里的norm_type: sync_bn会被自动注入到 DarkNet 实例。从源码结构看darknet.py 中还实现了ConvBNLayer卷积 BN 激活的组合层与DownSample下采样层等基础构件DarkNet 主体即由这些构件堆叠而成out_shape基于ShapeSpec返回各下采样阶段特征图的通道维度供 neck 的from_config推断输入通道。2.2 创建 Neck特征融合模块特征融合模块位于ppdet/modeling/necks目录下创建 ppdet/modeling/necks/yolo_fpn.pyimport paddle.nn as nn from ppdet.core.workspace import register, serializable register serializable class YOLOv3FPN(nn.Layer): __shared__ [norm_type] def __init__(self, in_channels[256, 512, 1024], norm_typebn): super(YOLOv3FPN, self).__init__() # 省略具体结构 def forward(self, blocks): # 省略前向计算 pass classmethod def from_config(cls, cfg, input_shape): # 省略根据 backbone 输出的 input_shape 推断并初始化 in_channels pass property def out_shape(self): # 省略输出特征图通道信息 pass然后在 necks/init.py 中注册引用from . import yolo_fpn from .yolo_fpn import *要点说明neck 模块同样需要register注册可用serializable支持序列化。neck 需继承paddle.nn.Layer并实现forward同时需实现out_shape属性定义输出特征图通道以及类方法from_config用于从配置文件与上一级模块backbone的输出形状推断输入通道并初始化实例。这是组件自动对接通道数的关键。从源码看yolo_fpn.py 中的YoloDetBlock是 YOLOv3 FPN 的核心单元包含 5 个卷积块 1 个 tip 卷积forward对 backbone 输出的多尺度特征逐级做上采样融合后分别输出给 headout_shape返回ShapeSpec列表。2.3 创建 Head检测头head 模块存放在ppdet/modeling/heads目录下创建 ppdet/modeling/heads/yolo_head.pyimport paddle.nn as nn from ppdet.core.workspace import register register class YOLOv3Head(nn.Layer): __shared__ [num_classes] __inject__ [loss] def __init__(self, anchors[[10, 13], [16, 30], [33, 23], [30, 61], [62, 45], [59, 119], [116, 90], [156, 198], [373, 326]], anchor_masks[[6, 7, 8], [3, 4, 5], [0, 1, 2]], num_classes80, lossYOLOv3Loss, iou_awareFalse, iou_aware_factor0.4): super(YOLOv3Head, self).__init__() # 省略具体结构 def forward(self, feats, targetsNone): # 省略前向计算 pass然后在 heads/init.py 中注册引用from . import yolo_head from .yolo_head import *要点说明head 模块需要register注册并继承paddle.nn.Layer、实现forward。__inject__表示该参数会从全局已注册模块字典中导入例如这里的lossYOLOv3Loss会被解析为YOLOv3Loss类的实例注入。从源码看yolo_head.py 中YOLOv3Head会根据anchor_masks将 9 组 anchors 划分为 3 组对应 3 个下采样尺度的输出并为每个尺度创建一个 1x1 卷积输出通道数为len(anchors[i]) * (num_classes 5)启用iou_aware时为num_classes 6训练时直接调用注入的self.loss(yolo_outputs, targets, self.anchors)计算损失推理时输出原始预测供后处理解码。2.4 创建 Loss损失函数损失模块存放于ppdet/modeling/losses目录下创建 ppdet/modeling/losses/yolo_loss.pyimport paddle.nn as nn from ppdet.core.workspace import register register class YOLOv3Loss(nn.Layer): __inject__ [iou_loss, iou_aware_loss] __shared__ [num_classes] def __init__(self, num_classes80, ignore_thresh0.7, label_smoothFalse, downsample[32, 16, 8], scale_x_y1., iou_lossNone, iou_aware_lossNone): super(YOLOv3Loss, self).__init__() # 省略具体实现 def forward(self, inputs, targets, anchors): # 省略前向计算 pass然后在 losses/init.py 中注册引用from . import yolo_loss from .yolo_loss import *要点说明loss 模块需要register注册并继承paddle.nn.Layer、实现forward。__inject__可用于引入已在全局字典中封装的子模块如iou_loss、iou_aware_loss__shared__可用于全局共享参数如num_classes。从源码看yolo_loss.py 中的YOLOv3Loss通过obj_loss、cls_loss、yolov3_loss组合实现目标损失、分类损失与定位损失ignore_thresh控制负样本的忽略阈值预测框与任一 GT 的 IoU 小于该值才作为负样本参与置信度损失label_smooth开启标签平滑downsample[32, 16, 8]对应三个检测尺度的下采样倍数。2.5 创建后处理模块Post-processing后处理模块定义在 ppdet/modeling/post_process.py 中其中BBoxPostProcess类负责解码与 NMS 等后处理操作from ppdet.core.workspace import register register class BBoxPostProcess(object): __shared__ [num_classes] __inject__ [decode, nms] def __init__(self, num_classes80, decodeNone, nmsNone): # 省略具体实现 pass def __call__(self, head_out, rois, im_shape, scale_factor): # 省略具体实现 pass要点说明后处理模块需要register注册。__inject__引入全局字典中封装的模块如decode解码如YOLOBox与nms如MultiClassNMS二者均定义在 ppdet/modeling/layers.py见第 511 行MultiClassNMS、第 620 行YOLOBox。从源码看post_process.py 中BBoxPostProcess.__call__先调用decode(head_out, rois, im_shape, scale_factor)将 head 的原始输出解码为边界框与置信度再调用nms(bboxes, score, num_classes)做多类别 NMS最终返回[N, 6]的预测结果类别、得分、坐标与每张图的框数量bbox_numexport_onnx开启时还会附加一个假框以兼容 ONNX 导出。2.6 创建架构Architecture所有完整架构代码位于ppdet/modeling/architectures目录。meta_arch.py 定义了BaseArch基类import paddle.nn as nn from ppdet.core.workspace import register register class BaseArch(nn.Layer): def __init__(self): super(BaseArch, self).__init__() def forward(self, inputs): self.inputs inputs self.model_arch() if self.training: out self.get_loss() else: out self.get_pred() return out def model_arch(self, ): pass def get_loss(self, ): raise NotImplementedError(Should implement get_loss method!) def get_pred(self, ): raise NotImplementedError(Should implement get_pred method!)所有架构都必须继承BaseArch。architectures/yolo.py 中YOLOv3的定义如下register class YOLOv3(BaseArch): __category__ architecture __inject__ [post_process] def __init__(self, backboneDarkNet, neckYOLOv3FPN, yolo_headYOLOv3Head, post_processBBoxPostProcess): super(YOLOv3, self).__init__() self.backbone backbone self.neck neck self.yolo_head yolo_head self.post_process post_process classmethod def from_config(cls, cfg, *args, **kwargs): # 省略按 backbone - neck - head 顺序逐级创建子模块 pass def get_loss(self): # 省略 pass def get_pred(self): # 省略 pass要点说明所有架构都需要用register注册。组装完整网络时必须设置__category__ architecture以标识这是一个完整的目标检测模型。backbone、neck、head、post_process 等检测组件作为字符串传入架构由create工厂根据配置实例化后组合成最终网络。这种组件化设计极大提升了复用性通过替换不同组件即可组合出多个模型例如把 DarkNet 换成 MobileNet。from_config类方法实现了模块组合时的通道自动配置。从 yolo.py 的源码可见其调用链backbone create(cfg[backbone])→ 以backbone.out_shape作为input_shape创建 neck → 再以neck.out_shape创建 yolo_head各组件通道数由此自动衔接。实际源码中YOLOv3.__init__还支持data_formatNCHW/NHWC与for_mot是否输出特征供多目标跟踪模型使用等参数get_loss与get_pred统一走_forward训练时yolo_head(neck_feats, self.inputs)返回各损失推理时通过post_process(yolo_head_outs, mask_anchors, im_shape, scale_factor)输出{bbox, bbox_num}。值得一提的是architectures/yolo.py头部注释明确指出YOLOv3、PP-YOLO、PP-YOLOv2、PP-YOLOE、PP-YOLOE 共用同一套 YOLOv3 架构PP-YOLOE 系列建议使用ppyoloe.py中的 PPYOLOE 架构尤其在蒸馏或 aux head 场景下。这意味着基于本教程创建的组件也能通过替换 head、neck 快速向 PP-YOLO 等进阶模型演进。3. 创建配置文件3.1 网络结构配置文件YOLOv3 的网络结构配置位于configs/yolov3/_base_/文件夹。以 configs/yolov3/base/yolov3_darknet53.yml 为例architecture: YOLOv3 pretrain_weights: https://paddledet.bj.bcebos.com/models/pretrained/DarkNet53_pretrained.pdparams norm_type: sync_bn YOLOv3: backbone: DarkNet neck: YOLOv3FPN yolo_head: YOLOv3Head post_process: BBoxPostProcess DarkNet: depth: 53 return_idx: [2, 3, 4] # 使用默认配置时无需显式声明 # YOLOv3FPN: YOLOv3Head: anchors: [[10, 13], [16, 30], [33, 23], [30, 61], [62, 45], [59, 119], [116, 90], [156, 198], [373, 326]] anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] loss: YOLOv3Loss YOLOv3Loss: ignore_thresh: 0.7 downsample: [32, 16, 8] label_smooth: false BBoxPostProcess: decode: name: YOLOBox conf_thresh: 0.005 downsample_ratio: 32 clip_bbox: true nms: name: MultiClassNMS keep_top_k: 100 score_threshold: 0.01 nms_threshold: 0.45 nms_top_k: 1000配置文件中的关键约定顶层字段architecture指定架构类名pretrain_weights指定预训练权重的 URL 或路径norm_type作为全局参数被各组件通过__shared__共享。自顶向下声明文件从上到下依次对应上一节中的各模型组件架构 → backbone → neck → head → loss → 后处理每个小节键名与注册的类名一一对应。可省略默认配置如果某组件完全使用默认参数可以不在配置中声明如上述YOLOv3FPNcreate会按默认值实例化。通过改配置换模型只需修改配置即可组合出不同模型例如 configs/yolov3/base/yolov3_mobilenet_v1.yml 将 backbone 从 DarkNet 切换为 MobileNet v1。这套配置名 注册类名的约定之所以能成立是因为 ppdet/core/workspace.py 中的register会把类名写入全局配置字典workspace.py而create则按名字从该字典取出配置并实例化workspace.py。实例化过程中依次处理shared全局共享参数注入、from_config通道自动推导与inject子模块注入这正是只写配置、不用写组装代码的底层原理。3.2 优化器配置文件优化器配置定义模型使用的优化器与学习率调度策略。PaddleDetection 已集成多种优化器与学习率策略实现在 ppdet/optimizer.py。YOLOv3 的优化器配置见 configs/yolov3/base/optimizer_270e.ymlepoch: 270 LearningRate: base_lr: 0.001 schedulers: - !PiecewiseDecay gamma: 0.1 milestones: # 以 epoch 为单位 - 216 - 243 - !LinearWarmup start_factor: 0. steps: 4000 OptimizerBuilder: optimizer: momentum: 0.9 type: Momentum regularizer: factor: 0.0005 type: L2要点说明OptimizerBuilder.optimizer指定优化器类型与参数。以 Momentum 为例momentum: 0.9为动量系数当前支持的全部优化器类型可查阅 PaddlePaddle 官方优化器文档。LearningRate.schedulers设置不同学习率调整策略的组合这里先线性预热LinearWarmup前 4000 步从start_factor0.线性升至base_lr再按PiecewiseDecay在 epoch 216、243 处以gamma: 0.1阶梯式衰减。需要特别注意的是PaddlePaddle 原生的学习率调整策略需要做一层简单封装才能在此处使用相关实现见源码 ppdet/optimizer.py。regularizer指定权重正则化factor: 0.0005、type: L2表示 L2 权重衰减系数为 5e-4这也是目标检测训练中常用的设置。3.3 Reader 配置文件Reader 配置负责数据读取与预处理流水线。完整说明请参阅 Reader 配置文档这里给出 YOLOv3 的 Reader 配置 configs/yolov3/base/yolov3_reader.yml 的骨架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要点说明TrainReader/EvalReader/TestReader分别定义训练、评估、测试阶段的数据流水线可在其中配置不同的预处理算子sample_transforms逐样本算子、batch_transforms批级算子、每张 GPU 的batch_size、DataLoader 的worker_num等。训练时shuffle: true、drop_last: true并开启共享内存use_shared_memory加速数据读取TestReader.inputs_def.image_shape: [3, 608, 608]指定推理输入尺寸。数据集本身的配置如 COCO 的TrainDataset/EvalDataset/TestDataset位于 configs/datasets 下通过!COCODataSet等!语法直接序列化模块实例详见 READER_en.md。在训练、评估、测试运行过程中Reader 迭代器由 ppdet/engine/trainer.py 创建通过ppdet.core.workspace.create构建数据加载器。4. 从配置到模型的调用链总结读完本文你已经掌握了 PaddleDetection 建模的两条主线代码侧在ppdet/modeling下按backbones → necks → heads → losses → post_process → architectures的顺序创建组件类全部以register必要时叠加serializable注册并通过各目录__init__.py的from . import xxx; from .xxx import *引入架构类继承BaseArch并设置__category__ architecture。配置侧在configs/xxx/_base_/下编写网络结构配置顶层声明architecture与各组件参数、优化器配置epoch、LearningRate.schedulers、OptimizerBuilder与 Reader 配置最后用_BASE_引用数据集与 runtime 配置组装出完整训练配置。register让类名进入全局注册表create负责按配置实例化__shared__实现全局参数共享__inject__实现子模块注入from_config实现组件间通道自动对接——这五个机制构成了 PaddleDetection配置驱动、组件化组装的完整闭环。结合源码阅读重点可看 workspace.py、meta_arch.py 与 yolo.py你将能更透彻地理解每一处配置的底层含义并在此基础上快速定制属于自己的检测算法。【免费下载链接】PaddleDetectionObject Detection toolkit based on PaddlePaddle. It supports object detection, instance segmentation, multiple object tracking and real-time multi-person keypoint detection.项目地址: https://gitcode.com/gh_mirrors/pa/PaddleDetection创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表