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

Keras自定义GAN梯度报错:未提供变量梯度的技术求助

问题:非标准GAN训练时出现梯度为空报错

本人有Keras神经网络开发经验,正在构建一款非标准GAN——用预训练人脸图像质量判别器替代可训练判别器,实现48×48到96×96的图像超分辨率提升。之前用常规GAN做简单插值导致画质损失,所以改用预训练判别器方案,但训练时一直报ValueError: No gradients provided for any variable,报错指向generator_optimizer.apply_gradients(...)这一行,求解决办法。


训练步骤函数

@tf.function
def train_step(images_x, images_y):

  with tf.GradientTape() as gen_tape:
    generated_images = generator(images_x, training=True).numpy()

    # 集成预训练网络,返回形状为(batch_size,1)的numpy数组,值范围0-1
    fake_output = K.constant(face_detector.estimate(generated_images)) # K = tensorflow backend

    gen_loss = generator_loss(fake_output)

  gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)

  generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))

  return gen_loss

通用训练函数

def train(dataset_x, dataset_y, epochs):
  history = []
  MAX_PRINT_LABEL = 10
  th = BUFFER_SIZE // (BATCH_SIZE * MAX_PRINT_LABEL)

  for epoch in range(1, epochs + 1):
    print(f'{epoch}/{EPOCHS}: ', end='')

    start = time.time()
    n = 0

    gen_loss_epoch = 0
    l = len(dataset_x)
    for o in range(l):
      tf.config.run_functions_eagerly(True) # 怀疑问题出在这里,但不用急切执行train_step就跑不通
      gen_loss = train_step(dataset_x[o], dataset_y[o]) 
      tf.config.run_functions_eagerly(False)
      print(gen_loss, disc_loss)
      gen_loss_epoch += K.mean(gen_loss)
      if (n % th == 0): print('=', end='')
      n += 1
    generator.save("generator.h5")


    history += [gen_loss_epoch / n]
    

  return history

生成器结构

cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)

hidden_dim = 2


def dropout_and_batch():
  return Dropout(0.3)(BatchNormalization())

img_height = 96
img_width = 96
img_channels = 3  
latent_dim = 2  
input_img = Input(shape=(48, 48, 3))
x = Conv2D(32, 3, padding='same', activation='relu')(input_img)
x = Conv2D(32, 3, padding='same', activation='relu')(x)
x = Conv2D(32, 3, padding='same', activation='relu')(x)

x = Flatten()(input_img)
z_mean = Dense(latent_dim)(x)
z_log_var = Dense(latent_dim)(x)
@keras.saving.register_keras_serializable()
def sampling(args):
    z_mean, z_log_var = args
    epsilon = tf.random.normal([batch_size, latent_dim], 0, 1, tf.float32)
    return z_mean + tf.exp(0.5 * z_log_var) * epsilon 
z = Lambda(sampling, output_shape=(latent_dim,))([z_mean, 
 z_log_var])


decoder_input = Input(shape=(latent_dim,))
x = Dense(img_width*img_height*3, activation='relu')(decoder_input)
x = Reshape((img_width, img_height, 3))(x)
x = Conv2D(32, 3, padding='same', activation='relu')(x)
x = Conv2D(32, 3, padding='same', activation='relu')(x)
decoded = Conv2D(3, 3, padding='same', activation='sigmoid')(x)

encoder = Model(input_img, [z_mean, z_log_var, z])
decoder = Model(decoder_input, decoded)

generator = Model(input_img, decoder(encoder(input_img)[2]))

自定义损失函数

def generator_loss(fake_output):
  loss = cross_entropy(tf.ones_like(fake_output), fake_output)
  return loss

数据集加载代码

for path in glob("faces/*.jpg"):
  img = Image.open(path)
  y_train.append(np.asarray(img.resize((96, 96))))
  if y_train[-1].shape != (96, 96, 3):
    del y_train[-1]
    continue
  x_train.append(np.asarray(img.resize((48, 48))))
