PaddleDetection进阶教程3#模型参数配置

师向文
2023-12-01

1. 配置模块设计与介绍

1.1 简介

为了使配置过程更加自动化并减少配置错误,PaddleDetection的配置管理采取了较为严谨的设计。

1.2 设计思想

目前主流框架全局配置基本是一个Python dict,这种设计对配置的检查并不严格,拼写错误或者遗漏的配置项往往会造成训练过程中的严重错误,进而造成时间及资源的浪费。为了避免这些陷阱,从自动化和静态分析的原则出发,PaddleDetection采用了一种用户友好、 易于维护和扩展的配置设计。

1.3 基本设计

利用Python的反射机制,PaddleDection的配置系统从Python类的构造函数抽取多种信息 - 如参数名、初始值、参数注释、数据类型(如果给出type hint)- 来作为配置规则。 这种设计便于设计的模块化,提升可测试性及扩展性。

1.3.1 API

配置系统的大多数功能由 ppdet.core.workspace 模块提供

  • register: 装饰器,将类注册为可配置模块;能够识别类定义中的一些特殊标注。
    • __category__: 为便于组织,模块可以分为不同类别。
    • __inject__: 如果模块由多个子模块组成,可以这些子模块实例作为构造函数的参数注入。对应的默认值及配置项可以是类名字符串,yaml序列化的对象,指向序列化对象的配置键值或者Python dict(构造函数需要对其作出处理,参见下面的例子)。
    • __op__: 配合 __append_doc__ (抽取目标OP的 注释)使用,可以方便快速的封装PaddlePaddle底层OP。
  • serializable: 装饰器,利用 pyyaml 的序列化机制,可以直接将一个类实例序列化及反序列化。
  • create: 根据全局配置构造一个模块实例。
  • load_config and merge_config: 加载yaml文件,合并命令行提供的配置项。

1.3.2 示例

RPNHead 模块为例,该模块包含多个PaddlePaddle OP,先将这些OP封装成类,并将其实例在构造 RPNHead 时注入。

# excerpt from `ppdet/modeling/ops.py`
from ppdet.core.workspace import register, serializable

# ... more operators

@register
@serializable
class GenerateProposals(object):
    # NOTE this class simply wraps a PaddlePaddle operator
    __op__ = fluid.layers.generate_proposals
    # NOTE docstring for args are extracted from PaddlePaddle OP
    __append_doc__ = True

    def __init__(self,
                 pre_nms_top_n=6000,
                 post_nms_top_n=1000,
                 nms_thresh=.5,
                 min_size=.1,
                 eta=1.):
        super(GenerateProposals, self).__init__()
        self.pre_nms_top_n = pre_nms_top_n
        self.post_nms_top_n = post_nms_top_n
        self.nms_thresh = nms_thresh
        self.min_size = min_size
        self.eta = eta

# ... more operators

# excerpt from `ppdet/modeling/anchor_heads/rpn_head.py`
from ppdet.core.workspace import register
from ppdet.modeling.ops import AnchorGenerator, RPNTargetAssign, GenerateProposals

@register
class RPNHead(object):
    """
    RPN Head

    Args:
        anchor_generator (object): `AnchorGenerator` instance
        rpn_target_assign (object): `RPNTargetAssign` instance
        train_proposal (object): `GenerateProposals` instance for training
        test_proposal (object): `GenerateProposals` instance for testing
    """
    __inject__ = [
        'anchor_generator', 'rpn_target_assign', 'train_proposal',
        'test_proposal'
    ]

    def __init__(self,
                 anchor_generator=AnchorGenerator().__dict__,
                 rpn_target_assign=RPNTargetAssign().__dict__,
                 train_proposal=GenerateProposals(12000, 2000).__dict__,
                 test_proposal=GenerateProposals().__dict__):
        super(RPNHead, self).__init__()
        self.anchor_generator = anchor_generator
        self.rpn_target_assign = rpn_target_assign
        self.train_proposal = train_proposal
        self.test_proposal = test_proposal
        if isinstance(anchor_generator, dict):
            self.anchor_generator = AnchorGenerator(**anchor_generator)
        if isinstance(rpn_target_assign, dict):
            self.rpn_target_assign = RPNTargetAssign(**rpn_target_assign)
        if isinstance(train_proposal, dict):
            self.train_proposal = GenerateProposals(**train_proposal)
        if isinstance(test_proposal, dict):
            self.test_proposal = GenerateProposals(**test_proposal)

对应的yaml配置如下,请注意这里给出的是 完整 配置,其中所有默认值配置项都可以省略。上面的例子中的模块所有的构造函数参数都提供了默认值,因此配置文件中可以完全略过其配置。

