- 计算机视觉
- 深度学习
- 预训练
【免费下载链接】gluon-cv
Gluon CV Toolkit
本文围绕 GluonCV 的官方 API 文档页docs/api/loss.rst展开,系统讲解gluoncv.loss模块中全部自定义损失函数的参数、计算公式与源码实现,并结合仓库中 SSD、YOLO、CenterNet、ICNet、SiamRPN 等真实训练脚本的调用方式,帮助读者在实际视觉任务训练时正确选择与配置损失函数。
模块总览:gluoncv.loss 是什么
docs/api/loss.rst是 GluonCV 的 loss API 参考页。该页通过 Sphinx 的automodule指令渲染 gluoncv/loss.py 整个模块的成员,并在页首用autosummary特别列出FocalLoss与SSDMultiBoxLoss两个核心损失。模块文档字符串说明了其设计定位:
Losses are subclasses of
gluon.loss.Losswhich is a HybridBlock actually.
即:这些损失函数本质上是继承自 MXNet GluonLoss/HybridBlock的可混合(hybridize)模块,可以像普通网络层一样参与net.hybridize(),这也是它们在多设备训练脚本中能以列表形式跨设备批量计算的原因。
当前仓库版本为0.11.0(见 gluoncv/init.py),模块中通过__all__导出以下损失:
| 损失类 | 典型任务 |
|---|---|
FocalLoss | 类别不均衡的二分类/多标签分类 |
SSDMultiBoxLoss | SSD 目标检测 |
YOLOV3Loss | YOLOv3 目标检测 |
SoftmaxCrossEntropyLoss | 语义分割(带 ignore 标签) |
SegmentationMultiLosses | DAFUNet 等多头分割 |
MixSoftmaxCrossEntropyLoss | 带辅助输出/Mixup 的分割(FCN、PS 等) |
ICNetLoss | ICNet 三尺度级联分割 |
SoftmaxCrossEntropyOHEMLoss/MixSoftmaxCrossEntropyOHEMLoss | 带 OHEM 的分割 |
DistillationSoftmaxCrossEntropyLoss | 知识蒸馏训练 |
HeatmapFocalLoss | CenterNet 关键点/中心点热图 |
MaskedL1Loss | CenterNet 宽高/中心回归 |
SiamRPNLoss | SiamRPN 视觉跟踪 |
导入方式统一为import gluoncv as gcv后使用gcv.loss.<损失名>,或from gluoncv.loss import SiamRPNLoss。注意该模块依赖 MXNet 后端,而 PyTorch 生态下的对应损失(如HMFocalLoss)位于gluoncv/torch/子包中,不在本文范围。
FocalLoss:应对类别不均衡的分类损失
FocalLoss是文档页autosummary中首先列出的损失,实现位于 gluoncv/loss.py。其思想来自 RetinaNet 论文(Focal Loss for Dense Object Detection, arXiv:1708.02002):通过降低易分类负样本的权重,让网络聚焦于难样本。
参数说明
源码 docstring 定义的完整参数如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
axis | -1 | 计算 softmax/熵时求和的轴,即类别维度 |
alpha | 0.25 | 控制正负样本平衡的系数 α |
gamma | 2 | 控制难样本聚焦程度的指数 γ |
sparse_label | True | 标签是否为整数索引(而非概率分布) |
from_logits | False | 输入是否已经是 log 概率(通常来自 log_softmax) |
batch_axis | 0 | 表示 mini-batch 的轴 |
weight | None | 损失的全局标量权重 |
num_class | None | 类别数;当sparse_label=True时必填 |
eps | 1e-12 | 防止对数数值问题的极小值 |
size_average | True | 是否对除batch_axis外的所有维求均值 |
构造时有一个硬性校验(见 gluoncv/loss.py):若sparse_label=True而未提供num_class >= 1的整数,会直接抛出ValueError。
实现细节
hybrid_forward的前向逻辑(见 gluoncv/loss.py)可以逐行对应到公式:
- 若
from_logits=False,先对pred做 sigmoid,得到每个类别的概率; - 稀疏标签先经
one_hot展开,稠密标签则用label > 0得到正类掩码; - 计算
pt = where(one_hot, pred, 1 - pred),即"预测值在真实类别方向的概率"; - 计算
alpha_t = where(one_hot, alpha, 1 - alpha); - 损失主体:
loss = -alpha_t * (1 - pt)^gamma * log(min(pt + eps, 1)); - 经
_apply_weighting应用全局权重与逐元素sample_weight后,沿batch_axis以外的维度求均值,输出形状为(batch_size,)。
使用示例:
import gluoncv as gcv import mxnet as mx import mxnet.ndarray as nd # pred: (N, C) 的 logits,label: (N,) 的整数索引 focal = gcv.loss.FocalLoss(num_class=10, alpha=0.25, gamma=2.0) pred = mx.gluon.nn.Conv2D(10, 1)(nd.random_normal((4, 3, 32, 32))) label = nd.array([0, 3, 7, 9]) loss = focal(pred, label)SSDMultiBoxLoss:SSD 检测的损失与负样本挖掘
SSDMultiBoxLoss是文档页autosummary中列出的第二个损失,实现位于 gluoncv/loss.py。与FocalLoss不同,它继承自gluon.Block而非Loss,因为它的输入是列表(每个设备一个张量),需要跨设备归一化。
参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
negative_mining_ratio | 3 | 负样本与正样本的数量比(负样本挖掘比例) |
rho | 1.0 | L1-L2 平滑过渡阈值,即平滑参数 |
lambd | 1.0 | 分类与回归损失的相对权重,总损失为L = loss_class + lambda * loss_loc |
min_hard_negatives | 0 | 每张图片至少保留的困难负样本数 |
docstring 中特别提示:由于需要跨设备同步计算 batch 级统计量,该损失比非同步版本开销略大,但作者发现这样对收敛后的模型精度更友好。
计算流程(对应源码)
结合 gluoncv/loss.py 的实现,前向分为几步:
- 统计全局正样本数:
pos_samples = (cls_target > 0),把所有设备上的正样本数求和得到num_pos_all。若num_pos_all < 1且min_hard_negatives < 1,返回一组"零梯度占位损失"(cp * 0),保证反向传播图连通但不贡献梯度; - 分类损失与负样本挖掘:先对
cls_pred做log_softmax,用-nd.pick(pred, cls_target)取出真实类(含背景 0 类)的对数似然作为逐 anchor 交叉熵;随后用双次argsort的技巧计算每个位置在"按损失降序"中的排名,取rank < max(min_hard_negatives, 正样本数 * negative_mining_ratio)的位置作为困难负样本。只有"正样本 ∪ 困难负样本"参与损失,其余位置置零,最后除以max(1, num_pos_all)归一化; - 边界回归损失(L1-L2 平滑):对正样本计算
box_loss = |bp - bt|,再按|d| > rho ? |d| - rho/2 : (0.5/rho) * d²做 L1→L2 平滑,并只在正样本位置生效,同样除以全局正样本数; - 合成总损失:
sum_loss = cls_loss + lambd * box_loss。
返回值是三元组(sum_losses, cls_losses, box_losses),每个都是"每设备一个标量"的列表。
在训练脚本中的真实用法
scripts/detection/ssd/train_ssd.py 展示了标准用法:
mbox_loss = gcv.loss.SSDMultiBoxLoss() ... with autograd.record(): cls_preds = [] box_preds = [] for x in data: # 每个设备一张切片 cls_pred, box_pred, _ = net(x) cls_preds.append(cls_pred) box_preds.append(box_pred) sum_loss, cls_loss, box_loss = mbox_loss( cls_preds, box_preds, cls_targets, box_targets) autograd.backward(sum_loss) ... trainer.step(1) # 损失已按 num_pos 归一化,故 step 传 1 避免再除 batch注意最后trainer.step(1)的注释:由于损失内部已按正样本数归一化,step时不再除以 batch size,这是多设备训练时的一个关键细节。
YOLOV3Loss:YOLOv3 的四项损失
YOLOV3Loss实现位于 gluoncv/loss.py,用于 YOLOv3 训练。它接收 10 个输入:
objness(B, N):预测的物体性(0,1);box_centers(B, N, 2)、box_scales(B, N, 2):预测框中心与宽高;cls_preds(B, N, num_class):类别预测;- 对应的目标张量
objness_t、center_t、scale_t、class_t,其中objness_t中 0 表示负样本、1 表示正样本、-1 表示忽略; weight_t(B, N, 2):中心与尺度目标的损失乘子;class_mask(B, N, num_class):0/1 掩码,用于屏蔽被忽略的样本。
内部维护两个基础损失:SigmoidBinaryCrossEntropyLoss(from_sigmoid=False)与L1Loss。返回四项损失(均为总和):
obj_loss:物体性 logistic 损失,通过hard_objness_t = where(objness_t > 0, 1, objness_t)把正样本目标强制为 1,并用where(objness_t > 0, objness_t, objness_t >= 0)生成掩码使 -1 的忽略区不参与计算;center_loss:中心点 logistic 回归损失,乘子为weight_t * objness_t;scale_loss:尺度 L1 损失;cls_loss:逐类别 logistic 损失,class_mask与objness_t相乘后屏蔽忽略区。
归一化因子denorm是目标张量除 batch 维外所有维度的乘积,中心与尺度损失额外乘 2(因为各有 2 个分量)。该损失在模型侧被封装:gluoncv/model_zoo/yolo/yolo3.py 中YOLOv3Net持有self._loss = YOLOV3Loss(),训练时直接由网络前向输出四项损失。
分割损失家族:以 SoftmaxCrossEntropyLoss 为基类
gluoncv.loss中语义分割相关损失构成一个继承家族,全部围绕"支持 ignore 标签的 Softmax 交叉熵"构建,这正是分割任务与分类任务的关键差异——预测图上存在无效像素(边界、遮挡),必须被排除在损失之外。
SoftmaxCrossEntropyLoss(带 ignore 标签)
实现位于 gluoncv/loss.py。相比 MXNet 原生的gluon.loss.SoftmaxCrossEntropyLoss,它增加:
| 参数 | 默认值 | 说明 |
|---|---|---|
ignore_label | -1 | 需要忽略的标签值 |
size_average | True | 是否按有效像素数重新归一化(normalization='valid') |
前向调用 MXNet 的SoftmaxOutput算子,use_ignore=True并传入ignore_label;稀疏标签用pick取真实类概率,稠密标签则做-sum(log(softmaxout) * label);最后把ignore_label位置的损失显式置零,并沿batch_axis求均值。
SegmentationMultiLosses:多头直接相加
SegmentationMultiLosses(见 gluoncv/loss.py)继承上者,接收(pred1, pred2, pred3, label)四个输入,对三个预测头分别计算交叉熵后直接求和,即loss = loss1 + loss2 + loss3。它在 scripts/segmentation/train.py 中用于 DAFUNet('danet' in args.model)这类带多个预测分支的模型。
MixSoftmaxCrossEntropyLoss:辅助损失与 Mixup
MixSoftmaxCrossEntropyLoss(见 gluoncv/loss.py)是分割训练中最常用的损失,新增参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
aux | True | 是否启用辅助输出损失 |
mixup | False | 是否启用 Mixup 损失 |
aux_weight | 0.2 | 辅助损失的权重 |
其hybrid_forward按aux/mixup组合分派到四条路径:
- 普通模式:基类交叉熵;
- 辅助模式
_aux_forward:主输出与辅助输出各算一次交叉熵,返回loss1 + aux_weight * loss2; - Mixup 模式
_mixup_forward:输入(pred, label1, label2, lam),损失按lam * loss(label1) + (1 - lam) * loss(label2)加权,兼容稀疏/稠密标签; - 辅助 + Mixup 模式
_aux_mixup_forward:两个输出头分别做 Mixup 加权,再按aux_weight合成。
scripts/segmentation/train.py 展示了按模型选择损失的完整逻辑:
# create criterion if 'icnet' in args.model: criterion = ICNetLoss(crop_size=args.crop_size) elif 'danet' in args.model or (args.model_zoo and 'danet' in args.model_zoo): criterion = SegmentationMultiLosses() else: criterion = MixSoftmaxCrossEntropyLoss(args.aux, aux_weight=args.aux_weight)分类侧也复用了这个类:scripts/classification/imagenet/train_imagenet_nasnet.py 中 NASNet 训练用MixSoftmaxCrossEntropyLoss(aux_weight=0.4),开启sparse_label=False分支来配合稠密化(如 label smoothing)后的软标签。
ICNetLoss:三尺度级联加权
ICNetLoss(见 gluoncv/loss.py)专用于 ICNet 训练:
| 参数 | 默认值 | 说明 |
|---|---|---|
weights | (0.4, 0.4, 1.0) | 级联标签引导的权重 |
height/width | 取crop_size | 各尺度预测上采样的目标尺寸 |
crop_size | 480 | 默认裁剪尺寸 |
前向接收 5 个输入:三个尺度的预测pred1~pred3加label。每个预测先经BilinearResize2D统一到height × width,再分别算交叉熵,最终合成:
loss = loss1 + 0.4 * loss2 + 0.4 * loss3 + 1.0 * loss4
即在代码中写作loss[0] + weights[0] * loss[1] + weights[1] * loss[2] + weights[2] * loss[3]。
OHEM 变体:SoftmaxCrossEntropyOHEMLoss
SoftmaxCrossEntropyOHEMLoss(见 gluoncv/loss.py)把基类替换为 OHEM(Online Hard Example Mining)版本:底层调用F.contrib.SoftmaxOHEMOutput,固定thresh=0.6, min_keep=256,即只保留预测置信度低于 0.6 的最难像素(且至少保留 256 个),使网络在训练后期更关注难样本。MixSoftmaxCrossEntropyOHEMLoss(见 gluoncv/loss.py)在其上加了与前面同构的aux/aux_weight辅助输出支持。
DistillationSoftmaxCrossEntropyLoss:知识蒸馏训练
DistillationSoftmaxCrossEntropyLoss(见 gluoncv/loss.py)用于教师-学生蒸馏,参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
temperature | 1 | 软化教师预测的温度 T |
hard_weight | 0.5 | one-hot 硬标签损失的权重 |
sparse_label | True | one-hot 标签是否稀疏 |
内部持有两个原生SoftmaxCrossEntropyLoss(soft_loss用软标签、hard_loss用硬标签),前向hybrid_forward(F, output, label, soft_target)按权重分派:
hard_weight == 0:纯软标签损失T² * CE(output / T, soft_target)(平方温度因子用于补偿梯度缩放);hard_weight == 1:纯硬标签损失CE(output, label);- 一般情形:
(1 - hard_weight) * T² * CE(output/T, soft_target) + hard_weight * CE(output, label)。
scripts/classification/imagenet/train_imagenet.py 给出了真实调用:
sparse_label_loss = not (opt.label_smoothing or opt.mixup) if distillation: L = gcv.loss.DistillationSoftmaxCrossEntropyLoss(temperature=opt.temperature, hard_weight=opt.hard_weight, sparse_label=sparse_label_loss) else: L = gluon.loss.SoftmaxCrossEntropyLoss(sparse_label=sparse_label_loss)可见一个值得注意的约定:当使用 label smoothing 或 mixup 时,标签已经是稠密概率,因此sparse_label置为False。同样的写法也出现在 scripts/classification/imagenet/train_horovod.py 和 AutoGluon 风格的分类估计器 gluoncv/auto/estimators/image_classification/image_classification.py。
HeatmapFocalLoss 与 MaskedL1Loss:CenterNet 的关键点损失
这两个损失服务于 CenterNet,在 scripts/detection/center_net/train_center_net.py 中成组出现:
heatmap_loss = gcv.loss.HeatmapFocalLoss(from_logits=True) wh_loss = gcv.loss.MaskedL1Loss(weight=args.wh_weight) center_reg_loss = gcv.loss.MaskedL1Loss(weight=args.center_reg_weight)HeatmapFocalLoss(见 gluoncv/loss.py)是面向热图的 focal loss:
- 正样本(label == 1):
pos_loss = log(pred) * (1 - pred)^2; - 负样本(label < 1):
neg_loss = log(1 - pred) * pred^2 * (1 - label)^4,其中(1 - label)^4让离关键点越远的负样本权重越低; - 最终按正样本数归一化:
-(sum(pos_loss) + sum(neg_loss)) / max(num_pos, 1)。
注意其from_logits=True的用法:CenterNet 网络输出的热图已经过 sigmoid,因此传入的是概率而非 logits。
MaskedL1Loss(见 gluoncv/loss.py)计算带掩码的均值绝对误差:
L = sum(|label * mask - pred * mask|) / max(sum(mask), 1)
pred、label、mask任意形状但元素数须一致,最终按 mask 中非零元素数归一化,支持逐元素sample_weight。它在 CenterNet 中分别承担宽高回归(wh_weight)与中心点回归(center_reg_weight)的损失。仓库的 Auto 模块也复用了同样的组合,见 gluoncv/auto/estimators/center_net/center_net.py。
SiamRPNLoss:视觉跟踪的加权 L1 + 交叉熵
SiamRPNLoss(见 gluoncv/loss.py)是 SiamRPN 跟踪算法的专用损失,结构上有几个值得注意的固定常量:锚点网格self.h = self.w = 17(17×17 个锚点)、位置通道数loc_c = 10(5 个锚框 × 2)、分类通道数cls_c = 5。构造参数只有一个batch_size(默认 128,每设备训练 batch size),用于归一化。
前向hybrid_forward(F, cls_pred, loc_pred, label_cls, pos_index, neg_index, label_loc, label_loc_weight)返回(cls_loss, loc_loss):
- 位置损失
weight_l1_loss:把预测 reshape 为(B, 4, -1, 17, 17),对 4 个回归维求和后逐位置乘label_loc_weight,按 batch 取均值; - 分类损失
cross_entropy_loss:把类别预测重组为(N, 2)的二分类张量,分别对正样本索引pos_index与负样本索引neg_index用gather_nd取样计算 softmax 交叉熵,再以 0.5/0.5 加权合成;若某类索引为空则该项为 0。
scripts/tracking/siamrpn/train.py 中可以直接看到用法:from gluoncv.loss import SiamRPNLoss,并在训练前criterion = SiamRPNLoss(opt.batch_size)(见该文件 L22、L158)。
小结:按任务选择损失
回到docs/api/loss.rst本身,它作为 API 参考页的职责是把gluoncv.loss模块的成员索引给读者;本文则结合 gluoncv/loss.py 源码给出了完整的落地视图。可以按任务建立如下映射:
| 任务 | 首选损失 | 仓库内参考调用 |
|---|---|---|
| SSD 检测 | SSDMultiBoxLoss | scripts/detection/ssd/train_ssd.py |
| YOLOv3 检测 | YOLOV3Loss(封装在网络内) | gluoncv/model_zoo/yolo/yolo3.py |
| CenterNet 检测 | HeatmapFocalLoss+MaskedL1Loss | scripts/detection/center_net/train_center_net.py |
| 语义分割(带 aux) | MixSoftmaxCrossEntropyLoss | scripts/segmentation/train.py |
| ICNet 分割 | ICNetLoss | 同上(L194) |
| 类别不均衡分类 | FocalLoss | gluoncv/loss.py |
| 知识蒸馏 | DistillationSoftmaxCrossEntropyLoss | scripts/classification/imagenet/train_imagenet.py |
| SiamRPN 跟踪 | SiamRPNLoss | scripts/tracking/siamrpn/train.py |
使用这些损失时的两个通用注意点:其一,所有损失都继承自 Gluon 的Loss/Block,因此可以与hybridize协同,但SSDMultiBoxLoss需要跨设备列表输入,单设备调试时也可传单张量(内部_as_list会自动包装);其二,多损失组合(如aux_weight、lambd、weights)决定了各分支梯度强度,调参时应以训练日志中各分项损失(如CrossEntropy、SmoothL1等 metric)为观测抓手。
- 计算机视觉
- 深度学习
- 预训练
【免费下载链接】gluon-cv
Gluon CV Toolkit
相关推荐
tessdata 实战完整指南:如何用 OCR 训练数据把仓库库位标签识别做到又快又准
tessdata 实战完整指南:如何用 OCR 训练数据把仓库库位标签识别做到又快又准 tessdata 是一套开箱即用的 OCR(光学字符识别)训练数据集合,
OCR人工智能gaohaoyang.github.io主题SEO优化指南:提升博客排名的7个关键技巧
gaohaoyang.github.io主题SEO优化指南:提升博客排名的7个关键技巧 想要让你的技术博客在搜索引擎中获得更好的排名吗?gaohaoyang.g
重新定义音乐体验:如何通过EeveeSpotify解锁Spotify高级功能
重新定义音乐体验:如何通过EeveeSpotify解锁Spotify高级功能 在数字音乐时代,Spotify已成为全球数亿用户的首选流媒体平台。然而,每月高昂的
逆向工程插件系统移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考