x_train = np.asarray(x_train)/255
y_train = np.asarray(y_train)/255

BUFFER_SIZE = x_train.shape[0]
BATCH_SIZE = 100

BUFFER_SIZE = BUFFER_SIZE // BATCH_SIZE * BATCH_SIZE
x_train = x_train[:BUFFER_SIZE]
y_train = y_train[:BUFFER_SIZE]
print(x_train.shape, y_train.shape)


train_dataset_x = list(tf.data.Dataset.from_tensor_slices(x_train).shuffle(BUFFER_SIZE).batch(BATCH_SIZE))
train_dataset_y = list(tf.data.Dataset.from_tensor_slices(y_train).shuffle(BUFFER_SIZE).batch(BATCH_SIZE))

问题原因与解决方案

核心原因

报错本质是TensorFlow的自动微分计算链被打断,导致梯度无法回溯到生成器的可训练变量,具体问题点:

  1. .numpy()转换切断梯度链:generated_images = generator(...).numpy()将Tensor转为numpy数组,破坏了计算图的微分追踪,后续所有操作都无法关联到生成器变量。
  2. K.constant()固化输出:把预训练模型的输出转为常量Tensor,同样无法参与梯度传导。
  3. 频繁切换急切执行模式:训练循环中反复开关tf.config.run_functions_eagerly(),打乱了计算图的稳定构建。

修正步骤

1. 修复train_step,保留Tensor流

去掉.numpy()和K.constant(),确保所有操作在TensorFlow计算图内进行。如果预训练模型只能接受numpy输入,先转换但要立刻转回Tensor:

@tf.function
def train_step(images_x, images_y):
  with tf.GradientTape() as gen_tape:
    # 保留Tensor,不转numpy
    generated_images = generator(images_x, training=True)
    # 若预训练模型仅支持numpy,先临时转换,再转回Tensor
    fake_output_np = face_detector.estimate(generated_images.numpy())
    fake_output = tf.convert_to_tensor(fake_output_np, dtype=tf.float32)
    # 可选:添加像素级MSE损失,提升训练稳定性
    mse_loss = tf.keras.losses.MeanSquaredError()(images_y, generated_images)
    gen_loss = generator_loss(fake_output) + 0.01 * mse_loss

  gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
  # 增加空梯度判断,避免报错
  if gradients_of_generator:
    generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
  
  return gen_loss

如果预训练判别器是Keras模型,直接用Tensor输入:fake_output = face_detector(generated_images, training=False),完全保留梯度链。

2. 移除急切执行的频繁切换

删除训练循环中的tf.config.run_functions_eagerly()切换,要么全程用图模式(@tf.function),要么在训练函数开头设置一次急切执行:

def train(dataset_x, dataset_y, epochs):
  history = []
  MAX_PRINT_LABEL = 10
  th = BUFFER_SIZE // (BATCH_SIZE * MAX_PRINT_LABEL)
  # 可选:若仍需急切执行,仅设置一次
  # tf.config.run_functions_eagerly(True)

  for epoch in range(1, epochs + 1):
    print(f'{epoch}/{EPOCHS}: ', end='')
    start = time.time()
    n = 0
    gen_loss_epoch = 0
    l = len(dataset_x)
    for o in range(l):
      gen_loss = train_step(dataset_x[o], dataset_y[o]) 
      # 删除未定义的disc_loss打印
      print(gen_loss)
      gen_loss_epoch += tf.reduce_mean(gen_loss)
      if (n % th == 0): print('=', end='')
      n += 1
    generator.save("generator.h5")

    history.append(gen_loss_epoch / n)
    print(f' | 耗时: {time.time()-start:.2f}s')

  return history

3. 验证生成器可训练变量

打印generator.trainable_variables确认变量存在,若有不可训练层,检查是否是故意设置的:

print("生成器可训练变量数量:", len(generator.trainable_variables))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 07:58:14