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

TensorFlow移植DDQN+CNN代码时梯度返回None、优化器失效求助

问题:TensorFlow实现DDQN+CNN时梯度全为None的报错

报错信息

ValueError: No gradients provided for any variable['conv2d_66/kernel:0', 'conv2d_66/bias:0',...]

梯度始终返回[None, None..., None]

问题背景

参考PyTorch示例,用TensorFlow结合DDQN与CNN实现强化学习,移植时重构梯度相关代码后出现上述问题。

关键代码问题分析

1. 模型初始化逻辑错误

net_init函数每次调用都会重新创建self.predict和self.target模型,导致之前的模型参数被覆盖,梯度追踪完全失效——每次训练都是在新模型上操作,根本无法积累梯度。

2. 训练步骤完全脱离模型参数

train_step函数中,损失直接计算传入的states和actions的Huber损失,完全没有用到self.predict模型的输出,损失与模型参数无任何关联,自然无法计算梯度。

3. 计算图被numpy操作打断

train函数中使用.numpy()将张量转为numpy数组,脱离了TensorFlow的计算图,导致后续损失计算与模型参数的关联断裂,梯度无法传递。

解决建议

1. 重构模型初始化逻辑

将模型创建移到类的__init__方法中,net_init仅负责返回对应模型的输出:

def __init__(self, input_dim, output_dim, ...):
    self.input_dim = input_dim
    self.output_dim = output_dim
    # 初始化预测模型
    c, h, w = self.input_dim
    self.predict = Sequential([
        tf.keras.layers.Conv2D(32,8,4,activation='relu',input_shape=(c, h, w),data_format="channels_first"),
        tf.keras.layers.Conv2D(64, 4, 2, activation='relu',padding="VALID"),
        tf.keras.layers.Conv2D(64, 3, 1, activation='relu'),
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(512, activation='relu'),
        tf.keras.layers.Dense(self.output_dim, activation=None)
    ])
    # 初始化目标模型
    self.target = Sequential([
        tf.keras.layers.Conv2D(32, 8, 4, activation='relu', input_shape=(4, 84, 84), data_format="channels_first"),
        tf.keras.layers.Conv2D(64, 4, 2, activation='relu', padding="VALID"),
        tf.keras.layers.Conv2D(64, 3, 1, activation='relu'),
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(512, activation='relu'),
        tf.keras.layers.Dense(self.output_dim, activation=None)
    ])

def net_init(self, state, modelIndex):
    if modelIndex == 1:
        return self.predict(state)
    elif modelIndex == 2:
        return self.target(state)

2. 修正训练步骤,关联模型参数

修改train_step,让损失基于模型预测值与目标Q值计算:

@tf.function
def train_step(self, states, target_qs, actions):
    with tf.GradientTape() as tape:
        # 用预测模型得到当前状态的Q值
        current_qs = self.predict(states)
        # 提取对应动作的Q值
        action_mask = tf.one_hot(actions, self.output_dim)
        current_action_qs = tf.reduce_sum(current_qs * action_mask, axis=1)
        # 计算损失
        loss = tf.keras.losses.huber(current_action_qs, target_qs)
    
    # 计算模型参数的梯度
    gradients = tape.gradient(loss, self.predict.trainable_variables)
    # 梯度裁剪(可选)
    gradients = [tf.clip_by_norm(grad, 10) for grad in gradients if grad is not None]
    # 应用梯度更新
    self.optimizer.apply_gradients(zip(gradients, self.predict.trainable_variables))
    return loss

3. 保持计算图连续性,避免numpy操作

修改train函数中的目标Q值计算,用TensorFlow原生操作代替numpy:

def train(self):
    # ... 原有逻辑 ...
    state, next_state, action, reward, done = self.recall()
    # 获取当前动作的Q值
    current_qs = self.predict(state)
    action_mask = tf.one_hot(action, self.output_dim)
    current_action_qs = tf.reduce_sum(current_qs * action_mask, axis=1)
    # 获取下一状态的最佳动作(用预测模型)
    next_action_qs = self.predict(next_state)
    best_action = tf.argmax(next_action_qs, axis=1)
    # 获取目标模型的下一状态Q值
    next_target_qs = self.target(next_state)
    # 提取最佳动作对应的Q值
    indices = tf.stack([tf.range(tf.shape(best_action)[0]), best_action], axis=1)
    next_Q_1 = tf.gather_nd(next_target_qs, indices)
    # 计算目标Q值
    next_Q_2 = reward + (1 - done) * self.gamma * next_Q_1
    # 执行训练步骤
    loss = self.train_step(state, next_Q_2, action)
    return (int(current_action_qs.numpy().mean()), loss)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 13:13:12