20. MaskRCNN 大算子接口指南

20.1. MaskRCNN 基础

两阶的 MaskRCNN 由两类组成:

  • 3 个有权值模块: backbone.pt 和2个 bbox/mask 中间有权值层(按顺序命名为 torch_bbox/mask.pt ).

  • 5 个动态无权值模块:包括 RPN head, bbox pooler, bbox head, mask pooler, mask head.

因此,完整的MaskRCNN 可以通过以下过程表示:

  • bbox 检测头: backbone.pt => RPN head => bbox pooler => torch_bbox.pt => bbox head.

  • mask 检测头: backbone.pt => RPN head => mask pooler => torch_mask.pt => mask head.

20.1.1. 模块快速分割方法

由于 MaskRCNN 拆分依赖原框架工程的兼容情况, 用户可能无法trace每部分,本章节中以 mask head 为无法trace的例子.

MaskRCNN 的两类模块分割点, 即再次进入有权值模块首层的接入点.

20.2. MaskRCNN 大算子

由于基于细粒度操作的MaskRCNN部署, 操作动态IR的难度较高,因此提出了以下MaskRCNN 大算子解决方案:

粗粒度:

  1. 内置 MaskRCNN 专属后端:现在 mlir-backend 直接支持动态无权值模块, 目前包括 RPN head, bbox head, bbox poolermask pooler. 因此,大多数与前端推理图解析和优化相关的繁重工作得以节省, 如避免了大量动态形参推理或变种细粒度算子的支持.

  2. 模型重建:用户只需 4 个结构信息即可重建完整的 MaskRCNN:

  • io_map: 描述模块接口, 与MaskRCNN拓扑同构.定义为 (目标模块索引, 操作数索引):(源模块索引: 操作数索引).

  • config.yaml: 用于存储MaskRCNN超参数的 YAML 文件,事先提供.

  • BackBone: 通常从顶部到 RPN,事先从原始 MaskRCNN 中拆分.

  • 有权值模块: bbox/mask 中间有权值层,事先从原始 MaskRCNN 中拆分.

20.3. 快速入门

在深入了解新的 MaskRCNN 特性之前, 请先了解新的 MaskRCNN yaml文件格式和单元测试.

20.3.1. 准备您的 YAML

regression/dataset/MaskRCNN/CONFIG_MaskRCNN.yaml 中准备了一个默认的 YAML, 其结构如下:

  • model_transform的编译参数:重建 MaskRCNN 的结构信息.

    • io_map: 即定义 (目标模块索引, 操作数索引):(源模块索引: 操作数索引),其中 -1 表示整体模型的顶层输入,-2 表示整体模型的顶层输出,0、1、2… 表示 MaskRCNN 模块的 ID.

      例如,{(0,0):(-1,0),(1,0):(0,0),(-2,0):(1,0)},表示 模块[0] 的 input[0] 来自整体模型的 input[0], 模块[1] 的 input[0] 来自 模块[0] 的 output[0],整体模型的 output[0] 来自 模块[1] 的 output[0].

    • maskrcnn_output_num: 整体 MaskRCNN 的最终输出操作数的数量.

    • maskrcnn_structure: 描述 MaskRCNN 模块顺序.1 表示 torch.pt 模型,0 表示 PPLOp.例如,[1,0,1] 表示第一个模块是 torch 模型,第二个模块是 PPLOp,第三个模块是 torch 模型.

    • maskrcnn_ppl_op: 后端已经用PPL实现的 MaskRCNN 算子名称.

    • numPPLOp_InWithoutWeight_MaskRCNN: 每个 PPLOp 的输入操作数;请不要计入权重.

  • MaskRCNN 的超参数: 必要 MaskRCNN 配置参数, 来自源码 MaskRCNN 框架.

20.3.2. 模块单元测试

--case 提供单试模块的选择,当前支持 4 个动态无权值模块测试: RPN head, bbox pooler, bbox head, mask pooler.

更多指导请参见 test_MaskRCNN.py.

$ test_MaskRCNN.py --case MaskRCNN_Utest_RPNGetBboxes --debug

