自定义损失函数必须严格接收y_true和y_pred两个张量参数,顺序不可颠倒;需用框架原生运算、显式加权处理不平衡数据、统一量纲后再加权多目标损失,并优先采用类封装方式确保梯度正确与设备兼容。

损失函数必须接收 y_true 和 y_pred 两个张量参数
几乎所有框架(Keras/TensorFlow、PyTorch)都强制要求自定义损失函数的签名是二元输入:真实标签和模型预测。漏掉任一参数,或顺序颠倒,都会在 model.compile() 或训练循环中报错,比如 TypeError: loss_function() missing 1 required positional argument。
常见错误包括:传入额外参数(如权重、阈值)却不封装成闭包或类;用 NumPy 数组代替框架原生张量(tf.Tensor 或 torch.Tensor),导致梯度中断。
- TensorFlow/Keras 中必须用
tf.reduce_mean()、tf.square()等 TF ops,不能混用np.mean() - PyTorch 中必须保证所有运算可微,避免
.item()、numpy()等脱离计算图的操作 - 若需动态参数(如类别权重),应通过闭包或类初始化传入,而非直接加进函数签名
PyTorch 中用类继承 nn.Module 更安全
函数式写法(def my_loss(y_pred, y_true))在 PyTorch 中容易忽略梯度传播细节,尤其涉及 sigmoid、view、条件分支时。而继承 nn.Module 并实现 forward 方法,能自然接入 autograd 机制,也方便复用(如传参 smooth=1e-6)。
例如 DiceLoss 类里调用 F.sigmoid(inputs) 和 inputs.view(-1) 是安全的,但若在函数式写法中漏掉 requires_grad=True 或误用 in-place 操作(如 inputs.sigmoid_()),就会静默失败——损失值下降但权重不更新。
- 类方式自动支持
device转移(GPU/CPU) - 可直接作为模型组件参与
model.eval()/model.train()切换 - 避免在训练循环中反复构造 lambda 函数,减少闭包捕获引发的内存泄漏风险
不平衡任务必须显式加权,不能只靠 class_weight
Keras 的 class_weight 参数仅作用于内置损失(如 categorical_crossentropy),对自定义函数无效。若你写了带类别权重的逻辑但没在函数体内手动实现加权,模型实际仍按等权优化。
典型场景如医疗诊断:阴性样本占 95%,阳性仅 5%。此时需在损失函数中显式计算加权项,例如:
def weighted_bce(y_true, y_pred):
weights = tf.where(y_true == 1, 10.0, 1.0) # 阳性样本权重拉高
unweighted_loss = tf.keras.losses.binary_crossentropy(y_true, y_pred)
return tf.reduce_mean(weights * unweighted_loss)
- 权重值不能硬编码在训练循环里,必须嵌入损失函数内部
- PyTorch 中同理:用
torch.where()构造权重张量,再与逐样本损失相乘 - 注意权重维度要与
y_pred匹配(如 batch 维需对齐),否则广播出错
多目标损失必须统一量纲再加权求和
像风格迁移中同时优化内容损失、风格损失、总变差损失,三者数值范围可能差几个数量级(内容损失 ~1e-2,风格损失 ~1e3)。直接相加会导致小项被淹没,梯度几乎全由最大项主导。
正确做法是:先分别归一化(如除以各自历史均值),或引入可学习权重系数(alpha, beta),并在训练中监控各分量变化趋势。
- 不要用固定系数(如
0.5 * content_loss + 0.5 * style_loss),除非已验证量纲一致 - Keras 中可用
tf.stop_gradient()冻结某一分量用于调试,避免干扰主梯度流 - PyTorch 中建议用
loss_dict = {'content': ..., 'style': ...}分离记录,便于 TensorBoard 可视化
实际写的时候,最常被跳过的一步是——在训练前用极小 batch(2~4 样本)手算一遍损失值和梯度方向,确认函数行为符合直觉。这点比读文档管用得多。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











