MXNet Gluon 损失函数库(mxnet.gluon.loss)完整使用指南
2026/9/20 23:32:00 网站建设 项目流程

MXNet Gluon 损失函数库(mxnet.gluon.loss)完整使用指南

【免费下载链接】mxnetLightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript and more项目地址: https://gitcode.com/gh_mirrors/mxnet1/mxnet

导读

Gluon 在mxnet.gluon.loss模块中提供了 16 个预定义损失函数,覆盖回归、分类、大间隔排序、分布逼近与序列对齐等主流深度学习任务。本文以 docs/python_docs/python/api/gluon/loss/index.rst 为索引骨架,深入 python/mxnet/gluon/loss.py 的完整实现,逐一解析每个损失函数的数学定义、构造参数、输入输出约定与数值稳定性处理,并结合 tests/python/unittest/test_loss.py 的测试用例给出可复现的验证方式。读完本文,你将掌握 Gluon 损失函数的通用机制(权重、批轴、样本加权)以及针对具体任务的选择与配置方法。

gluon.loss 模块概览

mxnet.gluon.loss是 Gluon 高层 API 的核心组成之一,在 Gluon API 总索引 docs/python_docs/python/api/gluon/index.rst 中与gluon.nngluon.rnngluon.data等并列。该模块的全部类均继承自HybridBlock,因此既可以在命令式(NDArray)模式下直接调用,也可以通过hybridize()编译成符号图执行,天然兼容 Gluon 的混合编程范式。

模块导出清单(见 loss.py 的__all__):

  • 基类:Loss
  • 回归类:L1LossL2LossHuberLossPoissonNLLLoss
  • 分类类:SoftmaxCrossEntropyLoss(别名SoftmaxCELoss)、SigmoidBinaryCrossEntropyLoss(别名SigmoidBCELoss)、LogisticLoss
  • 大间隔/度量学习类:HingeLossSquaredHingeLossTripletLossCosineEmbeddingLoss
  • 分布与序列类:KLDivLossCTCLoss

别名(Alias)机制在源码中通过直接赋值实现,例如SigmoidBCELoss = SigmoidBinaryCrossEntropyLoss(loss.py)与SoftmaxCELoss = SoftmaxCrossEntropyLoss(loss.py),两个名字完全等价,可任意混用。

基类 Loss 与通用机制

所有损失函数都继承自Loss(HybridBlock)(loss.py),构造函数统一接收两个关键参数:

