- 人工智能
- 计算机视觉
- 预训练
【免费下载链接】PaddleSeg
Easy-to-use image segmentation library with awesome pre-trained model zoo, supporting wide-range of practical tasks in Semantic Segmentation, Interactive Segmentation, Panoptic Segmentation, Image Matting, 3D Segmentation, etc.
在语义分割训练中,"多个损失函数叠加"是提升精度的常用手段,例如将CrossEntropyLoss与LovaszSoftmaxLoss组合使用。但在 PaddleSeg 的早期设计中,每一个损失函数只能对应网络的一个 logit 输出——如果希望同一个网络输出同时被多种损失函数约束,就必须修改网络代码。MixedLoss正是为解决这一痛点而生的通用损失包装器:它允许将多个损失函数以模块化方式装入同一个类,对多个损失结果进行加权计算,从而在不改动任何网络结构的前提下实现混合损失训练。
读完本文,你将掌握MixedLoss的设计动机、源码级实现原理、YAML 配置文件写法,以及它在单输出/多输出模型中的两种典型配置范式,并了解它与训练主循环、配置校验器之间的底层联动关系。
一、为什么需要 MixedLoss:单一 logit 与多损失之间的矛盾
PaddleSeg 的训练主循环遵循"一个网络输出对应一个损失函数"的映射约定。在 训练核心代码 的loss_computation中,会先通过check_logits_losses强制校验:
len_logits = len(logits_list) len_losses = len(losses['types']) if len_logits != len_losses: raise RuntimeError( 'The length of logits_list should equal to the types of loss config: {} != {}.' .format(len_logits, len_losses))也就是说,模型输出的个数必须与loss.types中损失类的个数严格一致。这带来一个直接限制:如果某个网络(如单头的 DeepLabV3、PPMobileSeg)只有一个输出,却希望同时用交叉熵 + Lovasz Softmax 两种损失约束它,常规配置是无法直接表达的——除非修改网络代码新增一个输出分支。
MixedLoss通过"损失组合器"的思路绕开了这一限制:它把多个损失函数封装成一个整体,对外仍然表现为"一个损失类",占用loss.types中的一个槽位,内部则对每个子损失的结果乘以权重并逐项返回。网络代码因此完全不需要改动。
二、源码级解析:MixedLoss 的实现与约束
MixedLoss的实现位于 paddleseg/models/losses/mixed_loss.py,并注册进全局损失管理器。其核心代码如下:
@manager.LOSSES.add_component class MixedLoss(nn.Layer): """ Weighted computations for multiple Loss. The advantage is that mixed loss training can be achieved without changing the networking code. """ def __init__(self, losses, coef): super(MixedLoss, self).__init__() if not isinstance(losses, list): raise TypeError('`losses` must be a list!') if not isinstance(coef, list): raise TypeError('`coef` must be a list!') len_losses = len(losses) len_coef = len(coef) if len_losses != len_coef: raise ValueError( 'The length of `losses` should equal to `coef`, but they are {} and {}.' .format(len_losses, len_coef)) self.losses = losses self.coef = coef def forward(self, logits, labels): loss_list = [] for i, loss in enumerate(self.losses): output = loss(logits, labels) loss_list.append(output * self.coef[i]) return loss_list2.1 类签名与参数
class paddleseg.models.losses.MixedLoss(losses, coef)| 参数 | 类型 | 含义 |
|---|---|---|
losses | list of nn.Layer | 由多个损失函数类组成的列表,例如[CrossEntropyLoss(), LovaszSoftmaxLoss()] |
coef | list of float/int | 每个损失函数对应的权重比,列表长度必须与losses一致 |
需要特别注意一个细节:原文档将coef描述为float|int,这是指列表内每个元素的类型;从源码看,coef本身必须是一个list。构造时若传入单个标量,会直接抛出TypeError('coefmust be a list!')。
2.2 三大构造约束
__init__中内置了三道防御性校验,这是使用中高频踩坑点:
losses必须是 list:否则抛TypeError;coef必须是 list:否则抛TypeError;- 两者长度必须相等:否则抛
ValueError,并明确指出实际的长度数值。
2.3 前向计算:加权后逐项返回
forward(logits, labels)的逻辑非常简洁:遍历self.losses,对每个子损失调用loss(logits, labels)得到标量损失,再乘以对应权重self.coef[i],最后以一个损失列表loss_list的形式返回,而非返回单一加总标量。
这一点决定了它和训练主循环的配合方式:MixedLoss返回的列表会被外层训练逻辑逐项取出、各自参与反向传播累加(见下文第三节),因此权重既可以在MixedLoss内部施加,也可以由外层配置的coef再次加权。
2.4 注册机制:如何被 YAML 识别
MixedLoss类上方标注了@manager.LOSSES.add_component装饰器。在 paddleseg/cvlibs/manager.py 中,LOSSES = ComponentManager("losses")是全局损失组件管理器,add_component以类的__name__(即字符串MixedLoss)为键注册。因此,配置文件中type: MixedLoss的字符串才能被build_component反查并实例化。这也意味着:只要自定义损失类同样用该装饰器注册,就可以无缝嵌入MixedLoss的losses列表。
三、配置文件实战:两种典型用法
MixedLoss完全通过 YAML 的loss字段声明,无需任何 Python 代码改动。配置文件约定位于 configs 目录,官方给出一致的两层结构:
loss: types: - type: MixedLoss losses: - type: CrossEntropyLoss - type: LovaszSoftmaxLoss coef: [0.8, 0.2] coef: [1]3.1 范式一:单输出模型叠加多种损失
以官方配置 pp_mobileseg_base_ade20k_512x512_80k.yml 为例:
loss: types: - type: MixedLoss losses: - type: CrossEntropyLoss - type: LovaszSoftmaxLoss coef: [0.8, 0.2] coef: [1]这里的模型PPMobileSeg只有一个主输出,loss.types中也只有一个MixedLoss条目,满足"输出个数 == 损失类型个数"的校验。语义是:总损失 = 1.0 ×(0.8 × 交叉熵 + 0.2 × Lovasz Softmax)。外层coef: [1]是损失总权重,一般保持[1]即可。
3.2 范式二:多输出模型逐输出混合
以 ocrnet_hrnetw18_cityscapes_1024x512_160k_lovasz_softmax.yml 为例,OCRNet 有两个 logit 输出(主输出与辅助输出),因此配置中出现了两个MixedLoss条目:
loss: types: - type: MixedLoss losses: - type: CrossEntropyLoss - type: LovaszSoftmaxLoss coef: [0.8, 0.2] - type: MixedLoss losses: - type: CrossEntropyLoss - type: LovaszSoftmaxLoss coef: [0.8, 0.2] coef: [1, 0.4]外层coef: [1, 0.4]分别对应主输出与辅助输出的总权重(辅助输出通常权重更低)。同一模式还出现在 mscale_ocrnet_hrnetv2_psa_cityscapes_1024x2048_150k.yml、deeplabv3p_resnet50_os8_cityscapes_1024x512_80k_rmiloss.yml(RMI Loss 混合)以及fastscnn/ocrnet的 SCL 系列配置中,说明这是 PaddleSeg 混合损失的标准写法。
3.3 配置校验器的隐式补齐规则
在 paddleseg/cvlibs/config_checker.py 的DefaultLossRule中,除了强制要求loss必须同时包含types与coef两个键外,还内置了一条隐式补齐规则:
if len_types != len_coef: if len_types == 1: loss_cfg['types'] = loss_cfg['types'] * len_coef else: raise ValueError(...)即:当types只有 1 个而coef有多个时,校验器会自动把该损失类型复制多份以对齐输出数;但若types数量大于 1 且与coef不等,则直接报错。这解释了为什么单输出模型也可以写成types一项 +coef: [1, 0.4]的形式。
四、与训练主循环的联动:损失如何真正生效
MixedLoss并非独立生效,它与 训练核心 的loss_computation存在专用分支。当某个损失类名为MixedLoss时:
elif loss_i.__class__.__name__ == 'MixedLoss': mixed_loss_list = loss_i(logits, labels) for mixed_loss in mixed_loss_list: loss_list.append(coef_i * mixed_loss)可以看到两条关键链路:
- 外层
coef_i与内层权重是相乘关系:MixedLoss.forward内部已乘以coef[i],返回列表后外层再乘以该输出对应的总权重coef_i(例如 OCRNet 例子中的1或0.4); - 列表逐项展开:
MixedLoss返回的每个加权子损失都会被展开追加到总loss_list,最终由训练循环统一求和并反向传播,因此不需要修改train.py即可生效。
此外,在配置构建阶段,paddleseg/cvlibs/builder.py 的_build_loss会针对MixedLoss做一层特殊处理:当模型使用内置数据集类(非通用Dataset/SegDataset)时,会自动把数据集的IGNORE_INDEX同步进MixedLoss内部的每一个子损失配置,确保混合损失中所有子损失的ignore_index一致。这意味着如果你在MixedLoss内嵌套了多个损失,它们的ignore_index应保持一致,否则可能触发校验断言。
五、纯 Python 调用方式
除了 YAML 配置,MixedLoss也可以像普通nn.Layer一样在代码中直接组装:
import paddle from paddleseg.models.losses import MixedLoss, CrossEntropyLoss, LovaszSoftmaxLoss mixed_loss = MixedLoss( losses=[CrossEntropyLoss(), LovaszSoftmaxLoss()], coef=[0.8, 0.2], ) logits = paddle.randn([2, 19, 512, 512]) # 模拟网络输出 labels = paddle.randint(0, 19, [2, 512, 512]) loss_list = mixed_loss(logits, labels) # 返回 [0.8 * ce, 0.2 * lovasz]调用后返回的是加权后的损失列表,你可自行决定是求和、取平均,还是逐项用于多阶段反传。
六、使用注意事项与常见错误
losses与coef长度不匹配:这是最高频错误。例如给 3 个损失只配 2 个权重,会在构造阶段直接抛ValueError,且报错信息中会同时打印两者的实际长度,便于快速定位。coef误传为标量:MixedLoss(losses=[...], coef=0.8)会抛TypeError,必须写成coef=[0.8]。- 子损失的
ignore_index一致性:_build_loss会自动用数据集的IGNORE_INDEX覆盖并校验每个子损失,手动在子损失里写不同的ignore_index会触发断言失败。 - 多输出模型务必逐输出配置:当模型有多个 logit 输出时,
loss.types中需要为每个输出准备一个损失条目(可以是多个MixedLoss),外层coef与输出一一对应;数量对不上时训练会在第一步就抛出RuntimeError。 - 权重语义:内层
coef决定"同一输出上多个损失的相对权重",外层coef决定"不同输出之间的相对权重",两者是相乘关系,不要混淆。
七、更多相关资源
- 全部损失函数索引:docs/module/loss/losses_cn.md
- 常用于组合的损失文档:CrossEntropyLoss、LovaszSoftmaxLoss、DiceLoss、OhemCrossEntropyLoss、BCELoss
- 官方混合损失配置示例:configs/pp_mobileseg/pp_mobileseg_base_ade20k_512x512_80k.yml、configs/ocrnet/ocrnet_hrnetw18_cityscapes_1024x512_160k_lovasz_softmax.yml
- 混合损失在医疗分割中的扩展实现(带 deep supervision 支持):contrib/MedicalSeg/medicalseg/models/losses/mixes_losses.py
总体而言,MixedLoss用最小的侵入代价解决了"一输出多损失"的配置难题:对外它只是一个普通损失组件,对内它完成了子损失实例化、加权与列表化返回,配合训练主循环的专用分支与配置校验器的自动补齐规则,构成了 PaddleSeg 中可组合、可扩展的混合损失训练基础设施。
- 人工智能
- 计算机视觉
- 预训练
【免费下载链接】PaddleSeg
Easy-to-use image segmentation library with awesome pre-trained model zoo, supporting wide-range of practical tasks in Semantic Segmentation, Interactive Segmentation, Panoptic Segmentation, Image Matting, 3D Segmentation, etc.
相关推荐
PaddleSeg 损失函数 API 实战:Lovasz-Softmax、Lovasz-Hinge 与 MixedLoss 源码级详解与配置指南
PaddleSeg 损失函数 API 实战:Lovasz Softmax、Lovasz Hinge 与 MixedLoss 源码级详解与配置指南 PaddleS
人工智能计算机视觉预训练TVBoxOSC终极指南:如何用安卓手机打造智能电视控制中心?
TVBoxOSC终极指南:如何用安卓手机打造智能电视控制中心? TVBoxOSC是一个基于开源代码库构建的电视盒子智能控制解决方案。通过这款工具,用户可以将安卓
人工智能计算机视觉预训练SuperGradients 损失函数(Loss)完全指南:内置损失、自定义损失与配置化训练
SuperGradients 损失函数(Loss)完全指南:内置损失、自定义损失与配置化训练 导读 在 SuperGradients 中,损失函数(Loss)是
深度学习计算机视觉预训练人工智能
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考