RPNHead:
  test_proposal:
    eta: 1.0
    min_size: 0.1
    nms_thresh: 0.5
    post_nms_top_n: 1000
    pre_nms_top_n: 6000
  train_proposal:
    eta: 1.0
    min_size: 0.1
    nms_thresh: 0.5
    post_nms_top_n: 2000
    pre_nms_top_n: 12000
  anchor_generator:
    # ...
  rpn_target_assign:
    # ...

RPNHead 模块实际使用代码示例。

from ppdet.core.workspace import load_config, merge_config, create

load_config('some_config_file.yml')
merge_config(more_config_options_from_command_line)

rpn_head = create('RPNHead')
# ... code that use the created module!

配置文件用可以直接序列化模块实例,用 ! 标示,如

LearningRate:
  base_lr: 0.01
  schedulers:
  - !PiecewiseDecay
    gamma: 0.1
    milestones: [60000, 80000]
  - !LinearWarmup
    start_factor: 0.3333333333333333
    steps: 500

1.4 安装依赖

配置系统用到两个Python包,均为可选安装。

如需安装,运行下面命令即可。

pip install typeguard http://github.com/willthefrog/docstring_parser/tarball/master

1.5 相关工具

为了方便用户配置,PaddleDection提供了一个工具 (tools/configure.py), 共支持四个子命令:

  1. list: 列出当前已注册的模块,如需列出具体类别的模块,可以使用 --category 指定。

  2. help: 显示指定模块的帮助信息,如描述,配置项,配置文件模板及命令行示例。

  3. analyze: 检查配置文件中的缺少或者多余的配置项以及依赖缺失,如果给出type hint, 还可以检查配置项中错误的数据类型。非默认配置也会高亮显示。

  4. generate: 根据给出的模块列表生成配置文件,默认生成完整配置,如果指定 --minimal ,生成最小配置,即省略所有默认配置项。例如,执行下列命令可以生成Faster R-CNN (ResNet backbone + FPN) 架构的配置文件:

   python tools/configure.py generate FasterRCNN ResNet RPNHead RoIAlign BBoxAssigner BBoxHead LearningRate OptimizerBuilder

如需最小配置,运行:

python tools/configure.py generate --minimal FasterRCNN BBoxHead

2. RCNN系列模型参数配置教程

标签: 模型参数配置


#####################################基础配置#####################################

# 检测模型的名称
architecture: MaskRCNN

# 默认使用GPU运行,设为False时使用CPU运行
use_gpu: true
# 最大迭代次数,而一个iter会运行batch_size * device_num张图片
# 一般batch_size为1时,1x迭代18万次,2x迭代36万次
max_iters: 180000
# 模型保存间隔,如果训练时eval设置为True,会在保存后进行验证
snapshot_iter: 10000
# 输出指定区间的平均结果,默认20,即输出20次的平均结果
log_smooth_window: 20
# 默认打印log的间隔,默认20
log_iter: 20
# 训练权重的保存路径
save_dir: output
# 模型的预训练权重,默认是从指定url下载
pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar
# 验证模型的评测标准,可以选择COCO或者VOC
metric: COCO
# 用于模型验证或测试的训练好的权重
weights: output/mask_rcnn_r50_fpn_1x/model_final/
# 用于训练或验证的数据集的类别数目
# **其中包含背景类,即81=80 + 1(背景类)**
num_classes: 81

#####################################模型配置#####################################

# Mask RCNN元结构,包括了以下主要组件, 具体细节可以参考[论文]( https://arxiv.org/abs/1703.06870)
MaskRCNN:
  backbone: ResNet
  fpn: FPN
  roi_extractor: FPNRoIAlign
  rpn_head: FPNRPNHead
  bbox_assigner: BBoxAssigner
  bbox_head: BBoxHead
  mask_assigner: MaskAssigner
  mask_head: MaskHead
  rpn_only: false

# 主干网络
ResNet:
  # 配置在哪些阶段加入可变性卷积,默认不添加
  dcn_v2_stages: []
  # ResNet深度,默认50
  depth: 50
  # 主干网络返回的主要阶段特征用于FPN作进一步的特征融合
  # 默认从[2,3,4,5]返回特征
  feature_maps:
    - 2
    - 3
    - 4
    - 5
  # 是否在训练中固定norm layer的权重,默认从第2阶段开始固定
  freeze_at: 2
  # 是否停止norm layer的梯度回传,默认是
  freeze_norm: true
  # norm layer的权重衰退值
  norm_decay: 0.0
  # norm layer的类型, 可以选择bn/sync_bn/affine_channel, 默认为affine_channel
  norm_type: affine_channel
  # ResNet模型的类型, 分为'a', 'b', 'c', 'd'四种, 默认使用'b'类型
  variant: b

