【强化学习】Double DQN(Double Deep Q-Network)算法

📢本篇文章是博主强化学习(RL)领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对相关等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在👉强化学习专栏:

【强化学习】- 【单智能体强化学习】(12)---《Double DQN(Double Deep Q-Network)算法》

Double DQN(Double Deep Q-Network)算法

目录

[一、Double DQN算法详解](#一、Double DQN算法详解)

二、算法背景和提出

[2.1 过估计偏差问题](#2.1 过估计偏差问题)

[2.2 Double Q-Learning的灵感](#2.2 Double Q-Learning的灵感)

[2.3 Double DQN的提出](#2.3 Double DQN的提出)

[三、Double DQN的核心思想](#三、Double DQN的核心思想)

四、算法流程

五、公式推导

[[Python] Double DQN算法实现](#[Python] Double DQN算法实现)

[[Notice] 代码说明](#[Notice] 代码说明)

六、优势与特点

七、总结


一、Double DQN算法详解

强化学习中的深度Q网络(DQN)是一种将深度学习与Q学习结合的算法,它通过神经网络逼近Q函数以解决复杂的高维状态问题。然而,DQN存在过估计问题(Overestimation Bias),即在更新Q值时,由于同时使用同一个网络选择动作和计算目标Q值,可能导致Q值的估计偏高。

Double DQN(DDQN)引入了"双网络"机制来缓解这个问题,从而提高了算法的稳定性和收敛性。


二、算法背景和提出

在强化学习的早期研究中,Q学习是一种经典算法,它通过构建Q值表来描述每个状态-动作对的长期累积奖励。然而,当状态和动作空间变得巨大甚至连续时,Q学习方法难以扩展。为此,深度Q网络(Deep Q-Network, DQN)引入了神经网络来逼近Q函数,并取得了显著的成果,如成功应用于Atari游戏。但DQN算法在实际应用中暴露出了一些问题,其中过估计偏差(Overestimation Bias)尤为突出。

2.1 过估计偏差问题

在DQN算法中,Q值更新公式如下:

其中:

  • 是当前的即时奖励;
  • 是折扣因子;
  • 是目标网络的Q值。

DQN使用的是"最大值"max操作来选择动作并估计未来的价值,这种方式可能导致过高估计。其根本原因在于:

  1. 同一个网络(目标网络)既负责选择动作(动作选择偏好),又负责评估这些动作的价值(动作的价值计算)。
  2. 神经网络的逼近误差会放大估计值,从而进一步加剧过估计问题。

这种偏差会导致:

  • 策略变得过于激进;
  • 学习过程变得不稳定;
  • 收敛速度减慢甚至无法收敛。

2.2 Double Q-Learning的灵感

Double Q-Learning是一种用于减少过估计问题的经典方法。其基本思想是分离动作选择和价值估计。它使用两个独立的Q值表:

  1. 一个表用于选择动作;
  2. 另一个表用于计算目标值。

Double Q-Learning的目标值公式为:

通过这种分离计算,动作选择的误差不会直接影响到目标值计算,从而减少了过估计的风险。


2.3 Double DQN的提出

Double DQN(DDQN)受Double Q-Learning启发,将其思想扩展到深度强化学习领域。主要区别在于:

  1. 使用在线网络(Online Network)来选择动作;
  2. 使用目标网络(Target Network)来估计动作的价值。

Double DQN的目标值公式为:

其中:

  • 是在线网络,用于选择动作;
  • 是目标网络,用于估计目标Q值。

这种方法成功地解决了DQN的过估计问题,并在多个强化学习任务中表现出了更好的性能和稳定性。


三、Double DQN的核心思想

Double DQN通过分离动作选择目标Q值计算来减小过估计问题:

  1. 使用在线网络(Online Network)选择动作。
  2. 使用目标网络(Target Network)计算目标Q值。

这种分离使得目标Q值的计算更加可靠,有助于减少估计偏差。


四、算法流程

1.初始化

初始化两个神经网络:在线网络 和目标网络

的参数定期从 同步。

2.执行动作

当前状态下,利用选择动作

3.存储经验

将转移样本存入经验回放池。

4.经验回放

从经验回放池中随机采样一个小批量

5.目标值计算(关键点)

使用在线网络选择下一个状态的最佳动作:

使用目标网络计算目标Q值:

6.更新在线网络

使用均方误差(MSE)作为损失函数,对 进行梯度下降:

7.更新目标网络

每隔一定步数,将 的参数复制到


五、公式推导

  1. Q学习目标

    传统DQN的目标值是:

    这里的 max 操作会导致过估计问题。

  2. Double DQN目标

    DDQN通过分离动作选择和目标计算,目标值改为:

    • 动作 a 是由在线网络 选择的。
    • Q值是由目标网络计算的。
  3. 减小过估计的作用

    • 通过在线网络选择动作,可以更准确地反映当前策略的动作价值。
    • 目标网络仅用来计算Q值,减少了目标计算时的估计偏差。

[Python] Double DQN算法实现

下面给出是Double DQN算法的完整Python实现代码,它通过PyTorch框架实现,并包含了核心的在线网络和目标网络的更新机制:

项目代码我已经放入GitCode里面,可以通过下面链接跳转:🔥

【强化学习】--- Double DQN算法

后续相关单智能体强化学习算法也会不断在**【强化学习】**项目里更新,如果该项目对你有所帮助,请帮我点一个星星 ✨✨✨✨✨,鼓励分享,十分感谢!!!

若是下面代码复现困难或者有问题,也欢迎评论区留言

1. 导入必要库

python 复制代码
import numpy as np  # 导入NumPy库,用于处理数组和数值计算
import torch  # 导入PyTorch库,用于构建和训练深度学习模型
import torch.nn as nn  # 导入PyTorch的神经网络模块,用于构建网络结构
import torch.optim as optim  # 导入PyTorch的优化器模块,用于优化神经网络参数
import random  # 导入Python的随机模块,用于实现随机采样
from collections import deque  # 导入deque数据结构,用于存储经验回放池

2. 超参数设置

python 复制代码
# Hyperparameters
GAMMA = 0.99  # 折扣因子,控制奖励的时间衰减
LR = 0.001  # 学习率,用于控制优化器的步长
BATCH_SIZE = 64  # 每次训练的批量大小
MEMORY_CAPACITY = 10000  # 经验回放池的最大容量
TARGET_UPDATE = 10  # 目标网络更新的周期(每10个回合更新一次)

3. 定义网络

python 复制代码
# Define the neural network
class QNetwork(nn.Module):  # 定义Q网络,用于逼近Q值函数
    def __init__(self, state_dim, action_dim):
        super(QNetwork, self).__init__()  # 初始化父类
        self.fc1 = nn.Linear(state_dim, 128)  # 第一层全连接层,输入维度为状态维度,输出128维
        self.fc2 = nn.Linear(128, 128)  # 第二层全连接层,输入和输出均为128维
        self.fc3 = nn.Linear(128, action_dim)  # 输出层,输出维度为动作维度
    
    def forward(self, x):  # 定义前向传播过程
        x = torch.relu(self.fc1(x))  # 第一层激活函数为ReLU
        x = torch.relu(self.fc2(x))  # 第二层激活函数为ReLU
        x = self.fc3(x)  # 输出层不加激活函数,直接输出Q值
        return x

4. 缓存经验区

python 复制代码
# Replay buffer
class ReplayBuffer:  # 定义经验回放池,用于存储和采样经验数据
    def __init__(self, capacity):
        self.buffer = deque(maxlen=capacity)  # 使用deque实现固定长度的经验池
    
    def push(self, state, action, reward, next_state, done):  # 添加新的经验
        self.buffer.append((state, action, reward, next_state, done))
    
    def sample(self, batch_size):  # 随机采样一个批量的经验
        batch = random.sample(self.buffer, batch_size)
        states, actions, rewards, next_states, dones = zip(*batch)  # 解压为单独的数组
        return (np.array(states), np.array(actions), np.array(rewards),
                np.array(next_states), np.array(dones))
    
    def __len__(self):  # 返回经验池的当前大小
        return len(self.buffer)

5.Double DQN算法

python 复制代码
# Double DQN Agent
class DoubleDQNAgent:  # 定义Double DQN智能体
    def __init__(self, state_dim, action_dim):
        self.state_dim = state_dim  # 状态维度
        self.action_dim = action_dim  # 动作维度
        
        # Online and target networks
        self.online_net = QNetwork(state_dim, action_dim)  # 在线网络,用于实时决策
        self.target_net = QNetwork(state_dim, action_dim)  # 目标网络,用于稳定目标计算
        self.target_net.load_state_dict(self.online_net.state_dict())  # 初始化目标网络参数
        self.target_net.eval()  # 设置目标网络为评估模式
        
        self.optimizer = optim.Adam(self.online_net.parameters(), lr=LR)  # 使用Adam优化器
        self.memory = ReplayBuffer(MEMORY_CAPACITY)  # 创建经验回放池
        self.steps_done = 0  # 记录执行的步数
    
    def select_action(self, state, epsilon):  # 动作选择,使用ε-贪婪策略
        if random.random() < epsilon:  # 以概率epsilon选择随机动作(探索)
            return random.randint(0, self.action_dim - 1)
        else:  # 否则选择当前Q值最大的动作(利用)
            state = torch.FloatTensor(state).unsqueeze(0)  # 转换为张量并增加批量维度
            with torch.no_grad():  # 关闭梯度计算
                q_values = self.online_net(state)  # 通过在线网络计算Q值
            return q_values.argmax().item()  # 返回最大Q值对应的动作索引
    
    def store_transition(self, state, action, reward, next_state, done):  # 存储经验
        self.memory.push(state, action, reward, next_state, done)
    
    def update(self):  # 更新在线网络
        if len(self.memory) < BATCH_SIZE:  # 如果经验不足一个批量,则不更新
            return
        
        # Sample a batch of transitions
        states, actions, rewards, next_states, dones = self.memory.sample(BATCH_SIZE)  # 从经验池中采样
        states = torch.FloatTensor(states)
        actions = torch.LongTensor(actions).unsqueeze(1)  # 转换为张量并调整维度
        rewards = torch.FloatTensor(rewards).unsqueeze(1)
        next_states = torch.FloatTensor(next_states)
        dones = torch.FloatTensor(dones).unsqueeze(1)
        
        # Compute Q values
        q_values = self.online_net(states).gather(1, actions)  # 提取当前Q值
        
        # Compute target Q values using Double DQN
        with torch.no_grad():
            next_actions = self.online_net(next_states).argmax(dim=1, keepdim=True)  # 在线网络选择动作
            next_q_values = self.target_net(next_states).gather(1, next_actions)  # 目标网络评估动作
            target_q_values = rewards + (1 - dones) * GAMMA * next_q_values  # 计算目标Q值
        
        # Compute loss
        loss = nn.MSELoss()(q_values, target_q_values)  # 均方误差损失函数
        
        # Update online network
        self.optimizer.zero_grad()  # 清空梯度
        loss.backward()  # 反向传播计算梯度
        self.optimizer.step()  # 更新在线网络参数
    
    def update_target_network(self):  # 定期更新目标网络
        self.target_net.load_state_dict(self.online_net.state_dict())

6. 算法训练

python 复制代码
# Environment simulation (example: CartPole-v1)
import gym  # 导入Gym库,用于创建和交互强化学习环境

env = gym.make('CartPole-v1')  # 创建CartPole环境
state_dim = env.observation_space.shape[0]  # 获取状态空间的维度
action_dim = env.action_space.n  # 获取动作空间的维度

agent = DoubleDQNAgent(state_dim, action_dim)  # 创建Double DQN智能体

# Training Loop
num_episodes = 500  # 总训练回合数
epsilon_start = 1.0  # ε-贪婪策略的初始探索率
epsilon_end = 0.01  # 最终的探索率
epsilon_decay = 500  # 探索率的衰减速度

for episode in range(num_episodes):  # 按回合循环训练
    state = env.reset()  # 重置环境
    done = False  # 标记是否完成
    total_reward = 0  # 累计奖励初始化为0
    while not done:  # 每个时间步内
        # Epsilon-greedy action selection
        epsilon = epsilon_end + (epsilon_start - epsilon_end) * np.exp(-1. * agent.steps_done / epsilon_decay)  # 动态调整探索率
        action = agent.select_action(state, epsilon)  # 根据ε-贪婪策略选择动作
        
        # Step in the environment
        next_state, reward, done, _ = env.step(action)  # 执行动作并获取下一状态和奖励
        total_reward += reward  # 累加奖励
        
        # Store transition and update
        agent.store_transition(state, action, reward, next_state, done)  # 存储经验
        agent.update()  # 更新在线网络
        
        state = next_state  # 更新当前状态
        agent.steps_done += 1  # 增加步数计数
    
    # Update target network periodically
    if episode % TARGET_UPDATE == 0:
        agent.update_target_network()  # 定期更新目标网络
    
    print(f"Episode {episode}, Total Reward: {total_reward}")  # 打印当前回合的累计奖励

env.close()  # 关闭环境

[Notice] 代码说明

  1. ReplayBuffer:经验回放池,用于存储状态、动作、奖励、下一个状态和是否结束标志。
  2. QNetwork:定义深度Q网络,包含3个全连接层。
  3. DoubleDQNAgent
    • 维护在线网络(Online Network)和目标网络(Target Network)。
    • 使用在线网络选择动作,用目标网络计算目标值。
  4. 训练流程
    • 在每个时间步,使用( \epsilon )-贪婪策略选择动作。
    • 与环境交互,存储数据到经验回放池。
    • 采样小批量数据进行训练,通过Double DQN公式计算目标Q值。
    • 定期更新目标网络。
bash 复制代码
​# 环境配置
Python                  3.11.5
torch                   2.1.0
torchvision             0.16.0
gym                     0.26.2

由于博文主要为了介绍相关算法的原理和应用的方法,缺乏对于实际效果的关注,算法可能在上述环境中的效果不佳或者无法运行,一是算法不适配上述环境,二是算法未调参和优化,三是没有呈现完整的代码,四是等等。上述代码用于了解和学习算法足够了,但若是想直接将上面代码应用于实际项目中,还需要进行修改。


六、优势与特点

Double DQN与DQN的对比

特性 DQN Double DQN
目标值计算 动作选择和评估使用同一网络 分离动作选择和目标评估
过估计偏差 明显存在 显著减小
训练稳定性 容易震荡 更加稳定
算法复杂度 较低 略微增加(多一次网络前向计算)

1.减小过估计偏差

分离动作选择和目标计算后,Double DQN有效减少了过高估计的风险。

2.更稳定的训练过程

由于估计值更准确,训练更加平滑,收敛速度更快。

3.简单易实现

在DQN的基础上,仅需额外引入动作选择的分离逻辑,容易实现。


七、总结

Double DQN算法的提出,主要是为了解决DQN中的"过估计偏差"问题。通过引入双网络,Double DQN让动作选择和价值评估分离,大大提高了算法的稳定性和准确性。

更多强化学习 文章,请前往:【强化学习(RL)】专栏


博客都是给自己看的笔记,如有误导深表抱歉。文章若有不当和不正确之处,还望理解与指出。由于部分文字、图片等来源于互联网,无法核实真实出处,如涉及相关争议,请联系博主删除。如有错误、疑问和侵权,欢迎评论留言联系作者,或者添加VX:**Rainbook_2,**联系作者。✨

相关推荐
电子基础知识2 小时前
NanoEdge AI Studio入门
人工智能·硬件工程
LeeZhao@2 小时前
【项目】多模态图文理解-GLM-Edge实战
人工智能·yolo·计算机视觉·语言模型·aigc
破-风4 小时前
leetcode-------mysql
算法·leetcode·职场和发展
橙狮科技5 小时前
提示词工程教程:提示词安全
人工智能·安全·自然语言处理
李元豪6 小时前
机器视觉面试题PDF
机器学习
cxr8286 小时前
五类推理(逻辑推理、概率推理、图推理、基于深度学习的推理)的开源库 (二)
人工智能·深度学习
魔理沙偷走了BUG6 小时前
【AI数学基础】线性代数:内积和范数
人工智能·线性代数·机器学习
salsm6 小时前
使用 C++ 和函数式编程构建高效的 AI 模型
c++·人工智能
IT古董7 小时前
【机器学习】机器学习的基本分类-自监督学习-生成式方法(Generative Methods)
学习·机器学习·分类
qq_273900237 小时前
pytorch torch.scatter_reduce函数介绍
人工智能·pytorch·python