强化学习系列(3):深度Q网络的改进(Double DQN、Dueling DQN等)
一、Double DQN原理
解决的问题
在传统的DQN中,由于目标Q值的计算方式,容易出现对Q值的高估问题。这是因为在选择动作和计算目标Q值时都使用了目标网络中对Q值的估计,可能会导致选择到被高估的动作,从而使得训练不稳定,最终影响策略的学习效果。
改进思路
Double DQN通过解耦动作选择和动作评估这两个过程来缓解高估问题。具体来说,它在计算目标Q值时,利用当前的训练网络(也就是策略网络)来选择动作,而利用目标网络来评估该动作对应的Q值。这样就使得选择动作和评估动作来自不同的网络估计,减少了对Q值的过度乐观估计。
算法伪代码对比
# 传统DQN伪代码(关键部分)
for each step in episode:
action = choose_action(state, policy_net)
next_state, reward, done = take_action(action)
q_target = reward + gamma * max_a(Q_target(next_state, a))
update(policy_net, q_target)
# Double DQN伪代码(关键部分)
for each step in episode:
action = choose_action(state, policy_net)
next_state, reward, done = take_action(action)
best_action = choose_action(next_state, policy_net)
q_target = reward + gamma * Q_target(next_state, best_action)
update(policy_net, q_target)
代码示例(Python基于之前DQN代码修改)
# 在计算目标Q值部分进行修改
for episode in range(num_episodes):
state, _ = env.reset()
state = torch.tensor(state, dtype=torch.float).unsqueeze(0)
done = False
while not done:
# 选择动作等过程不变(省略部分重复代码)
# 计算目标Q值
best_action = torch.argmax(policy_net(next_state)).item()
q_targets = rewards + gamma * target_net(next_state)[0][best_action].unsqueeze(0).unsqueeze(1) * (1 - dones)
# 后续计算损失、更新网络等步骤不变(省略部分重复代码)
二、Dueling DQN原理
结构特点
Dueling DQN在网络结构上进行了创新,它将Q值函数分解为状态价值函数(V(s))和动作优势函数(A(s,a))两部分,即 (Q(s,a) = V(s) + A(s,a) - \frac{1}{|A|} \sum_{a’} A(s,a’))。网络中会有两个分支分别输出状态价值和动作优势,然后通过特定的聚合方式得到最终的Q值估计。
优势体现
这种分解方式能够更好地学习状态的内在价值,尤其是在一些状态下不同动作的价值差异不大时,通过分离状态价值和动作优势可以更准确地捕捉到每个动作的相对优势,使得网络对价值的估计更加灵活,提高学习效率和最终策略的质量。
网络结构代码示例(基于之前DQN网络修改)
import torch
import torch.nn as nn
class DuelingDQN(nn.Module):
def __init__(self, input_size, output_size):
super(DuelingDQN, self).__init__()
self.fc1 = nn.Linear(input_size, 64)
self.fc2 = nn.Linear(64, 64)
# 状态价值分支
self.fc_value = nn.Linear(64, 1)
# 动作优势分支
self.fc_advantage = nn.Linear(64, output_size)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
value = torch.relu(self.fc_value(x))
advantage = torch.relu(self.fc_advantage(x))
# 聚合得到Q值
q_value = value + advantage - torch.mean(advantage, dim=1, keepdim=True)
return q_value
三、Double DQN与Dueling DQN结合(Dueling Double DQN)
结合思路
将Double DQN的改进思想(解耦动作选择和评估以减少高估)和Dueling DQN的结构优势(分离状态价值和动作优势)相结合,能够在避免高估Q值的同时,更精准地对状态和动作价值进行学习,进一步提升强化学习算法在复杂环境下的性能。
代码实现(整合前面的修改)
# 定义Dueling Double DQN网络结构(结合前面的代码思路)
class DuelingDoubleDQN(nn.Module):
def __init__(self, input_size, output_size):
super(DuelingDoubleDQN, self).__init__()
self.fc1 = nn.Linear(input_size, 64)
self.fc2 = nn.Linear(64, 64)
self.fc_value = nn.Linear(64, 1)
self.fc_advantage = nn.Linear(64, output_size)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
value = torch.relu(self.fc_value(x))
advantage = torch.relu(self.fc_advantage(x))
q_value = value + advantage - torch.mean(advantage, dim=1, keepdim=True)
return q_value
# 训练过程中结合Double DQN的动作选择和评估方式来计算目标Q值
for episode in range(num_episodes):
state, _ = env.reset()
state = torch.tensor(state, dtype=torch.float).unsqueeze(0)
done = False
while not done:
action = torch.argmax(policy_net(state)).item()
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
next_state = torch.tensor(next_state, dtype=torch.float).unsqueeze(0)
reward = torch.tensor([reward]).unsqueeze(0)
# 结合Double DQN选择动作评估方式
best_action = torch.argmax(policy_net(next_state)).item()
q_targets = reward + gamma * target_net(next_state)[0][best_action].unsqueeze(0).unsqueeze(1) * (1 - done)
# 后续计算损失、更新网络等步骤不变(省略部分重复代码)
四、不同改进版本DQN在实际环境中的性能对比
实验设置
选择几个典型的Atari游戏环境(如Breakout、Pong、SpaceInvaders等),分别使用传统DQN、Double DQN、Dueling DQN以及Dueling Double DQN进行训练,设置相同的训练轮数、超参数(如学习率、折扣因子、经验回放缓冲区大小等),观察它们在每个游戏中的平均得分、收敛速度以及最终策略的稳定性等指标。
实验结果分析
- 平均得分方面:在大部分游戏中,Dueling DQN和Dueling Double DQN往往能取得比传统DQN更高的平均得分,说明其对状态和动作价值的更合理估计有助于找到更好的策略,获得更高的奖励。而Double DQN相较于传统DQN在一些容易出现高估问题的游戏中,平均得分也会有所提升,证明了其缓解高估的有效性。
- 收敛速度方面:Dueling DQN及其与Double DQN结合的版本通常收敛速度更快,因为它们能更准确地捕捉价值信息,更快地调整网络参数以逼近最优策略,相比之下传统DQN可能需要更多的训练轮次才能达到较好的效果。
- 策略稳定性方面:Double DQN由于解决了高估问题,使得训练过程更加稳定,策略不会因为过度高估某些动作而出现大幅波动。Dueling DQN通过合理的价值分解也让策略的调整更加平稳,而两者结合的Dueling Double DQN在这方面表现得更为出色。
五、常见问题及应对策略
1. 结合多种改进后出现训练过拟合怎么办?
- 正则化手段:可以添加L1或L2正则化项到损失函数中,约束网络参数的大小,防止网络过度拟合训练数据。例如在PyTorch中,对于优化器可以这样设置:
optimizer = torch.optim.Adam(policy_net.parameters(), lr=learning_rate, weight_decay=0.001) # weight_decay就是L2正则化系数
- 增加数据多样性:尝试采用更多样的探索策略,扩大经验回放缓冲区的容量,或者对环境进行一些随机化的扰动(如在Atari游戏中对图像进行随机的微小变换等),让智能体接触到更多不同的状态,减少过拟合现象。
2. 如何选择适合具体场景的改进版本?
- 分析高估问题影响程度:如果在实验中发现明显的Q值高估现象(比如训练过程中某些动作的Q值异常高且不稳定),优先考虑使用Double DQN或者其结合版本。
- 考虑状态动作关系复杂度:当状态和动作之间的关系比较复杂,难以直接通过单一的Q值来准确判断动作优劣时,Dueling DQN及其结合版本更有优势,因为它们能更好地分解和学习价值信息。
六、下期预告:策略梯度算法(Policy Gradient)基础与应用
在**强化学习系列(4)**中,您将学习到:
- 策略梯度算法的基本原理和核心思想,理解它与基于值函数的方法(如DQN系列)的区别。
- 常见的策略梯度算法(如REINFORCE、A2C、A3C等)的具体实现和特点。
- 如何将策略梯度算法应用到实际的控制类和决策类场景中,并通过实例代码进行演示。
欢迎继续关注本系列,一起深入探索强化学习的更多精彩内容,期待您在评论区分享您的见解和疑问哦! 🔔