参数默认值含义
weight因子类而异(多数为NoneL2Loss1.损失整体的全局标量缩放系数
batch_axis0表示 mini-batch 维度的轴,其余维度会被平均掉

基类通过__repr__提供形如L2Loss(batch_axis=0, w=1.0)的可读字符串,便于调试时打印损失对象。

通用输入约定

TripletLoss(三个张量输入)和CosineEmbeddingLoss(两个向量加一个标签)外,大多数损失函数统一接受:

  • pred:预测张量,形状任意;
  • label:真值张量,与pred元素数相同,内部会先经_reshape_like对齐形状(loss.py);
  • sample_weight(可选):逐元素权重张量,必须可广播到pred的形状。例如pred形状为(64, 10)时,想按样本加权应传入形状(64, 1)的张量。

所有损失输出的形状均为(batch_size,),即非批轴维度全部被平均/求和掉,方便后续直接与优化器配合或继续累加。

加权机制的实现

私有辅助函数_apply_weighting(loss.py)统一完成两步加权:

  1. 若提供sample_weight,先做逐元素(广播)相乘;
  2. 若提供weight,断言其为数值类型后整体乘以标量。

该函数还会根据当前是否处于 NumPy 兼容模式(is_np_array())自动选择broadcast_mulnp.multiply等不同算子实现,保证在新旧两套 ndarray API 下行为一致。

回归损失:L1Loss、L2Loss、HuberLoss、PoissonNLLLoss

L2Loss(均方误差)

数学定义(loss.py):

L = 1/2 * Σ |label_i - pred_i|²

注意源码中传入_apply_weighting的标量是self._weight / 2,即默认weight=1.时恰好实现标准的 MSE 定义。L2Loss的默认weight1.,而其他损失多为None。由于平方项的存在,它对离群点(outlier)敏感,适合误差呈高斯分布的回归场景。

测试用例 test_loss.py 直接验证了数值:对output=[1,2,3,4]label=[1,3,5,7],默认L2Loss求和为 7.0,weight=0.25时为 1.75,传入逐样本权重[0.5,1,0.5,1]时为 6.0。

L1Loss(平均绝对误差)

数学定义:

L = Σ |label_i - pred_i|

对离群点更鲁棒,常用于对异常值不敏感的回归任务。其实现与 L2 完全对称,只是把平方替换为绝对值(loss.py)。测试验证:相同输入下默认L1Loss求和为 6.0,weight=0.5时为 3.0。

HuberLoss(平滑 L1,别名 SmoothedL1)

数学定义(loss.py):

L = Σ { 1/(2ρ) * (label_i - pred_i)² 若 |label_i - pred_i| < ρ |label_i - pred_i| - ρ/2 否则 }
  • rho(默认1):L1 与 L2 的分界阈值。

实现先用F.where按绝对误差是否大于rho选择分段表达式,误差小时呈现 L2 的平滑特性、误差大时退化为 L1 的线性增长,兼顾了平滑性与离群鲁棒性,是目标检测中回归分支的常见选择。

PoissonNLLLoss(泊松负对数似然)

适用于计数型目标(服从泊松分布)的回归任务,数学定义(loss.py):

L = pred - target * log(pred) + log(target!)

参数:

参数默认值含义
from_logitsTrue为 True 时假设pred已是 log 值,计算exp(pred) - target * pred;为 False 时计算pred - target * log(pred + epsilon)
compute_fullFalse是否加入对阶乘项log(target!)的 Stirling 近似target*log(target) - target + 0.5*log(2π*target)(仅对target > 1生效)
epsilon1e-08防止log(0)的数值保护项

该损失输出是标量平均(形状(1,1)),与其他返回(batch_size,)的损失不同。测试 test_loss.py 分别对from_logits=TrueFalsecompute_full=True三种模式与 NumPy 手写公式做了逐一比对。

分类损失:SoftmaxCrossEntropyLoss、SigmoidBinaryCrossEntropyLoss、LogisticLoss

SoftmaxCrossEntropyLoss(软最大交叉熵)

这是多分类任务使用最频繁的损失,支持稠密与稀疏两种标签形式(loss.py):

  • sparse_label=True(默认):label为整数类别索引,其形状为pred去掉axis维后的形状。例如pred形状(1,2,3,4)axis=2时,label形状应为(1,2,4),取值在[0, 3)内;损失为L = -Σ log p_{i,label_i}
  • sparse_label=Falselabel为概率分布(one-hot 或软标签),形状与pred相同;损失为L = -Σ Σ label_j * log p_{ij}

参数:

参数默认值含义
axis-1计算 softmax 与熵所沿的类别轴
sparse_labelTrue标签是否为整数索引而非概率分布
from_logitsFalse输入是否为 log 概率(通常来自log_softmax)。若为 False,内部先做log_softmax,数值上更稳定

实现细节上,非 logits 模式下先用F.log_softmax(pred, axis),稀疏标签时用F.pick按索引取出对应类别的 log 概率并取负,避免了显式计算 softmax 再取 log 带来的中间溢出。

测试 test_loss.py 验证了对output=[[0,2],[1,4]]label=[0,1]的输出为[2.12692809, 0.04858733],并验证了sample_weight=[[0.5],[1.0]]加权后的结果;test_ce_loss 还将其接入mx.mod.Module完成了一个 10 类问题的端到端训练验证。

SigmoidBinaryCrossEntropyLoss(Sigmoid 二分类交叉熵,别名 SigmoidBCELoss)

适用于二分类与多标签分类,label取值应在[0, 1](loss.py)。

关键参数:

参数默认值含义
from_sigmoidFalse为 False 时损失内部将 sigmoid 与 BCE 合并计算,通过 log-sum-exp 技巧数值更稳定;为 True 时假定pred已是 sigmoid 输出,直接计算-Σ [label*log(pred) + (1-label)*log(1-pred)]

额外输入pos_weight:一个长度等于类别数的正样本加权向量(如pred形状(64,10)时取(1,10))。pos_weight > 1会降低假阴性数量、提升召回率;pos_weight < 1则降低假阳性数量、提升精确率,可用于类别不平衡场景。

从源码看(loss.py),非 sigmoid 模式使用稳定性公式:

max(x, 0) - x*z + log(1 + exp(-|x|))

其中 softrelu 项由Activation(act_type='softrelu')计算,等价于数值稳定的log(1+exp(x))。测试 test_bce_loss 同时对照了 NumPy 手写公式与test_bce_loss_with_pos_weight(test_loss.py)对pos_weight路径的验证。

LogisticLoss(逻辑损失)

二分类的另一种形式(loss.py):

L = Σ log(1 + exp(-pred_i * label_i))
  • label_format(默认'signed'):为'signed'label取值{-1, 1};为'binary'时取值{0, 1}(内部会把(label+1)/2变换后再计算)。传入其他值会抛出ValueError

实现同样采用稳定性公式relu(pred) - pred*label + softrelu(-|pred|)。测试 test_logistic_loss_equal_bce 证明了binary格式的LogisticLossSigmoidBCELoss(from_sigmoid=False)数值完全一致,两者只是标签表达方式的差异。

大间隔与度量学习损失:HingeLoss、SquaredHingeLoss、TripletLoss、CosineEmbeddingLoss

HingeLoss 与 SquaredHingeLoss

经典的 SVM 大间隔损失(loss.py):

Hinge: L = Σ max(0, margin - pred_i * label_i) SquaredHinge: L = Σ max(0, margin - pred_i * label_i)²

两者label均需取{-1, 1}margin默认1.0。SquaredHinge 对误分类施加二次惩罚,惩罚更强,是 soft-margin SVM 的常用变体。实现上 Hinge 用F.relu(margin - pred*label),SquaredHinge 在其外层再套一个F.square

TripletLoss(三元组损失)

度量学习/人脸识别等任务的核心损失(loss.py):

L = Σ max(‖positive_i - pred_i‖₂² - ‖negative_i - pred_i‖₂² + margin, 0)

输入为三个张量:pred(锚点)、positive(正样本)、negative(负样本),三者元素数需相同。损失在批轴内先求和,再施加margin(默认1)的 relu 截断,目标是让锚点与正样本的距离至少比与负样本的距离小margin

CosineEmbeddingLoss(余弦嵌入损失)

衡量两个输入向量间的余弦相似度(loss.py):

L = Σ { 1 - cos_sim(input1_i, input2_i) 若 label_i = 1 max(0, cos_sim(input1_i, input2_i) - margin) 若 label_i = -1 } cos_sim(a, b) = a·b / (‖a‖ · ‖b‖)
  • label为长度等于批大小的一维张量,取值{1, -1}表示两个输入是相似(1)还是不相似(-1);
  • margin默认0,控制不相似对的间隔。

内部_cosine_similarity(loss.py)用F.norm归一化后做点积,并加入1e-12的 epsilon 避免除零。测试 test_cosine_loss 将该损失与 NumPy 逐行手写的余弦损失做了数值比对。

分布与序列损失:KLDivLoss、CTCLoss

KLDivLoss(KL 散度损失)

用于度量两个分布之间的距离,常用于变分自编码器、知识蒸馏等场景(loss.py):

  • from_logits=True(默认):pred应为 log 概率(通常来自log_softmax),
L = Σ label_i * [log(label_i) - pred_i]
  • from_logits=Falsepred为未归一化分数(如 Dense 层输出),内部先做log_softmax再按上式计算,axis(默认-1)指定 softmax 维度。

label取值范围应为(0, 1)。实现中在log(label)内加入1e-12防止对 0 取对数。测试 test_kl_loss 用mx.sym.log_softmax(get_net(2))构造 logits 输入完成端到端训练验证。

CTCLoss(连接时序分类损失)

面向语音识别、OCR 等「未分段序列标注」任务的经典损失(loss.py),其理论出自 Graves 的论文《Connectionist Temporal Classification: Labelling Unsegmented Sequence Data with Recurrent Neural Networks》。

参数:

参数默认值含义
layout'NTC'pred的布局,N=批大小、T=序列长度、C=字母表大小;仅支持'NTC''TNC',传入其他值会触发断言
label_layout'NT'标签布局,仅支持'NT''TN'batch_axis会根据label_layoutN的位置自动推导
weightNone全局标量权重

输入约定:

  • pred:softmax 之前的未归一化预测张量,形状随layout变化(如'TNC'时为(序列长度, 批大小, 字母表大小))。最后一个维度索引alphabet_size - 1保留给内部空白标签(blank),因此alphabet_size应为实际字母表大小加一;
  • label:从 0 开始编号的标签张量,形状随label_layout变化;不定长序列需用-1填充成矩形;
  • pred_lengths / label_lengths(可选,默认None):形状(batch_size,)的序列长度向量,用于批内各序列长度不一致的场景。传入后use_data_lengths/use_label_lengths会被置为 True。

文档给出一个具体示例(loss.py):词表为[a, b, c],一批含三个序列'ba''cbb''abac',标签索引为{'a':0, 'b':1, 'c':2, blank:3},则alphabet_size=4,填充后的label张量为:

[[1, 0, -1, -1], [2, 1, 1, -1], [0, 1, 0, 2]]

测试 test_ctc_loss 覆盖了NTC/TNCNT/TN四种布局组合以及传入pred_lengths/label_lengths的变长序列场景;test_ctc_loss_train 则完成了端到端训练。

实战:在 Gluon 训练循环中使用损失函数

Gluon 的损失对象是HybridBlock,调用方式与网络层一致:先实例化,再在训练循环中以loss(pred, label)调用(可选传入sample_weight与部分损失特有的额外输入)。

以 example/gluon/mnist/mnist.py 为代表的典型训练流程如下:

from mxnet import gluon, autograd from mxnet.gluon import nn from mxnet.gluon import loss as gloss net = nn.Sequential() with net.name_scope(): net.add(nn.Dense(128, activation='relu')) net.add(nn.Dense(64, activation='relu')) net.add(nn.Dense(10)) # 输出层不加 softmax loss_fn = gloss.SoftmaxCrossEntropyLoss() # 默认 from_logits=False,内部融合 softmax trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.1, 'momentum': 0.9}) for epoch in range(10): for data, label in train_data: with autograd.record(): output = net(data) loss = loss_fn(output, label) # 返回形状 (batch_size,) 的损失 loss.backward() trainer.step(batch_size)

实践要点:

  1. 不要在输出层手动加 softmaxSoftmaxCrossEntropyLoss默认from_logits=False,会内部完成log_softmax,与F.softmax_cross_entropy等底层算子同理,数值更稳定;
  2. 多分类用SoftmaxCrossEntropyLoss,多标签用SigmoidBinaryCrossEntropyLoss:前者基于 softmax 的类别互斥假设,后者每个类别独立做 sigmoid;
  3. 类别不平衡:二分类场景可用pos_weight,多分类场景可用sample_weight对少数类样本加权,sample_weight形状取(batch_size, 1)即可按样本加权(loss.py);
  4. 符号图训练:所有损失均可在mx.mod.Module中以loss = Loss(output, l); loss = mx.sym.make_loss(loss)形式接入,test_loss.py 中每个损失都提供了对应的 Module 训练验证;
  5. 输出形状统一为(batch_size,):除PoissonNLLLoss输出标量均值外,其余损失均沿非批轴聚合,便于直接loss.backward()或作为mx.metric.Loss的输入。

源码结构速查

想深入研读实现细节,可按以下路径对照阅读:

  • 模块入口与完整实现:python/mxnet/gluon/loss.py(932 行,含全部 16 个类与两个辅助函数);
  • API 索引页:docs/python_docs/python/api/gluon/loss/index.rst(通过automodule自动从 docstring 生成 API 文档);
  • 数值与训练验证:tests/python/unittest/test_loss.py(覆盖每个损失的 NumPy 对照与 Module 端到端训练);
  • NumPy 兼容模式下的行为验证:tests/python/unittest/test_numpy_gluon.py;
  • 典型应用示例:example/gluon/mnist/mnist.py、example/gluon/image_classification.py、example/gluon/embedding_learning/model.py(TripletLoss 的度量学习用法)。

结语

mxnet.gluon.loss以统一的Loss基类收敛了权重与批轴等通用语义,用_apply_weighting统一了样本加权路径,并以HybridBlock保证了命令式与符号式两种执行模式的一致性。无论是快速搭建 MNIST 分类器,还是实现语音识别中的 CTC 对齐、度量学习中的三元组约束,这套预定义损失库都能以最小样板代码直接复用,且每个损失都配有可对照的单元测试,是理解 Gluon 训练管线与数值稳定性设计的上佳入口。

【免费下载链接】mxnetLightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript and more项目地址: https://gitcode.com/gh_mirrors/mxnet1/mxnet

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询