添加组件
- 一、创建自定义分割模型
- 二、创建自定义损失函数
- 三、创建自定义数据变换(数据增强)
- 四、创建自定义骨干网络
- 五、创建自定义数据集
- 举例
PaddleSeg 提供了五种类型的可扩展组件,即 MODELS、LOSSES、TRANSFORMS、BACKBONES、DATASETS。
PaddleSeg使用基于面向对象的设计思想,在创造你自己的模型时,请以 Python class 的形式编写。
一、创建自定义分割模型
如果你打算设计一个自定义分割模型,例如在 newnet.py 中实现 NewNet 类(你可以为你的模型起任何名字,但不要与已有模型的名字重复):
import paddle.nn as nn
from paddleseg.cvlibs import manager
@manager.MODELS.add_component
class NewNet(nn.Layer):
def __init__(self, param1, param2, param3):
pass
def forward(self, x):
pass
- 步骤 1: 将 newnet.py 文件放置在目录 paddleseg/models/ 下。
- 步骤 2: 在你的自定义模型类的上方添加一个 Python 装饰器
@manager.MODELS.add_component
。manager 是一个组件容器,包括 MODELS、BACKBONES、DATASETS、TRANSFORMS、LOSSES。当你添加了这个装饰器并在训练时合理的指定参数,PaddleSeg 就可以自动将你实现的模块添加到训练配置中,体现了低耦合的设计思想。 - 步骤 3: 在 paddleseg/models/__init__.py 中导入你的自定义分割模型类,如下所示:
from .backbones import * # 已经实现的骨干网络类
from .losses import * # 已经实现的损失函数类
from .ann import * #目前已经实现的21种分割模型。你将按照同样的规则添加自己的自定义分割模型。
from .bisenet import *
from .danet import *
from .deeplab import *
from .fast_scnn import *
from .fcn import *
from .gcnet import *
from .ocrnet import *
from .pspnet import *
from .gscnn import GSCNN
from .unet import UNet
from .hardnet import HarDNet
from .u2net import U2Net, U2Netp
from .attention_unet import AttentionUNet
from .unet_plusplus import UNetPlusPlus
from .unet_3plus import UNet3Plus
from .decoupled_segnet import DecoupledSegNet
from .emanet import *
from .isanet import *
from .dnlnet import *
from .sfnet import *
from .shufflenet_slim import ShuffleNetV2 # 以上导入请不要改动,否则会导致一些模型不可用
from .newnet import NewNet # 请在这里添加你自己实现的分割模型类
- 注意:如果仅实现了自己的类,而不在 models 的构造函数中导入你的类,PaddleSeg 将无法识别你添加的模型!
- 步骤 4: 在 yaml 文件中将 type 参数指定为你所创建的分割模型的名称(该参数必须与 newnet.py 中的 NewNet 类名保持一致,请不要误输为 python 文件的名称)。
另外,请你记住所创建的 yaml 文件的完整路径,以便后续在为 train.py 设置模型配置参数时使用你想要的配置。建议将模型的 yaml 文件都保存在 PaddleSeg/configs 的对应模型目录下。
model:
type: NewNet # 你的自定义模型名称
param1: ...
param2: ...
param3: ...
- 注意:如果你的模型有多个输出,即
损失 = 主损失 + 辅助损失
,则你必须修改 yaml 文件中的相应参数,否则会抛出“logits 的长度应该等于 loss 配置的类型数目: 2!=1。”
的错误。 比如 PSPNet 有两个 loss,且都是 CrossEntropyLoss,辅助 loss 的权重是 0.4,所以我们对 loss 的设置如下:
loss:
types:
- type: CrossEntropyLoss
coef: [1, 0.4] #为主损失和辅助损失分配不同的比重,即主损失对最终损失影响更大。
二、创建自定义损失函数
如果你打算设计一个自定义损失函数,例如在 new_loss.py 中实现 NewLoss 类(你可以为你的损失函数起任何名字,但不要与已有损失函数的名字重复):
import paddle.nn as nn
from paddleseg.cvlibs import manager
@manager.LOSSES.add_component
class NewLoss(nn.Layer):
def __init__(self, param1, ignore_index=255):
pass
def forward(self, x):
pass
- 步骤 1: 将 new_loss.py 文件放置在目录 paddleseg/models/losses 下。
- 步骤 2: 在你的自定义损失函数类的上方添加一个Python装饰器
@manager.LOSSES.add_component
。 - 步骤 3: 在 paddleseg/models/losses/__init__.py 中导入你的自定义损失函数类,如下所示:
from .mixed_loss import MixedLoss
from .cross_entropy_loss import CrossEntropyLoss
from .binary_cross_entropy_loss import BCELoss
from .lovasz_loss import LovaszSoftmaxLoss, LovaszHingeLoss
from .gscnn_dual_task_loss import DualTaskLoss
from .edge_attention_loss import EdgeAttentionLoss
from .bootstrapped_cross_entropy import BootstrappedCrossEntropyLoss
from .dice_loss import DiceLoss
from .ohem_cross_entropy_loss import OhemCrossEntropyLoss
from .decoupledsegnet_relax_boundary_loss import RelaxBoundaryLoss
from .ohem_edge_attention_loss import OhemEdgeAttentionLoss
from .l1_loss import L1Loss
from .mean_square_error_loss import MSELoss # 以上导入请不要改动,否则会导致一些损失函数不可用
from .new_loss import NewLoss # 请在这里添加你自己实现的损失函数类
- 步骤 4: 在 yaml 文件中将 type 参数指定为你所创建的损失函数的名称。
loss:
types:
- type: NewLoss # 你的自定义损失函数名称
param1: ...
coef: [1]
三、创建自定义数据变换(数据增强)
如果你打算设计一个自定义数据变换(数据增强),例如在 transforms.py 中新实现一个 NewTrans 类:
@manager.TRANSFORMS.add_component
class NewTrans(nn.Layer):
def __init__(self, param1):
pass
def __call__(self, im, label=None):
...
if label is None:
return (im, )
else:
return (im, label)
- 步骤 1: 在 paddleseg/transforms/transforms.py 文件中定义 NewTrans 类。
- 步骤 2: 在你的 transform 类的上方添加一个Python装饰器
@manager.TRANSFORMS.add_component
。这样就可以了。 - 请注意,不再需要将该类导入到 transforms 的构造函数中了。在 PaddleSeg 中,transform 组件把所有类都集成在一个文件里。你可以查看 PaddleSeg/paddleseg/transforms/__init__.py,看看其文件内容与上文提到的 paddleseg/models/__init__.py 与 paddleseg/models/losses/__init__.py 的文件内容有何不同。
from .transforms import *
from . import functional
# 可以看到,PaddleSeg/paddleseg/transforms/\_\_init\_\_.py 文件中,
# 以 from .transforms import * 导入所有已有的数据变换策略。
# 因此在你的自定义 transform 类写好后,在类对象创建过程中,它会被自动添加进来。
- 步骤 3: 在 yaml 文件中将 type 参数指定为你所创建的数据变换(数据增强)的名称:
train_dataset:
transforms:
- type: NewTrans # 你的自定义数据变换名称
param1: ...
注意:为了更好的可读性,请在 paddleseg/transforms/functional.py 中实现详细的转换函数。
四、创建自定义骨干网络
如果你打算设计一个自定义骨干网络,例如在 new_backbone.py 中实现 NewBackbone 类(你可以为你的骨干网络起任何名字,但不要与已有骨干网络的名字重复):
import paddle.nn as nn
from paddleseg.cvlibs import manager
@manager.BACKBONES.add_component
class NewBackbone(nn.Layer):
def __init__(self, param1):
pass
def forward(self, x):
pass
- 步骤 1: 将 new_backbone.py 文件放置在目录 paddleseg/models/backbones 下。
- 步骤 2: 在你的自定义骨干网络类的上方添加一个 Python 装饰器
@manager.BACKBONES.add_component
。 - 步骤 3: 在 paddleseg/models/backbones/__init__.py 中导入你的自定义骨干网络类,如下所示:
# 目前支持4种骨干网络
from .hrnet import *
from .resnet_vd import *
from .xception_deeplab import *
from .mobilenetv3 import * # 以上导入请不要改动,否则会导致一些骨干网络不可用
from .new_backbone import NewBackbone # 请在这里添加你自己实现的骨干网络类
- 步骤 4: 在 yaml 文件中将 type 参数指定为你所创建的骨干网络的名称。
model:
backbone:
type: NewBackbone # 你的自定义骨干网络名称
param1: ...
五、创建自定义数据集
如果你打算设计一个自定义数据集,例如在 new_data.py 中实现 NewData 类:
from paddleseg..dataset import Dataset
from paddleseg.cvlibs import manager
@manager.DATASETS.add_component
class NewData(Dataset):
def __init__(self,
dataset_root=None,
transforms=None,
mode='train'):
pass
- 步骤 1: 将 new_data.py 文件放置在目录 paddleseg/datasets 下。
- 步骤 2: 在你的自定义数据集类的上方添加一个 Python 装饰器
@manager.DATASETS.add_component
。 - 步骤 3: 在 paddleseg/datasets/__init__.py 中导入你的自定义数据集类,如下所示:
from .dataset import Dataset
from .cityscapes import Cityscapes
from .voc import PascalVOC
from .ade import ADE20K
from .optic_disc_seg import OpticDiscSeg
from .pascal_context import PascalContext
from .mini_deep_globe_road_extraction import MiniDeepGlobeRoadExtraction # 以上导入请不要改动,否则会导致一些数据集不可用
from .new_data import NewData # 请在这里添加你自己实现的数据集类
- 步骤 4: 在 yaml 文件中将 type 参数指定为你所创建的数据集的名称。
train_dataset:
type: NewData # 你的自定义数据集名称
dataset_root: ...
mode: train
举例
假设我们已经按照以上步骤编写好了五个自定义组件:MODELS(NewNet)、LOSSES(NewLoss)、TRANSFORMS(NewTrans)、BACKBONES(NewBackbone)、DATASETS(NewData)。
假设我们要为自定义模型的训练编写 yaml 文件,以设定本次用到的参数。此处我们主要关心对自定义组件参数的配置,其他参数(如优化器)不过多介绍,建议沿用参考配置。如:
batch_size: 4 # 设定迭代一次送入网络的图片数量。一般来说,你所使用机器的显存越大,可以调高batch_size的值。
iters: 10000 # 迭代次数
model:
type: NewNet # 自定义模型类的名称
backbone:
type: NewBackbone # 自定义骨干网络类的名称
pretrained: Null # 如果你实现训练过骨干网络的参数,请指定其存放路径
num_classes: 5 # 标注图中像素类别个数。你的模型应该是根据具体的分割任务设计的,因此你知道该任务下像素类别个数
pretrained: Null # 如果你有网络的与预训练参数,请指定其存放路径
backbone_indices: [-1]
loss:
types:
- type: NewLoss # 自定义损失函数类的名称
coef: [1] # 若使用多种loss,该列表长度与loss数目保持一致
train_dataset:
type: NewData # 自定义数据集类的名称
dataset_root: data/custom_data # 请参考README文档,按其推荐的整理结构组织分割任务所需要的文件,将其放在相应的项目路径下。推荐放在data/下。
transforms:
- type: NewTrans # 自定义数据转换(数据增强)类的名称
custom_factor: 0.5
mode: train # 对训练集设定训练模式
val_dataset:
type: NewData
dataset_root: data/custom_data
transforms:
- type: Normalize
mode: val # 对验证集设定验证模式
optimizer: # 优化器设置
type: sgd
momentum: 0.9
weight_decay: 4.0e-5
lr_scheduler: # 学习率的设置
type: PolynomialDecay
learning_rate: 0.01
power: 0.9
end_lr: 0
假设我们将上面的 yaml 文件保存为 PaddleSeg/configs/custom_configs/NewNet_NewLoss_NewTrans_NewBackbone_NewData.yml,请先切换到 PaddleSeg 目录下后,运行以下命令:
python train.py \
--config configs/custom_configs/NewNet_NewLoss_NewTrans_NewBackbone_NewData.yml \
--do_eval \
--use_vdl \
--save_interval 500 \
--save_dir output