回调函数未被调用的首要原因是未通过callbacks参数显式传入回调实例列表;需确认model.fit()中callbacks=[MyCallback()]拼写正确、非空,且类继承自tf.keras.callbacks.Callback,同时数据集batch正常产出、steps_per_epoch设置合理。

回调函数没被调用:检查是否传入 callbacks 参数
TensorFlow 的 model.fit() 不会自动执行任何回调,必须显式通过 callbacks 参数传入列表。常见疏漏是写了回调类但忘记放进 callbacks=[...] 里,或者传了空列表 callbacks=[]。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 确认
model.fit()调用时确实包含callbacks=[MyCallback()](不是callback=...单数拼写) - 避免在训练前动态清空或覆盖
callbacks列表(比如误写成callbacks = []后又没重赋值) - 可在回调的
__init__中加print("MyCallback initialized"),确认实例化成功(排除类未被创建的问题)
on_train_batch_end 没触发:确认 batch_size 和 steps_per_epoch 设置
on_train_batch_end 在每个 batch 训练完后调用,但如果用了 steps_per_epoch 且值过小(如 1),可能只运行一次就结束 epoch;更隐蔽的是——如果你的数据是 tf.data.Dataset 且没设置 batch_size,或用了 dataset.batch(..., drop_remainder=True) 导致实际 batch 数为 0,那这个钩子根本不会进入。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 打印
len(train_dataset)和train_dataset.cardinality().numpy(),确认数据集非空且可计数 - 用
for x, y in train_dataset.take(1): print(x.shape)验证 batch 是否正常产出 - 若手动指定
steps_per_epoch,确保它 ≤ 实际 batch 总数,否则训练提前终止,回调自然不执行
自定义回调继承错误:必须继承 tensorflow.keras.callbacks.Callback
很多人直接写 class MyCallback:(没继承),或错继承成 tf.keras.callbacks.BaseLogger 等非回调基类。TensorFlow 只识别显式继承自 tensorflow.keras.callbacks.Callback 的类,并依赖其内部的 set_model、set_params 等方法注入上下文。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 务必写
class MyCallback(tf.keras.callbacks.Callback):(注意是tf.keras,不是keras独立包,二者 API 不完全兼容) - 不要重写
__init__却忘了调用super().__init__()(虽然不是强制,但部分内部状态依赖父类初始化) - 调试时可在
on_train_begin中打印self.model,若为None,说明继承或注册环节出问题
回调中修改模型状态失败:避免在 on_batch_end 里直接改 model.layers[i].trainable
这类操作看似合理,但 TensorFlow 2.x 的 eager mode 下,model.layers[i].trainable = False 不会立即生效——权重更新逻辑在 optimizer.step 前已固化,该修改仅影响后续 epoch 或 compile 后的行为。结果就是你“以为”冻结了某层,但梯度仍在反向传播。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 如需动态冻结/解冻,应在
on_epoch_begin中修改layer.trainable,然后调用model.compile(...)重建计算图(注意:这会重置 optimizer 状态) - 更稳妥的做法是用
tf.GradientTape手动控制 trainable_variables,在训练 step 内过滤变量,而非依赖 layer.trainable 标志 - 所有涉及
model.trainable_weights的读取,务必放在on_train_batch_end之后(例如在on_train_end中检查),因为训练过程中该属性可能滞后更新
__init__ 打印有输出 → 再看 on_train_begin 是否执行 → 最后盯住 on_train_batch_end 的 batch index 是否递增。别跳步。


















