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
相关产品推荐
相关产品推荐

