使用模型¶
基于Yacs配置构建模型¶
从一个yacs配置对象中,
可以通过诸如build_model、build_backbone、build_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长整型,一个包含N个标签的向量,范围在[0, num_categories)之间。“gt_masks”: 一个PolygonMasks或BitMasks对象,存储N个掩码,每个实例对应一个。
“gt_keypoints”: 一个Keypoints对象,存储N组关键点,每组对应一个实例。
“sem_seg”:
Tensor[int]采用(H, W)格式。用于训练的真实语义分割数据。 数值表示从0开始的类别标签。“proposals”: 一个仅用于Fast R-CNN风格模型的Instances对象,包含以下字段:
“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可以直接提供您所需的中间结果。您有以下几种选择:
编写一个(子)模型。按照教程的指导,你可以重写一个模型组件(例如模型的头部),使其功能与现有组件相同,但返回你需要的输出。
部分执行模型。您可以像往常一样创建模型, 但使用自定义代码来执行它,而不是使用其
forward()方法。例如, 以下代码在mask head之前获取mask特征。images = ImageList.from_tensors(...) # 预处理后的输入张量 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])
使用前向钩子。 前向钩子可以帮助你获取某个模块的输入或输出。 如果它们不完全符合你的需求,至少可以结合部分执行来获取其他张量。
所有选项都需要您阅读文档,有时还需要阅读现有模型的代码,以理解内部逻辑,从而编写代码获取内部张量。