# FPN多特征融合
FPN:
  # FPN使用的最高层特征后是否添加额外conv,默认false
  has_extra_convs: false
  # FPN使用主干网络最高层特征,默认是resnet第5阶段后添加额外卷积操作变<成了FPN的第6个,总共有5个阶段
  max_level: 6
  # FPN使用主干网络最低层特征,默认是resnet第2阶段的输出
  min_level: 2
  # FPN中使用Norm类型, bn/sync_bn/affine_channel/null, 默认不用null
  norm_type: null
  # FPN输出特征的通道数量, 默认是256
  num_chan: 256
  # 特征图缩放比例, 默认是[0.03125, 0.0625, 0.125, 0.25]
  spatial_scale:
    - 0.03125
    - 0.0625
    - 0.125
    - 0.25

# 检测第一阶段RPN
FPNRPNHead:
  # FPN第一层特征生成anchor时,默认anchor尺寸32
  anchor_start_size: 32
  # RPNHead默认进行前背景二分类
  num_classes: 1
  # 根据特征图尺寸,在特征图的每个位置生成N个大小、长宽比各不同anchor
  # N = anchor_sizes * aspect_ratios
  # 具体实现参考[API](fluid.layers.anchor_generator)
  anchor_generator:
    aspect_ratios:
      - 0.5
      - 1.0
      - 2.0
    variance:
      - 1.0
      - 1.0
      - 1.0
      - 1.0
  # 首先计算Anchor和GT BBox之间的IoU,为每个Anchor匹配上GT,
  # 然后根据阈值过滤掉IoU低的Anchor,得到最终的Anchor及其GT进行loss计算
  # 具体实现参考[API](fluid.layers.rpn_target_assign)
  rpn_target_assign:
    rpn_batch_size_per_im: 256
    rpn_fg_fraction: 0.5
    rpn_negative_overlap: 0.3
    rpn_positive_overlap: 0.7
    rpn_straddle_thresh: 0.0
  # 首先取topk个分类分数高的anchor
  # 然后通过NMS对这topk个anchor进行重叠度检测,对重叠高的两个anchor只保留得分高的
  # 训练和测试阶段主要区别在最后NMS保留的Anchor数目
  # 训练时输出2000个proposals,推理时输出1000个proposals
  # 具体实现参考[API](fluid.layers.generate_proposals)
  train_proposal:
    min_size: 0.0
    nms_thresh: 0.7
    post_nms_top_n: 2000
    pre_nms_top_n: 2000
  test_proposal:
    min_size: 0.0
    nms_thresh: 0.7
    post_nms_top_n: 1000
    pre_nms_top_n: 1000

# 对FPN每层执行RoIAlign后,然后合并输出结果,用于BBox Head计算
FPNRoIAlign:
  # 用于抽取特征特征的FPN的层数,默认为4
  canconical_level: 4
  # 用于抽取特征特征的FPN的特征图尺寸,默认为224
  canonical_size: 224
  # 用于抽取特征特征的最高层FPN,默认是2
  max_level: 5
  # 用于抽取特征特征的最底层FPN,默认是2
  min_level: 2
  #roi extractor的采样率,默认为2
  sampling_ratio: 2
  # 输出bbox的特征图尺寸,默认为7
  box_resolution: 7
  # 输出mask的特征图尺寸,默认为14
  mask_resolution: 14

# 输出实例掩码的Head
MaskHead:
  # 卷积的数量,FPN是4,其他为0,默认为0
  num_convs: 4
  # mask head输出的特征图尺寸,默认14
  resolution: 28
  # 空洞率,默认为1
  dilation: 1
  # 第一个卷积后输出的特征图通道数, 默认为256
  num_chan_reduced: 256
  # 输出的mask的类别,默认为81
  num_classes: 81

# 求rpn生成的roi跟gt bbox之间的iou,然后根据阈值进行过滤,保留一定数量的roi
# 再根据gt bbox的标签,对roi进行标签赋值,即得到每个roi的类别
# 具体实现参考[API](fluid.layers.generate_proposal_labels)
BBoxAssigner:
  batch_size_per_im: 512
  bg_thresh_hi: 0.5
  bg_thresh_lo: 0.0
  fg_fraction: 0.25
  fg_thresh: 0.5
  num_classes: 81
  shuffle_before_sample: true
  bbox_reg_weights:
    - 0.1
    - 0.1
    - 0.2
    - 0.2

# 根据roi的label,选择前景,为其赋值mask label
# 具体实现参考[API](fluid.layers.generate_mask_labels)
MaskAssigner:
  resolution: 28
  num_classes: 81

