You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Keras模型call未定义training参数为何传training=True不报错

问题描述

在基于TensorFlow Keras实现自定义模型时存在如下现象:

  • 创建继承Model类的自定义模型时,重写的call方法仅定义了x、something=False两个入参,未定义tf.keras.Model默认call方法中存在的training参数
  • 在train_step训练函数中调用模型实例时,传入了something=True、training=True参数
  • 代码执行过程中未抛出任何参数不匹配错误,运行时输出了大量my call打印内容

复现问题的完整代码如下:

import tensorflow as tf
from tensorflow.keras import Model, Flatten, Dense
mnist = tf.keras.datasets.mnist

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

# Add a channels dimension
x_train = x_train[..., tf.newaxis].astype("float32")
x_test = x_test[..., tf.newaxis].astype("float32")

train_ds = tf.data.Dataset.from_tensor_slices(
    (x_train, y_train)).shuffle(10000).batch(32)

class MyModel(Model):
  def __init__(self):
    super(MyModel, self).__init__()
    self.fl = Flatten()
    self.d = Dense(10)
  
  ######My problem#######
  def call(self, x, something=False):
    if something:
      tf.print('my call')
    x = self.fl(x)
    return self.d(x)


model = MyModel()
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
optimizer = tf.keras.optimizers.Adam()

@tf.function
def train_step(X,Y):
  with tf.GradientTape() as tape:
    ######My problem#######
    predictions = model(X, something =True, training = True)
    loss = loss_object(Y, predictions)
  gradients = tape.gradient(loss, model.trainable_variables)
  optimizer.apply_gradients(zip(gradients, model.trainable_variables))

for epoch in range(3):

  for X,Y in train_ds:
    train_step(X,Y)
原因解释

出现这个现象的核心逻辑来自Keras模型调用的分层设计,和Python原生的方法参数传递规则无关:

  • 写model(...)调用模型实例时,首先触发的不是你重写的call方法,而是父类Model实现的__call__入口方法。这个入口方法承担了前向传播前后的框架层通用逻辑:比如开启/关闭自动求导上下文、设置训练/推理全局状态、处理mask、调用层的build逻辑自动构建权重、执行提前注册的回调钩子等。
  • __call__方法会对传入的关键字参数做分流处理:
    • 框架内置的保留参数(training、mask等)会被__call__直接消费,用来设置全局执行状态,控制Dropout、BatchNormalization这类存在训练/推理行为差异的层的执行逻辑,这部分参数不会被透传到你自定义的call方法中
    • 不在保留参数列表里的自定义关键字参数(比如代码里的something),才会被透传给你重写的call方法
  • 因为传入的training=True在入口层就被拦截处理了,根本没有传递到自定义的call方法,自然不会触发Python的"未定义关键字参数"报错。
  • 运行时大量打印my call属于符合代码逻辑的正常表现:something=True作为自定义参数被成功透传到call方法中,触发了tf.print逻辑;训练时每个batch都会执行一次前向传播,每遍历一个batch就会触发一次打印,3轮epoch遍历6万张MNIST样本(batch size为32时单轮约1875个batch),自然会产生大量打印输出。

注意:如果需要在自定义call方法内部根据训练/推理状态写分支逻辑,必须显式在call的入参列表中加入training参数,框架不会自动把这个参数注入到你的call方法作用域里。

内容的提问来源于stack exchange,提问作者ml alltheway

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 20:42:17