基于TensorFlow的深度RL多动作模型训练梯度异常问题求助
问题分析
- 原损失函数逻辑错误:当前损失用
-tf.reduce_mean(predicted_action * reward),但reward是前序动作的反馈,和当前预测动作无因果关系,梯度方向完全错误,无法引导网络优化到能获得高奖励的动作。 - 梯度为空的原因:如果直接用前序
action计算损失,损失值和actor网络的predicted_action没有关联,TensorFlow的梯度带(GradientTape)无法追踪到actor变量的依赖关系,所以返回空梯度。
解决方案(基于REINFORCE策略梯度)
针对连续动作空间的RL任务,你需要用策略梯度算法(比如REINFORCE),核心是让网络学习“执行某个动作后获得高奖励,就提升这个动作被选中的概率”。具体修正步骤如下:
1. 调整Actor网络的输出(适配连续动作)
因为你的动作是二维连续向量,通常actor网络需要输出动作的均值(可选加上标准差,固定或可学习),用高斯分布来采样动作,这样才能计算动作的对数概率(用于策略梯度损失)。
示例Actor网络结构(假设你用LSTM):
class Actor(tf.keras.Model): def __init__(self): super().__init__() self.lstm1 = tf.keras.layers.LSTM(64, return_sequences=True) self.lstm2 = tf.keras.layers.LSTM(64) self.dense = tf.keras.layers.Dense(2) # 输出二维动作均值 def call(self, inputs): x = self.lstm1(inputs) x = self.lstm2(x) action_mean = tf.nn.sigmoid(self.dense(x)) # 假设动作范围是[0,1],用sigmoid约束 return action_mean
2. 保存完整轨迹数据
训练前需要收集每个episode的轨迹:(state_t, action_t, reward_t),最后计算每个步骤的折扣累积奖励G_t(即从t到episode结束的所有奖励按折扣因子γ加权求和)。
添加缓冲区存储轨迹:
def __init__(self): # 其他初始化代码... self.state_buffer = [] self.action_buffer = [] self.reward_buffer = [] self.gamma = 0.95 # 折扣因子,可调整
环境交互时存入数据:
# 示例交互逻辑 state = env.reset() done = False while not done: action_mean = self.actor(tf.convert_to_tensor([state], dtype=tf.float32)) # 从高斯分布采样动作(添加小噪声保证探索) action = tf.random.normal(shape=action_mean.shape, mean=action_mean, stddev=0.1) action = tf.clip_by_value(action, 0.0, 1.0) # 约束动作在合理范围 next_state, reward, done, _ = env.step(action.numpy()[0]) # 存入缓冲区 self.state_buffer.append(state) self.action_buffer.append(action.numpy()[0]) self.reward_buffer.append(reward) state = next_state
3. 计算折扣累积奖励
每个episode结束后,计算每个步骤的G_t:
def compute_discounted_rewards(self): discounted_rewards = [] running_add = 0 # 从后往前计算折扣奖励 for r in reversed(self.reward_buffer): running_add = r + self.gamma * running_add discounted_rewards.insert(0, running_add) # 归一化奖励(稳定训练) discounted_rewards = tf.convert_to_tensor(discounted_rewards, dtype=tf.float32) discounted_rewards = (discounted_rewards - tf.reduce_mean(discounted_rewards)) / (tf.math.reduce_std(discounted_rewards) + 1e-8) return discounted_rewards
4. 修正训练函数
基于轨迹数据计算策略梯度损失:损失 = -E[ logπ(a|s) * G ],其中logπ(a|s)是动作a在状态s下的对数概率,G是折扣累积奖励。
def train_step(self): # 计算折扣累积奖励 discounted_rewards = self.compute_discounted_rewards() states = tf.convert_to_tensor(self.state_buffer, dtype=tf.float32) actions = tf.convert_to_tensor(self.action_buffer, dtype=tf.float32) with tf.GradientTape() as tape: action_means = self.actor(states, training=True) # 计算动作的对数概率(假设动作服从高斯分布,标准差固定为0.1) log_probs = -0.5 * tf.reduce_sum(tf.square((actions - action_means)/0.1) + tf.math.log(2 * tf.constant(math.pi)) + tf.math.log(tf.square(0.1)), axis=1) # 策略梯度损失 loss = -tf.reduce_mean(log_probs * discounted_rewards) # 计算梯度并更新网络 grads = tape.gradient(loss, self.actor.trainable_variables) self.optimizer.apply_gradients(zip(grads, self.actor.trainable_variables)) # 清空缓冲区 self.state_buffer.clear() self.action_buffer.clear() self.reward_buffer.clear() # 更新探索噪声(可选,比如逐渐减小标准差) # self.epsilon = self.epsilon * self.epsilon_dec
5. 修复奖励函数的小问题
移除冗余代码并确保返回奖励值:
def transform_actions_to_reward(actions, state, agent, tipo, iteration): actions = actions[0] energy = state[0] price = state[1] battery_charge = state[2] percentage_stored = actions[0] percentage_empty = actions[1] energy_stored = energy * percentage_stored energy_iny = energy * (1 - percentage_stored) battery_iny = agent.battery * percentage_empty agent.fill_bat(energy_stored) if price == 1: reward = energy_iny + battery_iny elif price == 0: reward = -(energy_iny + battery_iny) return reward # 必须返回奖励值
关键注意点
- 连续动作空间下,不能直接用动作值乘奖励,必须用策略的对数概率来关联动作和奖励,这样梯度才能正确传递到actor网络的参数。
- 折扣累积奖励的归一化能大幅提升训练稳定性,避免奖励波动导致梯度爆炸/消失。
- 探索机制:连续动作通常用高斯噪声添加到均值上,而不是epsilon-greedy(后者更适合离散动作),可以随着训练逐渐减小噪声标准差来降低探索。
内容的提问来源于stack exchange,提问作者gustavo lobos astorquiza
相关产品推荐
相关产品推荐

