You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

8.1 KiB

PaddleRS开发指南

0 目录

1 新增遥感专用模型

1.1 编写模型定义

首先,在paddlers/rs_models中找到任务对应的子目录(包),任务和子目录的对应关系如下:

  • 变化检测:cd
  • 场景分类:clas
  • 目标检测:det
  • 图像复原:res
  • 图像分割:seg

在子目录中新建文件,以{模型名称小写}.py命名。在文件中编写完整的模型定义。

新模型必须是paddle.nn.Layer的子类。对于图像分割、目标检测、场景分类和图像复原任务,分别需要遵循PaddleSegPaddleDetectionPaddleClasPaddleGAN套件中制定的相关规范。**对于变化检测、场景分类和图像分割任务,模型构造时必须传入num_classes参数以指定输出的类别数目。对于图像复原任务,模型构造时必须传入rs_factor参数以指定超分辨率缩放倍数(对于非超分辨率模型,将此参数设置为None)。**对于变化检测任务,模型定义需遵循的规范与分割模型类似,但有以下几点不同:

  • forward()方法接受3个输入参数,分别是selft1t2,其中t1t2分别表示前、后两个时相的输入影像。
  • 对于多任务变化检测模型(例如模型同时输出变化检测结果与两个时相的建筑物提取结果),需要指定类的USE_MULTITASK_DECODER属性为True,同时在OUT_TYPES属性中设置模型前向输出的列表中每一个元素对应的标签类型。可参考ChangeStar模型的定义。

需要注意的是,如果子目录中存在公共组件,例如paddlers/rs_models/cd/layerspaddlers/rs_models/cd/backbonespaddlers/rs_models/seg/layers中的内容,应当尽可能复用这些组件。

1.2 添加docstring

必须为新模型添加docstring,并在其中给出原文引用和链接(对引用格式不做严格要求,但希望尽可能和该任务已有的其他模型保持一致)。详细的注释规范请参考《代码注释规范》。一个例子如下所示:

"""
The ChangeStar implementation with a FarSeg encoder based on PaddlePaddle.

The original article refers to
    Z. Zheng, et al., "Change is Everywhere: Single-Temporal Supervised Object Change Detection in Remote Sensing Imagery"
    (https://arxiv.org/abs/2108.07002).

Note that this implementation differs from the original code in two aspects:
1. The encoder of the FarSeg model is ResNet50.
2. We use conv-bn-relu instead of conv-relu-bn.

Args:
    num_classes (int): Number of target classes.
    mid_channels (int, optional): Number of channels required by the ChangeMixin module. Default: 256.
    inner_channels (int, optional): Number of filters used in the convolutional layers in the ChangeMixin module.
        Default: 16.
    num_convs (int, optional): Number of convolutional layers used in the ChangeMixin module. Default: 4.
    scale_factor (float, optional): Scaling factor of the output upsampling layer. Default: 4.0.
"""

1.3 编写训练器

请遵循如下步骤:

  1. paddlers/rs_models/{任务子目录}__init__.py文件中添加from ... import语句,可仿造文件中已有的例子。

  2. paddlers/tasks目录中找到任务对应的训练器定义文件(例如变化检测任务对应paddlers/tasks/change_detector.py)。

  3. 在文件尾部追加新的训练器定义。训练器需要继承自相关的基类(例如BaseChangeDetector),重写__init__()方法,并根据需要重写其他方法。对训练器__init__()方法编写的要求如下:

    • 对于变化检测、场景分类、目标检测、图像分割任务,__init__()方法的第1个输入参数是num_classes,表示模型输出类别数。对于变化检测、场景分类、图像分割任务,第2个输入参数是use_mixed_loss,表示用户是否使用默认定义的混合损失;第3个输入参数是losses,表示训练时使用的损失函数。对于图像复原任务,第1个参数是losses,含义同上;第2个参数是rs_factor,表示超分辨率缩放倍数;第3个参数是min_max,表示输入、输出影像的数值范围。
    • __init__()的所有输入参数都必须有默认值,且在取默认值的情况下,模型接收3通道RGB输入
    • __init__()中需要更新params字典,该字典中的键值对将被用作模型构造时的输入参数。
  4. 在全局变量__all__中添加新增训练器的类名。

需要注意的是,对于图像复原任务,模型的前向、反向逻辑均实现在训练器定义中。对于GAN等需要用到多个网络的模型,训练器的编写请参照如下规范:

  • 重写build_net()方法,使用GANAdapter维护所有网络。GANAdapter对象在构造时接受两个列表作为输入:第一个列表中包含所有的生成器,其中第一个元素为主生成器;第二个列表中包含所有的判别器。
  • 重写default_loss()方法,构建损失函数。若训练过程中需要用到多个损失函数,推荐以字典的形式组织。
  • 重写default_optimizer()方法,构建一个或多个优化器。当build_net()返回值的类型为GANAdapter时,parameters参数为一个字典。其中,parameters['params_g']是一个列表,顺序包含各个生成器的state dict;parameters['params_d']是一个列表,顺序包含各个判别器的state dict。若构建多个优化器,在返回时应使用OptimizerAdapter包装。
  • 重写run_gan()方法,该方法接受netinputsmode、和gan_mode四个参数,用于执行训练过程中的某一个子任务,例如生成器的前向计算、判别器的前向计算等等。
  • 重写train_step()方法,在其中编写模型训练过程中一次迭代的具体逻辑。通常的做法是反复调用run_gan(),每次调用时都根据需要构造不同的inputs、并使其工作在不同的gan_mode,并从每次返回的outputs字典中抽取有用的字段(如各项损失),汇总至最终结果。

GAN训练器的具体例子可以参考ESRGAN

2 新增数据预处理/数据增强函数或算子

2.1 新增数据预处理/数据增强函数

paddlers/transforms/functions.py中定义新函数。若该函数需要对外暴露、提供给用户使用,则必须为其添加docstring。

2.2 新增数据预处理/数据增强算子

paddlers/transforms/operators.py中定义新算子,所有算子均继承自paddlers.transforms.Transform类。算子的apply()方法接收一个字典sample作为输入,取出其中存储的相关对象,处理后对字典进行in-place修改,最后返回修改后的字典。在定义算子时,只有极少数的情况需要重写apply()方法。大多数情况下,只需要重写apply_im()apply_mask()apply_bbox()apply_segm()方法就分别可以实现对图像、分割标签、目标框以及目标多边形的处理。

如果处理逻辑较为复杂,建议先封装为函数,添加到paddlers/transforms/functions.py中,然后在算子的apply*()方法中调用函数。

在编写完算子的实现后,必须撰写docstring,并在__all__中添加类名

3 新增遥感影像处理工具

遥感影像处理工具存储在tools/目录中。每个工具应该是相对独立的脚本,不依赖于paddlers/目录中的内容,用户在不安装PaddleRS的情况下也能够直接执行。

在编写脚本时,请使用Python标准库argparse处理用户输入的命令行参数,并在if __name__ == '__main__':代码块中执行具体的逻辑。如果有多个工具用到相同的函数或类,请在tools/utils中定义这些通用组件。