直接继承 keras.callbacks.callback 并重写 on_train_begin、on_batch_end、on_epoch_end 等生命周期方法即可;keras 自动调用,参数固定,logs 内容依阶段而异,训练时 batch 级无 validation 指标,epoch 级才有;可通过 self.model 访问模型与优化器,修改学习率需用 assign,stop_training 需在正确时机设置。

怎么继承keras.callbacks.Callback写自己的回调类
直接继承 keras.callbacks.Callback,重写其中的生命周期方法即可。Keras 在训练/验证/预测各阶段会自动调用这些方法,不需要手动触发。
最常用的是 on_train_begin、on_batch_end、on_epoch_end 和 on_train_end。注意:所有方法接收的参数都固定,比如 on_batch_end 一定带 batch 和 logs(字典,含当前 batch 的 loss、metrics 等)。
示例:一个简单打印 batch loss 的回调:
class PrintBatchLoss(keras.callbacks.Callback):
def on_batch_end(self, batch, logs=None):
if batch % 10 == 0:
print(f"Batch {batch}: loss = {logs.get('loss'):.4f}")
使用时传入 model.fit(..., callbacks=[PrintBatchLoss()]) 即可。
logs 字典里到底有哪些键?训练时能取到 validation 指标吗
logs 的内容取决于所处阶段和模型配置。它不是固定集合,而是由 Keras 动态注入的——比如 on_batch_end 的 logs 通常只有 'loss' 和你定义的训练 metrics(如 'accuracy'),但**没有 validation 数据**;而 on_epoch_end 的 logs 才可能包含 'val_loss'、'val_accuracy' 等,前提是 fit 中设置了 validation_data 或 validation_split。
常见误区是想在 on_batch_end 里读 logs['val_loss'] —— 这会报 KeyError,因为 validation 是按 epoch 计算的,不参与 batch 级更新。
建议做法:
- 查键前先用
logs.keys()打印确认(调试时加一行print(list(logs.keys()))) - 取值统一用
logs.get('xxx'),避免 KeyError - 需要跨阶段访问(比如存上个 epoch 的 val_loss),自己用
self属性缓存
如何在回调里访问模型、优化器或自定义变量
回调实例在训练开始前就被传入 model.fit,Keras 会在 on_train_begin 之前把模型对象赋给 self.model,所以你随时可以通过 self.model 拿到整个模型(包括权重、层、optimizer)。
例如想在每个 epoch 结束时保存学习率:
class LrLogger(keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs=None):
lr = self.model.optimizer.learning_rate.numpy()
print(f"Epoch {epoch}: LR = {lr:.6f}")
注意点:
-
self.model.optimizer是 optimizer 实例,其学习率可能是标量(tf.Variable)或调度器(tf.keras.optimizers.schedules.LearningRateSchedule),调用.numpy()前先判断类型 - 不要在回调里修改
self.model结构(比如 add layer),会破坏图构建;但可以安全调用self.model.trainable_variables或执行self.model.predict() - 如果需访问自定义张量(如某个中间层输出),得提前用
tf.keras.Model构建子模型,不能临时从self.model.layers[i]取输出
为什么回调里改 learning rate 没生效?或者 model.stop_training = True 不起作用
这两个问题本质都是时机错位。model.stop_training = True 必须在 on_batch_end 或 on_epoch_end 中设置,且 Keras 只在该方法返回后检查一次标志位——如果设在 on_train_begin 或 on_epoch_begin,它不会中断当前 epoch。
学习率更新失败更常见于以下情况:
- 用的是
tf.keras.optimizers.Adam等内置优化器,但直接赋值opt.learning_rate = new_lr—— 这样只改了 Python 对象属性,没更新底层tf.Variable;正确做法是opt.learning_rate.assign(new_lr) - 用了学习率调度器(如
ExponentialDecay),却试图绕过它手动 assign,会被调度器下个 step 覆盖 - 在
on_batch_end中频繁 assign,导致训练不稳定(尤其小 batch size 时),建议限制频率(如每 50 batch 一次)
真正要干预训练流程,优先考虑用 Keras 内置机制(如 ReduceLROnPlateau、EarlyStopping),自定义回调更适合监控、日志、轻量干预。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











