双DQN(DDQN)能够缓解标准Q学习和DQN中固有的过高估计偏差。从标准DQN实现过渡到DDQN,只需要对损失函数中目标Q值的计算方式进行一个虽小但能带来显著改变的修改。回顾一下标准DQN对状态转移 $(s, a, r, s', d)$ 的目标计算(其中 $d$ 表示 $s'$ 是否为终止状态):从经验回放缓冲区中第 $i$ 个样本的目标 $y_i$ 为: $$ y_i = r_i + \gamma \max_{a'} Q_{target}(s'_i, a') \quad \text{(如果 } s'_i \text{ 不是终止状态)} $$ $$ y_i = r_i \quad \text{(如果 } s'i \text{ 是终止状态)} $$ 在这里,目标网络 $Q{target}$ 既用于选择最佳的下一步动作($a'$),也用于评估该动作的价值。这种耦合关系可能导致过高估计。双DQN的修改双DQN将此过程解耦。它使用在线网络($Q_{online}$)在下一个状态 $s'i$ 中选择最佳动作,然后使用目标网络($Q{target}$)来评估该所选动作的价值。双DQN目标 $y_i$ 的计算变为: $$ a'{max} = \arg\max{a'} Q_{online}(s'i, a') $$ $$ y_i = r_i + \gamma Q{target}(s'i, a'{max}) \quad \text{(如果 } s'_i \text{ 不是终止状态)} $$ $$ y_i = r_i \quad \text{(如果 } s'i \text{ 是终止状态)} $$ 请注意这个变化:我们首先根据在线网络在状态 $s'i$ 中找到使Q值最大化的动作 $a'{max}$。然后,我们将这个特定的动作 $a'{max}$ 代入目标网络,以获得目标计算所需的Q值估计。实现修改假设您有一个标准的DQN实现,可能包含一个处理从回放缓冲区采样的批次经验的 learn 或 compute_loss 方法。您需要修改计算目标Q值的部分。以下是目标计算代码片段的对比分析(假设 online_net 和 target_net 是您的网络模型,并且 next_states、rewards、dones 是从采样批次中获取的张量/数组):标准DQN目标计算(代码片段):# 假设 next_states 是从经验回放缓冲区中获取的下一个状态的批次 # 从目标网络获取下一个状态的Q值 next_q_values_target = target_net(next_states) # 为每个下一个状态选择最大的Q值 max_next_q_values = next_q_values_target.max(dim=1)[0] # 或在NumPy/TF中使用 axis=1 # 计算目标 y_i(处理 dones=True 的终止状态) target_q_values = rewards + gamma * max_next_q_values * (1 - dones)双DQN目标计算(代码片段):# 假设 next_states, rewards, dones 是从经验回放缓冲区中获取的批次数据 # 1. 使用*在线*网络在下一个状态中选择最佳动作 next_q_values_online = online_net(next_states) best_next_actions = next_q_values_online.argmax(dim=1) # 或 axis=1 # 2. 使用*目标*网络评估这些选定的动作 # 从目标网络获取下一个状态的所有Q值 next_q_values_target = target_net(next_states) # 选择与 best_next_actions 对应的Q值 # 需要仔细的索引(例如,PyTorch/TF 中的 gather) q_values_of_best_actions = next_q_values_target.gather(1, best_next_actions.unsqueeze(-1)).squeeze(-1) # 3. 计算目标 y_i(处理 dones=True 的终止状态) target_q_values = rewards + gamma * q_values_of_best_actions * (1 - dones)核心变化涉及以下步骤:使用 online_net 对 next_states 执行前向传播,以找到 argmax 动作($a'_{max}$)。使用 target_net 对 next_states 执行前向传播,以获取所有可能下一步动作的Q值。从步骤2中选择与步骤1中选定动作对应的Q值。这需要仔细的张量索引(例如,PyTorch中的 gather 或 TensorFlow中的 tf.gather_nd)。使用这些选定的Q值来计算最终目标 $y_i$。您的DQN代码其余部分,包括经验回放机制、从在线网络权重定期更新目标网络权重、优化器步骤以及环境交互过程中的epsilon-贪婪动作选择,通常保持不变。digraph DDQN_Target { rankdir=LR; node [shape=box, style=rounded, fontname="sans-serif", margin=0.2]; edge [fontname="sans-serif"]; subgraph cluster_online { label = "在线网络 (Q_online)"; bgcolor="#e9ecef"; style=filled; s_prime [label="下一状态 (s')"]; q_online_s_prime [label="所有 a' 的 Q_online(s', a')\n", shape=ellipse, style=filled, fillcolor="#a5d8ff"]; argmax [label="argmax_a'", shape=diamond, style=filled, fillcolor="#ffe066"]; a_max [label="最佳动作 (a'_max)", shape=ellipse, style=filled, fillcolor="#ffd8a8"]; s_prime -> q_online_s_prime; q_online_s_prime -> argmax; argmax -> a_max; } subgraph cluster_target { label = "目标网络 (Q_target)"; bgcolor="#e9ecef"; style=filled; s_prime_target [label="下一状态 (s')"]; // 视觉上需要一个单独的节点实例 q_target_s_prime [label="所有 a' 的 Q_target(s', a')\n", shape=ellipse, style=filled, fillcolor="#a5d8ff"]; q_target_eval [label="选择 Q_target(s', a'_max)", shape=diamond, style=filled, fillcolor="#96f2d7"]; final_q [label="Q_target(s', a'_max)", shape=ellipse, style=filled, fillcolor="#b2f2bb"]; s_prime_target -> q_target_s_prime; q_target_s_prime -> q_target_eval; q_target_eval -> final_q; } reward [label="奖励 (r)", shape=ellipse, style=filled, fillcolor="#ffc9c9"]; gamma [label="折扣因子 (\u03b3)", shape=ellipse, style=filled, fillcolor="#bac8ff"]; adder [label="+", shape=circle, style=filled, fillcolor="#ced4da"]; multiplier [label="*", shape=circle, style=filled, fillcolor="#ced4da"]; target_y [label="目标值 (y)", shape= Mdiamond, style=filled, fillcolor="#fcc2d7"]; // 连接 a_max -> q_target_eval [label="使用所选动作"]; final_q -> multiplier; gamma -> multiplier; multiplier -> adder; reward -> adder; adder -> target_y; // 如有需要,用于对齐的隐形边(可能很复杂) s_prime -> s_prime_target [style=invis]; }双DQN中目标Q值组件($\gamma Q_{target}(s', \arg\max_{a'} Q_{online}(s', a'))$)的计算示意图。在线网络选择最佳动作,目标网络评估该特定动作的价值。根据上述代码片段和图表修改您现有的DQN智能体代码。测试您的实现,例如再次在CartPole环境或更复杂的Atari环境(如果您正在使用这些)中进行测试。观察训练是否显得更稳定,或者与您的标准DQN实现相比,智能体是否获得更好的性能,请记住结果可能因超参数和环境细节而异。这种亲自动手的修改提供了改进DQN算法的直接经验。