自定义损失函数必须继承 tf.keras.losses.Loss 并实现 call 方法,返回 shape 为 (batch_size,) 的张量;需处理 dtype 对齐、sample_weight、参数序列化及 eager 模式调试。

自定义损失函数必须继承 tf.keras.losses.Loss,不能继承 Python 的普通类
TensorFlow 2.x 的自定义损失函数不是“继承 Python 的 Loss 类”——Python 本身没有叫 Loss 的内置类。你真正要继承的是 tf.keras.losses.Loss,这是 Keras 提供的抽象基类,它要求你实现 call 方法(接收 y_true 和 y_pred),并支持自动微分、批次维度对齐和 reduction 行为。
常见错误是直接写一个普通函数或继承 object,然后传给 model.compile(loss=...)——这虽然能跑,但会丢失样本级权重支持、sample_weight 处理逻辑,且在分布式训练或 tf.function 装饰下容易出 silent bug。
- 必须继承
tf.keras.losses.Loss -
call方法必须返回 shape 为(batch_size,)的tf.Tensor(标量 loss 需用tf.reduce_mean等聚合后由框架统一处理) - 若需支持
sample_weight,应在call内手动加权(父类不自动处理,除非你调用super().call(...)且父类已实现)
call 方法里别漏掉 tf.convert_to_tensor 和 dtype 对齐
输入的 y_true 和 y_pred 不一定是 float32,尤其当模型输出是 float16 或标签是整数类型(如分类任务的 int32 标签)时,直接做减法或交叉熵会报错或产生 NaN。
正确做法是在 call 开头做显式转换:
def call(self, y_true, y_pred):
y_true = tf.convert_to_tensor(y_true)
y_pred = tf.convert_to_tensor(y_pred, dtype=y_true.dtype)
# 后续计算...
- 不建议用
tf.cast强转y_true到float32,除非你确定所有任务都需浮点标签(比如回归);分类任务中整型标签是合法且高效的 - 若损失涉及 log(如交叉熵),务必确保
y_pred已经过 softmax 或 sigmoid,并做了 clip(例如tf.clip_by_value(y_pred, 1e-7, 1-1e-7)),否则梯度爆炸或 NaN 很常见
带参数的自定义损失(如 focal loss 的 alpha、gamma)必须在 __init__ 中保存为属性
如果你的损失函数需要超参(比如 focal_loss(alpha=0.25, gamma=2.0)),这些值必须在 __init__ 中存为 self.alpha、self.gamma,而不是作为 call 的参数传入——Keras 在构建图或保存模型时,只会序列化类实例的属性,不会捕获 call 的临时参数。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
否则会出现:训练时正常,但 model.save() 后再加载,loss 行为异常或报 AttributeError。
- 所有可配置参数都应在
__init__中设默认值,并赋给self - 如果想让损失函数支持序列化(即能被
tf.keras.models.load_model正确重建),还需重写get_config方法并返回参数字典 - 简单场景下可跳过
get_config,但只要用了model.save(save_format='h5')或导出 SavedModel,就强烈建议加上
调试时先关掉 @tf.function,避免错误信息被吞掉
当你把自定义 loss 传给 model.compile 后,Keras 默认在训练循环中用 @tf.function 包装 loss 计算。一旦 call 里有非法操作(比如对 None 求 shape、用 numpy 函数、未 traceable 的控制流),你会看到类似 OperatorNotAllowedInGraphError 或极简的 TypeError: Expected float32, got None,根本看不出哪一行出问题。
- 临时在
call开头加tf.print("y_true:", tf.shape(y_true), "y_pred:", tf.shape(y_pred))辅助定位维度 - 更有效的是:训练前加
tf.config.run_functions_eagerly(True),强制 eager mode 运行,此时错误堆栈会指向真实代码行 - 确认无误后再关掉 eager,恢复性能
最易被忽略的是:自定义 loss 的返回值必须是 scalar tensor(单个数值)或 batch-wise tensor(shape=(N,)),不能是 Python float、list、numpy array——哪怕只差一层 .numpy() 或 float(),都会导致训练静默失败或梯度为零。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










