趋近智
为深度Q网络(DQN)及双DQN(DDQN)、对决网络架构等几项重要改进提供实用实现指导和代码示例。这些实现的起点是标准的DQN。假定您熟悉Python、深度学习 (deep learning)库(如PyTorch或TensorFlow),并能够与强化学习 (reinforcement learning)环境(例如,使用Gymnasium)进行交互。
我们的目的并非提供一个完整、优化、即插即用的库,而是说明实现DDQN和对决DQN所需的具体代码修改,从DQN代理结构开始。
在为DDQN或对决DQN进行修改之前,让我们回顾典型DQN代理的必要组成部分:
torch.nn.Module或tf.keras.Model),它接收状态表示作为输入,并为每个可能动作输出Q值。$(s, a, r, s', d)$(状态、动作、奖励、下一状态、完成标记 (token))的数据结构(通常是双端队列)。act(state): 根据当前状态选择动作,使用epsilon-greedy策略,参照Q网络。step(state, action, reward, next_state, done): 将转换存储在回放缓冲区中,并可能触发学习更新。learn(): 从回放缓冲区采样一批转换,计算目标Q值,计算目标Q值和预测Q值之间的损失(例如,MSE或Huber损失),并在Q网络上执行梯度下降 (gradient descent)步骤。定期更新目标网络。双DQN的核心思想是减少标准Q学习和DQN中存在的过高估计偏差。回顾标准DQN对转换的目标计算:
这里,表示目标网络。max运算符使用目标网络既选择最佳的下一动作,又评估该动作的价值。DDQN将此解耦:
在线网络 () 用于为下一状态选择最佳动作 ,但目标网络 () 用于评估在状态中执行该动作的Q值。
代码修改:
修改主要发生在learn方法中,具体来说,是在计算采样批次的目标Q值处。
# 假设:
# - states, actions, rewards, next_states, dones 是从回放缓冲区采样的批次
# - q_network 是在线网络 (theta)
# - target_q_network 是目标网络 (theta_minus)
# - gamma 是折扣因子
# - 为便于说明,使用类似PyTorch的语法
# 1. 从在线网络获取下一状态的Q值
with torch.no_grad(): # 此处无需追踪梯度
q_next_online = q_network(next_states) # 形状: (batch_size, 动作数量)
# 2. 使用在线网络选择下一状态的最佳动作
best_actions_next = torch.argmax(q_next_online, dim=1).unsqueeze(-1) # 形状: (batch_size, 1)
# 3. 从目标网络获取下一状态的Q值
with torch.no_grad():
q_next_target = target_q_network(next_states) # 形状: (batch_size, 动作数量)
# 4. 从目标网络中选择与在线网络选择的最佳动作相对应的Q值
# 使用 gather() 根据 best_actions_next 索引选择Q值
q_target_next = torch.gather(q_next_target, dim=1, index=best_actions_next) # 形状: (batch_size, 1)
# 5. 计算DDQN目标值
# 处理终止状态(其中 next_state 为 None 或 done=True)
# dones 张量对于非终止状态通常为0,对于终止状态为1
target_q_values = rewards.unsqueeze(-1) + (gamma * q_target_next * (1 - dones.unsqueeze(-1)))
# --- 学习步骤的其余部分如下 ---
# 6. 从在线网络获取已采取动作的当前Q值
current_q_values = torch.gather(q_network(states), dim=1, index=actions.unsqueeze(-1))
# 7. 计算损失 (例如, MSE损失)
loss = F.mse_loss(current_q_values, target_q_values)
# 8. 执行梯度下降
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 9. 更新目标网络(定期或通过Polyak平均)
# ...
此片段显示了重要差异:使用q_network查找best_actions_next,然后使用target_q_network获取与这些动作相关的值q_target_next。
对决网络架构分离了状态值函数和动作优势函数的估算。其想法是,有时状态的价值与所采取的动作无关,并且网络应该能够表示这一点。这些被组合以生成最终Q值。
一种常见表述为:
其中表示共享层的参数 (parameter),表示值流,表示优势流。减去平均优势确保了可识别性并提升了稳定性。另一种方法是减去最大优势:
代码修改:
主要修改在于Q网络模型的定义。您需要修改其架构,使其从一个共同的特征表示层分出两个输出流(值流和优势流)。
# 使用 PyTorch nn.Module 的示例
import torch
import torch.nn as nn
import torch.nn.functional as F
class DuelingQNetwork(nn.Module):
def __init__(self, state_size, action_size, hidden_units=[64, 64]):
super(DuelingQNetwork, self).__init__()
self.action_size = action_size
# 共享层
self.fc1 = nn.Linear(state_size, hidden_units[0])
self.fc2 = nn.Linear(hidden_units[0], hidden_units[1])
# 值流
self.value_stream = nn.Linear(hidden_units[1], 1)
# 优势流
self.advantage_stream = nn.Linear(hidden_units[1], action_size)
def forward(self, state):
x = F.relu(self.fc1(state))
x = F.relu(self.fc2(x))
# 计算值 V(s)
value = self.value_stream(x) # 形状: (batch_size, 1)
# 计算优势 A(s, a)
advantages = self.advantage_stream(x) # 形状: (batch_size, 动作数量)
# 使用均值减法方法组合 V(s) 和 A(s, a)
# 注意: 保持维度一致以进行广播
# value 形状: (batch_size, 1)
# advantages 形状: (batch_size, 动作数量)
# advantages.mean(dim=1, keepdim=True) 形状: (batch_size, 1)
q_values = value + (advantages - advantages.mean(dim=1, keepdim=True))
# 替代方法: 使用最大值减法
# q_values = value + (advantages - advantages.max(dim=1, keepdim=True)[0])
return q_values # 形状: (batch_size, 动作数量)
# --- 使用方法 ---
# state_dim = env.observation_space.shape[0]
# action_dim = env.action_space.n
# q_network = DuelingQNetwork(state_dim, action_dim)
# target_q_network = DuelingQNetwork(state_dim, action_dim)
# target_q_network.load_state_dict(q_network.state_dict()) # 初始化目标网络权重
# 代理训练循环的其余部分(经验回放、目标更新、损失计算)
# 与标准DQN或DDQN相同。只需使用此DuelingQNetwork
# 用于在线和目标网络。
使用对决架构时,损失计算、目标更新和交互循环没有根本性的改变。您只需用对决版本替换您的标准Q网络模型定义,用于在线和目标网络。通过将对决网络架构用于和并应用前面显示的DDQN目标计算逻辑,您可以轻松地与DDQN结合。
实现这些变体是第一步。接下来是进行实验并观察它们的影响。
CartPole-v1或LunarLander-v2,以加快迭代速度。然后,转向更复杂的任务,如雅达利游戏(例如,PongNoFrameskip-v4,BreakoutNoFrameskip-v4),使用适当的包装器进行帧跳过和堆叠。下面是一个图表,展现了您可能观察到的潜在差异:
DQN、双DQN和对决DQN的学习进展比较,显示训练集数上的平均每集奖励。实际结果会根据环境、实现细节和超参数 (parameter) (hyperparameter)有很大差异。
请记住,超参数调优(学习率、回放缓冲区大小、目标网络更新频率、epsilon衰减计划、网络架构)对于任何这些算法获得良好性能都非常重要。需要进行实验来找到适合您具体问题的设置。结合这些技术,例如在DDQN更新规则中使用对决架构(对决DDQN),是一种常见做法。
这部分内容有帮助吗?
© 2026 ApX Machine LearningAI伦理与透明度•