模型配置与自定义

模型由若干子模块构成,子模块内部实现互相独立,子模块之间的依赖由约定的接口实现; 模块之间的输入输出均为字典类型

任何模型结构可以被抽象为“特征提取模块 + 任务分支”的结构, 特征提取模块包括 backbone 和 neck,任务分支则是针对不同子任务具有不同的结构

BackBone & Neck

  1. 任何的Backbone或者Neck需要继承于 torch.nn.Module , 需要实现以下几个接口:

  • __init__() 当模块有前驱时,第一个参数为前趋模块输出channel数

  • get_outplanes() 当模块有后继时,需实现此方法返回该模块的输出channel数,帮助构建后继网络

  • forward() input为dataset的输出,该方法的输出格式一般为

{'features':[], 'strides': []}
  1. __init__() 函数的参数主要取决于配置文件, 例如,我们先使用一个最简单的配置文件 custom_net.yaml, 实现一个L层的卷积网络.

Note

确保config文件中定义的type为能够被import的模块,在这个例子中,我们新建一个文件custom.py放在 up.models.backbones 文件夹下面. 可以使用注册器来注册新模块并指定一个别名,在配置文件中指定别名即可使用该模块。

net:
    name: backbone
    type: custom_net
    kwarg:
        depth: 3
        out_planes: [64, 128, 256]
import torch
import torch.nn as nn

class CustomNet(torch.nn.Module):
    def __init__(self, depth, out_planes):
        """
        构造参数为配置文件中的kwarg, 在这个例子中由于没有前驱模块,
        所以没有inplances参数
        """
        self.out_planes = out_planes

        in_planes = 3
        for i in range(depth):
            self.add_module(f'layer{i}',
                            nn.Conv2d(in_planes, out_planes[i], kernel_size=3, padding=1))
            self.add_module('relu', nn.ReLU(inplace=True))
            in_planes = out_planes[i]

然后我们再实现 forward()get_outplanes() 函数

Note

foward() 函数需要计算输出的features和strides, 这两个值都为数组形式。

def forward(self, input):
    """
    input的字典类型,数据的组织方式主要取决于config中定义好的Dataset,
    在这里我们假设input中包含了image这一项
    """

    x = input['image']

    for submodule in self.children():
        x = submodule(x)

    # 输出为一个字典,需要包括features和strides两项, 同时我们保留input中的其他数据
    input['features'] = [x]
    input['strides'] = [1]

    return input

def get_outplanes(self):

    return self.out_planes

Note

对于backbone,可以在__init__.py中引用该类,会自动注册至MODULE_ZOO_REGISTRY; 对于检测与分割任务中可能用到的neck,需要通过@MODULE_ZOO_REGISTRY.register(“bias”)将对应的类注册至MODULE_ZOO_REGISTRY;