20.4. 新前端接口API

20.4.1. [步骤 1] 运行 model_transform

用于将 MaskRCNN 转换为 MLIR 文件.

  • 跳过推理:请注意, 在此步骤中不需要形参推理或计算推理, 无需输入/比较参考的 数据 .npz 文件,但需要事先提供 config.yaml.

  • 跳过预处理:请注意, 在此步骤中, 默认无预处理.

  • 新的启动符: 注意 enable_maskrcnn.

$ model_transform.py \
    --model_def  backbone.pt \
    --model_extern torch_bbox.pt,torch_mask.pt \
    --model_name MaskRCNN \
    --input_shapes [[1,3,800,1216],[1,1,4741,4],[1,1,20000,4],[1,1,20000,4],[1,1,100,4]] \
    --mlir MaskRCNN.mlir \
    --enable_maskrcnn \
    --path_yaml regression/dataset/MaskRCNN/CONFIG_MaskRCNN.yaml

20.4.2. [步骤 2] 生成输入数据

20.4.2.1. MaskRCNN 输入格式

MaskRCNN 大算子框架需要 5 个输入:

  • 预处理图片:经过预处理的图片.

  • max_shape_RPN/max_shape_GetBboxB:如果输入图片的形参为 S1,原始形参为 S0,则最大形参为 int(S0 * S1 / S0),并扩展为常量权重张量.

  • scale_factor_GetBboxB/scale_factor_MaskPoolerB:如果输入图片的形参为 S1,原始形参为 S0,则缩放因子为 float(S1 / S0),并扩展为常量权重张量.

20.4.2.2. 输入格式化工具

tpu-mlir/python/tools/tool_maskrcnn.py 提供了一个格式化输入数据工具,以帮助您生成满足上述要求的数据.

  • 跳过预处理: 输入图像应为经过预处理的图像,因为 MaskRCNN 的预处理过程通常很复杂,并依赖于原框架中的特定函数.

除了 path_yaml,还需要指定三个参数:

  • path_input_image: 经过预处理的图像,保存为 npz 格式.

  • basic_max_shape_inverse:预处理后的高度和宽度.

  • basic_scalar_factor: 正是上述的 float(S1 / S0), basic_max_shape_inverse 除以原始形状重新排序后的 height, width.

结果数据将存储在与 path_input_image 相同的路径中,但后缀为 SuperiorMaskRCNNInputPreprocessed.

请查看 tool_maskrcnn.py 以获取更多指导.

$ tool_maskrcnn.py \
  --path_yaml              ./regression/dataset/MaskRCNN/CONFIG_MaskRCNN.yaml \
  --path_input_image       Superior_IMG_BackBone.npz \
  --basic_max_shape_inverse 1216,800 \
  --basic_scalar_factor     1.8734375,1.8735363 \
  --debug

20.4.3. [步骤 3] 运行 model_deploy

  • 跳过推理: 此处跳过量化比较和仿真比较。

  • 强制参数: --quantize 模式被强制为 F32, --processor 被强制为 BM1684X

  • 新的启动符: 注意 enable_maskrcnn.

$ model_deploy.py \
    --mlir MaskRCNN.mlir \
    --quantize F32 \
    --processor BM1684X \
    --model MaskRCNN.bmodel \
    --debug \
    --enable_maskrcnn

20.5. IO_MAP 指南

手动生成 io_map 分为两个步骤:

  • 模块接口的完备定义: 准确收集输入和输出的操作数和形参,以及模块连接.

  • 创建相应的 io_map: 应准确且唯一地重建完整的 MaskRCNN.

20.5.1. [步骤 1] 描述模块接口

如开始所述, 完整的 MaskRCNN 被截断为多个模块.

请为每个模块描述以下信息:

  • 输入:输入操作数或常量权重

  • 形参:以 4 维shape表示.

  • 数据类型:仅支持 fp32 或 int32.

  • 连接: 每个输入找出其来源的上层模块(可能不是上一个相邻模块), 和上层模块相应输出的操作序数.

请注意, -1 表示完整 MaskRCNN 的输入, 而 -2 表示完整模型的输出

[-1] Top_In

输入编号

名称

形参

数据类型

输入 0)

‘img.1’

[1,3,800,1216]

输入 1)

‘max_shape_RPN’

[bs,1,max_filter_num,4]

int32

输入 2)

‘max_shape_GetBboxB’

[1,bs*20000,1,4]

int32

输入 3)

‘scale_factor_GetBboxB’

[1,bs,20000,4]

FP32

输入 4)

‘scale_factor_MaskPooler’

[bs,1,roi_slice,4]

FP32

[Torch] SubBlock-0: BackBone.pt

IO类型

名称

形参

数据类型

连接信息[源模块-操作数序号]

输入 0)

‘img.1’

[1,3,800,1216]

FP32

from_[TOP_IN]Input-0

输出 0)

‘11’

[1,256,200,304]

FP32

输出 1)

‘12’

[1,256,100,152]

FP32

输出 2)

‘13’

[1,256,50,76]

FP32

输出 3)

‘16’

[1,256,25,38]

FP32

输出 4)

‘15’

[1,256,13,19]

FP32

输出 5)

‘18’

[1,3,200,304]

FP32

输出 6)

‘19’

[1,3,100,152]

FP32

输出 7)

‘20’

[1,3,50,76]

FP32

输出 8)

‘21’

[1,3,25,38]

FP32

输出 9)

‘22’

[1,3,3,19]

FP32

输出 10)

‘23’

[1, 2,200,304]

FP32

输出 11)

‘24’

[1,12,100,152]

FP32

输出 12)

‘25’

[1,12,50,76]

FP32

输出 13)

‘26’

[1,12,25,38]

FP32

输出 14)

‘27’

[1,12,13,19]

FP32

[PPL] SubBlock-1: ppl::RPN_get_bboxes

IO类型

名称

形参

连接信息[源模块-操作数序号]

输出

0 result_list

[bs,1,max_per_img,num_levels]

输入

1 cls_scores_0

[bs,3,200,304]

[Torch][SubBlock-0]Output 5)

输入

2 cls_scores_1

[bs,3,100,152]

[Torch][SubBlock-0]Output 6)

输入

3 cls_scores_2

[bs,3,50,76]

[Torch][SubBlock-0]Output 7)

输入

4 cls_scores_3

[bs,3,25,38]

[Torch][SubBlock-0]Output 8)

输入

5 cls_scores_4

[bs,3,13,19]

[Torch][SubBlock-0]Output 9)

输入

6 bbox_preds_0

[bs,12,200,304]

[Torch][SubBlock-0]Output 10)

输入

7 bbox_preds_1

[bs,12,100,152]

[Torch][SubBlock-0]Output 11)

输入

8 bbox_preds_2

[bs,12,50,76]

[Torch][SubBlock-0]Output 12)

输入

9 bbox_preds_3

[bs,12,25,38]

[Torch][SubBlock-0]Output 13)

输入

10 bbox_preds_4

[bs,12,13,19]

[Torch][SubBlock-0]Output 14)

输入

11 max_shape

[bs,1,max_filter_num,4]

[TOP_IN]Input-1

输入

12 mlvl_anchors_0

[bs,1,3*200*304,4]

[mlir][Weight]

输入

13 mlvl_anchors_1

[bs,1,3*100*152,4]

[mlir][Weight]

输入

14 mlvl_anchors_2

[bs,1,3*50*76,4]

[mlir][Weight]

输入

15 mlvl_anchors_3

[bs,1,3*25*38,4]

[mlir][Weight]

输入

16 mlvl_anchors_4

[bs,1,3*13*19,4]

[mlir][Weight]

[PPL] SubBlock-2: ppl::Bbox_Pooler

IO类型

名称

形参

连接信息[源模块-操作数序号]

输出

0 result_res

[bs*250,256,PH,PW]

输出

1 result_rois

[bs,max_per_img,1,roi_len]

输入

2 feat0

[bs,256,H,W]

[Torch][SubBlock-0]Output 0)

输入

3 feat1

[bs,256,H/2,W/2]

[Torch][SubBlock-0]Output 1)

输入

4 feat2

[bs,256,H/4,W/4]

[Torch][SubBlock-0]Output 2)

输入

5 feat3

[bs,256,H/8,W/8]

[Torch][SubBlock-0]Output 3)

输入

6 rois_multi_batch

[bs,roi_slice,1,roi_len]

[PPL][SubBlock-1]result_list

[Torch] SubBlock-3: torch_bbox.pt

Batch

IO类型

名称

形参

数据类型

连接信息[源模块-操作数序号]

Batch-1

输入

0

[250,256,7,7]

FP32

[PPL][SubBlock-2]result_res

输出

0

[250,81]

FP32

输出

1

[250,320]

FP32

[PPL] SubBlock-4: ppl::get_bboxes_B

Batch

IO类型

名称

形参

连接信息[源模块-操作数序号]

Batch 1

输出

result_det_bboxes

[bs,1,100,5]

输出

result_det_labels

[bs,1,100,1]

输入

rois

[1,bs*250,1,5]

[PPL][SubBlock-2]1-result_rois

输入

bbox_pred

[1,bs*250,1,320]

[Torch][SubBlock-3]Output 1

输入

cls_score

[1,bs*250,1,81]

[Torch][SubBlock-3]Output 0

输入

max_val

[1,bs*20000,1,4]

[TOP_IN]Input-2

输入

scale_factor

[1,bs,20000,4]

[TOP_IN]Input-3

[PPL] SubBlock-5: ppl::Mask_Pooler

IO类型

序号

名称

形参

连接信息[源模块-操作数序号]

输出

0

result_res

[roi_num,C,PH,PW]

输入

1

x0

[bs,256,H,W]

[Torch][SubBlock-0]Output 0

输入

2

x1

[bs,C,H/2,W/2]

[Torch][SubBlock-0]Output 1

输入

3

x2

[bs,C,H/4,W/4]

[Torch][SubBlock-0]Output 2

输入

4

x3

[bs,C,H/8,W/8]

[Torch][SubBlock-0]Output 3

输入

5

det_bboxes_multi_batch

[bs,1,roi_slice,roi_len]

[PPL][SubBlock-4]0-result_det_bboxes

输入

6

det_labels_multi_batch

[bs,1,roi_slice,1]

[PPL][SubBlock-4]1-result_det_labels

输入

7

scale_factor

[bs,1,roi_slice,4]

[TOP_IN]Input-4

[Torch] SubBlock-6: torch_mask.pt

Batch

IO类型

序号

名称

形参

数据类型

连接信息[源模块-操作数序号]

Batch 1

输入

0

input.2

[100,256,14,14]

FP32

[PPL][SubBlock-5]0-result_res

输出

0

75

[100,80,28,28]

FP32

Batch 4

输入

0

input.2

[400,256,14,14]

FP32

输出

0

75

[400,80,28,28]

FP32

[-2] TOP_OUT

IO类型

序号

形参

数据类型

连接信息[源模块-操作数序号]

输出

0

[bs,1,100,5]

FP32

[PPL][SubBlock-5]0-result_det_bboxes

输出

1

[bs,1,100,1]

FP32

[PPL][SubBlock-5]1-result_det_labels

输出

2

[100,80,28,28]

FP32

[Torch][SubBlock-6]

20.5.2. [步骤 2] 描述 IO_MAP

以以下格式重新组织上述模块接口:

  • 模块名称:一个模块的名称和序号.

  • 上层输入:每个输入找出其来源的上层模块(可能不是上一个相邻模块), 和上层模块相应输出的操作序数.

  • 连接数:记录输入操作数的总数.

  • 映射: (目标模块索引, 操作数索引):(源模块索引: 操作数索引)

请注意, -1 表示完整 MaskRCNN 的输入, 而 -2 表示完整模型的输出.

[0]TORCH_0-rpn

  • 上层输入:

    • ← [-1]TOP_IN[0]

  • 连接数: 1

  • 映射:

    • (0,0):(-1,0)

[1]PPL-RPNGetBboxes

  • 上层输入:

    • ← [0]TORCH_0-rpn[5:15]

    • ← [-1]TOP_IN[1]

  • 连接数: 10

  • 映射:

    • (1,0):(0,5)

    • (1,1):(0,6)

    • (1,2):(0,7)

    • (1,3):(0,8)

    • (1,4):(0,9)

    • (1,5):(0,10)

    • (1,6):(0,11)

    • (1,7):(0,12)

    • (1,8):(0,13)

    • (1,9):(0,14)

    • (1,10):(-1,1)

[2]PPL-Bbox_Pooler

  • 上层输入:

    • ← [0]TORCH_0-rpn[0:4]

    • ← [1]PPL-RPNGetBboxes[0]

  • 连接数: 4 + 1

  • 映射:

    • (2,0):(0,0)

    • (2,1):(0,1)

    • (2,2):(0,2)

    • (2,3):(0,3)

    • (2,4):(1,0)

[3]Torch-2

  • 上层输入:

    • ← [2]PPL-Bbox_Pooler

  • 连接数: 1

  • 映射:

    • (3,0):(2,0)

[4]PPL-GetBboxB

  • 上层输入:

    • ← [2]PPL-Bbox_Pooler[1]

    • ← [3]Torch-2[0:2]_inverse

    • ← [-1]TOP_IN[2:4]

  • 连接数: 1 + 2 (逆向) + 2

  • 映射:

    • (4,0):(2,1)

    • (4,1):(3,1)

    • (4,2):(3,0)

    • (4,3):(-1,2)

    • (4,4):(-1,3)

[5]ppl-MaskPooler

  • 上层输入:

    • ← [0]Torch-RPN[0:4]

    • ← [4]PPL-GetBboxB[0:2]

    • ← [-1]TOP_IN[4]

  • 连接数: 4 + 2

  • 映射:

    • (5,0):(0,0)

    • (5,1):(0,1)

    • (5,2):(0,2)

    • (5,3):(0,3)

    • (5,4):(4,0)

    • (5,5):(4,1)

    • (5,6):(-1,4)

[6]Torch-3

  • 上层输入:

    • ← [5]ppl-MaskPooler

  • 连接数: 1

  • 映射:

    • (6,0):(5,0)

[-2]TOP_OUT

  • 上层输入:

    • ← [4]PPL-GetBboxB[0:2]

    • ← [6]Torch-3

  • 连接数: 2 + 1

  • 映射:

    • (-2,0):(4,0)

    • (-2,1):(4,1)

    • (-2,2):(6,0)

20.5.3. IO_MAP参数整理

收集上述所有映射信息后,生成 io_map 字典:

  • io_map: {(0,0):(-1,0),(1,0):(0,5),(1,1):(0,6),(1,2):(0,7),(1,3):(0,8),(1,4):(0,9),(1,5):(0,10),

    (1,6):(0,11),(1,7):(0,12),(1,8):(0,13),(1,9):(0,14),(1,10):(-1,1),(2,0):(0,0),(2,1):(0,1),(2,2):(0,2),

    (2,3):(0,3),(2,4):(1,0),(3,0):(2,0),(4,0):(2,1),(4,1):(3,1),(4,2):(3,0),(4,3):(-1,2),(4,4):(-1,3),

    (5,0):(0,0),(5,1):(0,1),(5,2):(0,2),(5,3):(0,3),(5,4):(4,0),(5,5):(4,1),(5,6):(-1,4),(6,0):(5,0),

    (-2,0):(4,0),(-2,1):(4,1),(-2,2):(6,0)}

现直接在 model_transform 中使用它, 编译过程中将生成一个 revised_io_map_${model_name}.svg 图片,以帮助您检查和可视化 io_map, 如下图.

_images/revised_io_map_Superior_MaskRCNN_End2End.png

20.6. mAP 推理

转换和部署这样的粗粒度 MaskRCNN 进行到这里还不够, 要在 COCO2017 数据集上mAP推理, 需要仔细地接入原始推理框架.

有关更多推断细节,请参阅我们的 model-zoo 项目.