使用模型

从 Yacs 配置构建模型

从 yacs 配置对象,可以使用诸如 build_modelbuild_backbonebuild_roi_heads 等函数构建模型(及其子模型)。

from detectron2.modeling import build_model
model = build_model(cfg)  # returns a torch.nn.Module

build_model 仅构建模型结构并使用随机参数填充它。请参阅以下内容,了解如何将现有检查点加载到模型中以及如何使用 model 对象。

加载/保存检查点

from detectron2.checkpoint import DetectionCheckpointer
DetectionCheckpointer(model).load(file_path_or_url)  # load a file, usually from cfg.MODEL.WEIGHTS

checkpointer = DetectionCheckpointer(model, save_dir="output")
checkpointer.save("model_999")  # save to output/model_999.pth

Detectron2 的检查点识别 pytorch 的 .pth 格式中的模型,以及我们模型库中的 .pkl 文件。有关其使用方式的更多详细信息,请参阅 API 文档

可以使用 torch.{load,save}(针对 .pth 文件)或 pickle.{dump,load}(针对 .pkl 文件)任意操作模型文件。

使用模型

可以使用 outputs = model(inputs) 调用模型,其中 inputs 是一个 list[dict]。每个字典对应于一幅图像,所需键取决于模型类型以及模型是处于训练模式还是评估模式。例如,为了进行推理,所有现有模型都期望“image”键,以及可选的“height”和“width”。下面将解释现有模型的输入和输出的详细格式。

训练:在训练模式下,所有模型都必须在 EventStorage 下使用。训练统计信息将被放入存储中

from detectron2.utils.events import EventStorage
with EventStorage() as storage:
  losses = model(inputs)

推理:如果你只想使用现有模型进行简单的推理,DefaultPredictor 是模型周围的包装器,它提供这种基本功能。它包含默认行为,包括模型加载、预处理,并对单个图像而不是批处理进行操作。有关使用方式,请参阅其文档。

你也可以像这样直接运行推理

model.eval()
with torch.no_grad():
  outputs = model(inputs)

模型输入格式

用户可以实现支持任何任意输入格式的自定义模型。这里我们将介绍 detectron2 中所有内置模型支持的标准输入格式。它们都将 list[dict] 作为输入。每个字典对应于一幅图像的信息。

该字典可能包含以下键

  • “image”: Tensor,采用 (C, H, W) 格式。通道的含义由 cfg.INPUT.FORMAT 定义。图像归一化(如果有)将在模型内部使用 cfg.MODEL.PIXEL_{MEAN,STD} 执行。

  • “height”, “width”: 推理中所需的输出高度和宽度,它不一定是 image 字段的高度或宽度的相同值。例如,image 字段包含调整大小的图像(如果调整大小用作预处理步骤)。但你可能希望输出以原始分辨率呈现。如果提供,模型将以该分辨率生成输出,而不是以 image 作为模型输入时的分辨率。这样效率更高,也更准确。

  • “instances”: 用于训练的 Instances 对象,具有以下字段

    • “gt_boxes”: 一个 Boxes 对象,存储 N 个框,每个实例一个。

    • “gt_classes”: Tensor,类型为 long,N 个标签的向量,范围为 [0, num_categories)。

    • “gt_masks”: 一个 PolygonMasksBitMasks 对象,存储 N 个掩码,每个实例一个。

    • “gt_keypoints”: 一个 Keypoints 对象,存储 N 个关键点集,每个实例一个。

  • “sem_seg”: Tensor[int],采用 (H, W) 格式。用于训练的语义分割真值。值表示从 0 开始的类别标签。

  • “proposals”: 一个 Instances 对象,仅用于 Fast R-CNN 风格的模型,具有以下字段

    • “proposal_boxes”: 一个 Boxes 对象,存储 P 个提议框。

    • “objectness_logits”: Tensor,P 个分数的向量,每个提议一个。

对于内置模型的推理,仅需要“image”键,而“width/height”是可选的。

我们目前没有为全景分割训练定义标准输入格式,因为模型现在使用自定义数据加载器生成的自定义格式。

如何与数据加载器连接:

默认 DatasetMapper 的输出是一个字典,它遵循上述格式。在数据加载器执行批处理后,它将变为 list[dict],内置模型支持该格式。

模型输出格式

在训练模式下,内置模型输出一个 dict[str->ScalarTensor],其中包含所有损失。

在推理模式下,内置模型输出一个 list[dict],每幅图像一个字典。根据模型执行的任务,每个字典可能包含以下字段

  • “instances”: Instances 对象,具有以下字段

    • “pred_boxes”: Boxes 对象,存储 N 个框,每个检测到的实例一个。

    • “scores”: Tensor,N 个置信度分数的向量。

    • “pred_classes”: Tensor,N 个标签的向量,范围为 [0, num_categories)。

    • “pred_masks”: 一个形状为 (N, H, W) 的 Tensor,每个检测到的实例的掩码。

    • “pred_keypoints”: 一个形状为 (N, num_keypoint, 3) 的 Tensor。最后一维中的每一行都是 (x, y, score)。置信度分数大于 0。

  • “sem_seg”: Tensor,采用 (num_categories, H, W) 格式,语义分割预测。

  • “proposals”: Instances 对象,具有以下字段

    • “proposal_boxes”: Boxes 对象,存储 N 个框。

    • “objectness_logits”: N 个置信度分数的 torch 向量。

  • “panoptic_seg”: 一个 (pred: Tensor, segments_info: Optional[list[dict]]) 元组。 pred 张量具有 (H, W) 形状,包含每个像素的段 ID。

    • 如果 segments_info 存在,则每个字典描述 pred 中的一个段 ID,并具有以下字段

      • “id”: 段 ID

      • “isthing”: 该段是否是事物还是素材

      • “category_id”: 该段的类别 ID。

      如果像素的 ID 不存在于 segments_info 中,则认为它是 全景分割 中定义的空值标签。

    • 如果 segments_info 为 None,则 pred 中的所有像素值必须 ≥ -1。值为 -1 的像素被分配空值标签。否则,每个像素的类别 ID 通过 category_id = pixel // metadata.label_divisor 获得。

部分执行模型:

有时你可能希望获得模型内部的中间张量,例如特定层的输入、后处理之前的输出。由于通常存在数百个中间张量,因此没有提供你需要的中间结果的 API。你具有以下选择

  1. 编写一个(子)模型。按照 教程,你可以重写模型组件(例如模型的头部),使其与现有组件做同样的事情,但返回你需要的输出。

  2. 部分执行模型。你可以像往常一样创建模型,但使用自定义代码来执行它,而不是它的 forward()。例如,以下代码在掩码头部之前获得掩码特征。

    images = ImageList.from_tensors(...)  # preprocessed input tensor
    model = build_model(cfg)
    model.eval()
    features = model.backbone(images.tensor)
    proposals, _ = model.proposal_generator(images, features)
    instances, _ = model.roi_heads(images, features, proposals)
    mask_features = [features[f] for f in model.roi_heads.in_features]
    mask_features = model.roi_heads.mask_pooler(mask_features, [x.pred_boxes for x in instances])
    
  3. 使用 前向钩子。前向钩子可以帮助你获得特定模块的输入或输出。如果它们不是你想要的,它们至少可以与部分执行一起使用以获得其他张量。

所有选项都需要你阅读现有模型的文档,有时还需要阅读代码,以了解内部逻辑,以便编写代码来获得内部张量。