# 输出检测框的Head
BBoxHead:
  # 在roi extractor和bbox head之间,插入两层FC,对特征做进一步学习
  head: TwoFCHead
  # 通过NMS进行bbox过滤
  # 具体实现参考[API](fluid.layers.multiclass_nms)
  keep_top_k: 100
  nms_threshold: 0.5
  score_threshold: 0.05
  num_classes: 81
  # 对bbox的坐标进行编解码操作
  # 具体实现参考[API](fluid.layers.box_coder)
  box_coder:
    axis: 1
    box_normalized: false
    code_type: decode_center_size
    prior_box_var:
      - 0.1
      - 0.1
      - 0.2
      - 0.2

# 输出检测框之前,对特征进一步学习
TwoFCHead:
  # FC输出的特征图通道数,默认是1024
  num_chan: 1024

#####################################训练配置#####################################

# 学习率配置
LearningRate:
  # 初始学习率, 一般情况下8卡gpu,batch size为2时设置为0.02
  # 可以根据具体情况,按比例调整
  # 比如说4卡V100,bs=2时,设置为0.01
  base_lr: 0.01
  # 学习率规划器
  # 具体实现参考[API](fluid.layers.piecewise_decay)
  schedulers:
    #学习率衰减策略
    - !PiecewiseDecay
      gamma: 0.1
      milestones:
        - 120000
        - 160000
    # 在训练开始时,调低学习率为base_lr * start_factor,然后逐步增长到base_lr,这个过程叫学习率热身,按照以下公式更新学习率
    # linear_step = end_lr - start_lr
    # lr = start_lr + linear_step * (global_step / warmup_steps)
    # 具体实现参考[API](fluid.layers.linear_lr_warmup)
    - !LinearWarmup
      start_factor: 0.3333333333333333
      steps: 500

OptimizerBuilder:
  # 默认使用SGD+Momentum进行训练
  # 具体实现参考[API](fluid.optimizer)
  optimizer:
    momentum: 0.9
    type: Momentum
  # 默认使用L2权重衰减正则化
  # 具体实现参考[API](fluid.regularizer)
  regularizer:
    factor: 0.0001
    type: L2

#####################################数据配置#####################################

# 模型训练集设置参考
# 训练、验证、测试使用的数据配置主要区别在数据路径、模型输入、数据增强参数设置
TrainReader:
  # 1个GPU的batch size,默认为1
  batch_size: 1
  # 数据集目录配置
  dataset:
    # 数据集根目录
    dataset_dir: dataset/coco
    # 标记文件所在目录
    annotation: annotations/instances_train2017.json
    # 训练图片所在目录
    image_dir: train2017
  # 训练过程中模型的相关输入
  # 包括图片,图片长宽高等基本信息,图片id, 标记的目标框、实例标签、实例分割掩码
  fields:
    - image
    - im_info
    - im_id
    - gt_box
    - gt_label
    - is_crowd
    - gt_mask
  # 输入Image的尺寸
  image_shape:
    - 3
    - 800
    - 1333
  # 对一个batch中的单张图片做的数据增强
  sample_transforms:
    # 读取Image图像为numpy数组
    # 可以选择将图片从BGR转到RGB,可以选择对一个batch中的图片做mixup增强
    - !DecodeImage
      to_rgb: true  # default: true
      with_mixup: false  # default: false
    # 对图片进行随机翻转
    # 可以选择同步翻转mask,可以选择归一化bbox的坐标
    - !RandomFlipImage
      is_mask_flip: true  # default: false
      is_normalized: false  # default: false
      prob: 0.5  # default: 0.5
    # 归一化图片,默认均值[0.485, 0.456, 0.406],方差[1, 1, 1]
    # 可以选择将归一化结果除以255,可以选择图片的数据格式
    - !NormalizeImage
      is_channel_first: false
      is_scale: true
      mean:
        - 0.485
        - 0.456
        - 0.406
      std:
        - 0.229
        - 0.224
        - 0.225
    # 调整图片尺寸,默认采用cv2的线性插值
    - !ResizeImage
      interp: 1
      max_size: 1333
      target_size: 800
      use_cv2: true  # default: true
    # 调整图片数据格式,默认使用CHW
    - !Permute
       channel_first: true
      to_bgr: false  # default: true
  # 对一个batch中的图片统一做的数据增强
  batch_transforms:
    # 将一个batch中的图片,按照最大的尺寸,做补齐
    - !PadBatch
      pad_to_stride: 32  # default: 32
      # 选择是否使用padding之后的image信息,默认为true
      use_padded_im_info: true
  # 使用多进程/线程的数目,默认为2
  worker_num: 2
  # 选择是否使用多进程,默认为false
  use_process: false
  # 使用数据集中的样本数目,默认是-1,表示使用全部
  samples: -1
  # 选择是否打乱所有样本的顺序
  shuffle: true
  # 如果最后一个batch的图片数量为奇数,选择是否丢掉这个batch,不进行训练,默认是不丢掉的
  drop_last: false
 类似资料: