LazyConfig 配置介绍

Lazy Configs

传统的基于 yacs 的配置系统提供了基本的标准功能。 但是,它并没有为许多新项目提供足够的灵活性。 我们开发了一种替代的、非侵入式的配置系统,可以与detectron2 或任何其他复杂的项目一起使用。

Python Syntax

我们的配置对象仍然是字典。 我们没有使用 Yaml 来定义字典,而是直接在 Python 中创建字典。 这为用户提供了 Yaml 中不存在的以下功能:

  1. 使用 Python 轻松操作字典(添加和删除)。
  2. 编写简单的算术或调用简单的函数。
  3. 使用更多数据类型
  4. 使用熟悉的 Python 导入语法导入/组合其他配置文件。

可以像这样加载 Python 配置文件:

# config.py:
a = dict(x=1, y=2, z=dict(xx=1))
b = dict(x=3, y=4)

# my_code.py:
from detectron2.config import LazyConfig
cfg = LazyConfig.load("path/to/config.py")  # an omegaconf dictionary
assert cfg.a.z.xx == 1

在 LazyConfig.load 之后,cfg 将是一个字典,其中包含在配置文件的全局范围内定义的所有字典。 请注意:

  1. 在加载过程中,所有字典都会转换为 omegaconf 配置对象。 这允许访问 omegaconf 功能,例如其访问语法和插值。
  2. config.py 中的绝对导入与常规 Python 中的工作方式相同。
  3. 相对导入只能从配置文件中导入字典。 它们只是 LazyConfig.load_rel 的语法糖。 他们可以在不需要 init.py 的情况下以相对路径加载 Python 文件。

LazyConfig.save 可以将配置对象保存到 yaml。 请注意,如果配置文件中出现不可序列化的对象(例如 lambdas),这并不总是成功的。 是否牺牲储蓄能力以换取灵活性取决于用户。

Recursive Instantiation

LazyConfig 系统大量使用递归实例化,这是一种使用字典来描述对函数/类的调用的模式。 字典包括:

  1. 一个“target”键,其中包含可调用的路径,例如“module.submodule.class_name”。
  2. 表示要传递给可调用对象的参数的其他键。 参数本身可以使用递归实例化来定义。

我们提供了一个帮助函数 LazyCall 来帮助创建这样的字典。 以下代码使用 LazyCall

from detectron2.config import LazyCall as L
from my_app import Trainer, Optimizer
cfg = L(Trainer)(
  optimizer=L(Optimizer)(
    lr=0.01,
    algo="SGD"
  )
)

创建一个像这样的字典:

cfg = {
  "_target_": "my_app.Trainer",
  "optimizer": {
    "_target_": "my_app.Optimizer",
    "lr": 0.01, "algo": "SGD"
  }
}

通过使用这样的字典来表示对象,一个通用的实例化函数可以将它们变成实际的对象,即:

from detectron2.config import instantiate
trainer = instantiate(cfg)
# equivalent to:
# from my_app import Trainer, Optimizer
# trainer = Trainer(optimizer=Optimizer(lr=0.01, algo="SGD"))

这种模式足以描述非常复杂的对象,例如:

from detectron2.config import LazyCall as L
from detectron2.layers import ShapeSpec
from detectron2.modeling.meta_arch import GeneralizedRCNN
from detectron2.modeling.anchor_generator import DefaultAnchorGenerator
from detectron2.modeling.backbone.fpn import LastLevelMaxPool
from detectron2.modeling.backbone import BasicStem, FPN, ResNet
from detectron2.modeling.box_regression import Box2BoxTransform
from detectron2.modeling.matcher import Matcher
from detectron2.modeling.poolers import ROIPooler
from detectron2.modeling.proposal_generator import RPN, StandardRPNHead
from detectron2.modeling.roi_heads import (
    StandardROIHeads,
    FastRCNNOutputLayers,
    MaskRCNNConvUpsampleHead,
    FastRCNNConvFCHead,
)

from ..data.constants import constants

model = L(GeneralizedRCNN)(
    backbone=L(FPN)(
        bottom_up=L(ResNet)(
            stem=L(BasicStem)(in_channels=3, out_channels=64, norm="FrozenBN"),
            stages=L(ResNet.make_default_stages)(
                depth=50,
                stride_in_1x1=True,
                norm="FrozenBN",
            ),
            out_features=["res2", "res3", "res4", "res5"],
        ),
        in_features="${.bottom_up.out_features}",
        out_channels=256,
        top_block=L(LastLevelMaxPool)(),
    ),
    proposal_generator=L(RPN)(
        in_features=["p2", "p3", "p4", "p5", "p6"],
        head=L(StandardRPNHead)(in_channels=256, num_anchors=3),
        anchor_generator=L(DefaultAnchorGenerator)(
            sizes=[[32], [64], [128], [256], [512]],
            aspect_ratios=[0.5, 1.0, 2.0],
            strides=[4, 8, 16, 32, 64],
            offset=0.0,
        ),
        anchor_matcher=L(Matcher)(
            thresholds=[0.3, 0.7], labels=[0, -1, 1], allow_low_quality_matches=True
        ),
        box2box_transform=L(Box2BoxTransform)(weights=[1.0, 1.0, 1.0, 1.0]),
        batch_size_per_image=256,
        positive_fraction=0.5,
        pre_nms_topk=(2000, 1000),
        post_nms_topk=(1000, 1000),
        nms_thresh=0.7,
    ),
    roi_heads=L(StandardROIHeads)(
        num_classes=80,
        batch_size_per_image=512,
        positive_fraction=0.25,
        proposal_matcher=L(Matcher)(
            thresholds=[0.5], labels=[0, 1], allow_low_quality_matches=False
        ),
        box_in_features=["p2", "p3", "p4", "p5"],
        box_pooler=L(ROIPooler)(
            output_size=7,
            scales=(1.0 / 4, 1.0 / 8, 1.0 / 16, 1.0 / 32),
            sampling_ratio=0,
            pooler_type="ROIAlignV2",
        ),
        box_head=L(FastRCNNConvFCHead)(
            input_shape=ShapeSpec(channels=256, height=7, width=7),
            conv_dims=[],
            fc_dims=[1024, 1024],
        ),
        box_predictor=L(FastRCNNOutputLayers)(
            input_shape=ShapeSpec(channels=1024),
            test_score_thresh=0.05,
            box2box_transform=L(Box2BoxTransform)(weights=(10, 10, 5, 5)),
            num_classes="${..num_classes}",
        ),
        mask_in_features=["p2", "p3", "p4", "p5"],
        mask_pooler=L(ROIPooler)(
            output_size=14,
            scales=(1.0 / 4, 1.0 / 8, 1.0 / 16, 1.0 / 32),
            sampling_ratio=0,
            pooler_type="ROIAlignV2",
        ),
        mask_head=L(MaskRCNNConvUpsampleHead)(
            input_shape=ShapeSpec(channels=256, width=14, height=14),
            num_classes="${..num_classes}",
            conv_dims=[256, 256, 256, 256, 256],
        ),
    ),
    pixel_mean=constants.imagenet_bgr256_mean,
    pixel_std=constants.imagenet_bgr256_std,
    input_format="BGR",
)

还有一些对象或逻辑不能简单地用字典来描述,例如重用的对象或方法调用。 它们可能需要一些重构才能使用递归实例化。

Using Model Zoo LazyConfigs

我们使用 LazyConfig 系统在模型动物园中提供了一些配置,例如:

  1. 公共基线。
  2. 新的 Mask R-CNN 基线

安装detectron2 后,可以通过模型动物园API model_zoo.get_config 加载它们。
使用这些作为参考,您可以自由地为您自己的项目定义自定义配置结构/字段,只要您的训练脚本可以理解它们。 尽管如此,我们的模型动物园配置仍然遵循一些简单的约定以保持一致性,例如 cfg.model 定义模型对象,cfg.dataloader.{train,test} 定义数据加载器对象,cfg.train 包含键值形式的训练选项。 除了 print(),一个更好的查看配置结构的方法是这样的:

from detectron2.model_zoo import get_config
from detectron2.config import LazyConfig
print(LazyConfig.to_py(get_config("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.py")))

从输出中可以更轻松地找到要更改的相关选项,例如 dataloader.train.total_batch_size 用于批量大小,或 optimizer.lr 用于基本学习率。

我们提供了一个参考训练脚本 tools/lazyconfig_train_net.py,可以训练/评估我们的模型动物园配置。 它还展示了如何支持命令行值覆盖。

为了展示新系统的强大功能和灵活性,我们展示了一个简单的配置文件可以让detectron2 训练来自torchvision 的ImageNet 分类模型,即使detectron2 不包含有关ImageNet 分类的特征。 这可以作为在其他深度学习任务中使用detectron2的参考。

Summary

通过使用递归实例化来创建对象,我们避免了将巨大的配置传递到许多地方,因为 cfg 只传递给实例化。 这有以下好处:

  1. 它是非侵入性的:要构造的对象是与配置无关的常规 Python 函数/类。 他们甚至可以住在其他图书馆。 例如,{“target”: “torch.nn.Conv2d”, “in_channels”: 10, “out_channels”: 10, “kernel_size”: 1} 定义了一个卷积层。
  2. 清楚将调用哪些函数/类,以及它们使用哪些参数。
  3. cfg 不需要预定义的键和结构。 只要它转换为有效代码,它就有效。 这提供了更多的灵活性。
  4. 您仍然可以将庞大的字典作为参数传递,就像旧方法一样。

递归实例化和 Python 语法是正交的:你可以使用一个而没有另一个。 但是通过将它们放在一起,配置文件看起来很像将要执行的代码:
在这里插入图片描述

但是,配置文件只定义了字典,可以通过组合或覆盖轻松地进一步操作。 相应的代码只会在稍后调用实例化时执行。 在某种程度上,在配置文件中,我们正在编写“可编辑代码”,稍后将在需要时“延迟执行”。 这就是为什么我们称这个系统为“LazyConfig”。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值