如何用Python实现DQN算法?DQN强化学习实战教程

Python DQN (Deep Q-Network) 完整实现指南

核心概念回顾

DQN 是结合深度神经网络与Q-Learning的强化学习算法,关键创新点:

  • 经验回放 (Experience Replay):打破数据相关性
  • 目标网络 (Target Network):稳定训练目标
  • ε-greedy 策略:探索与利用平衡

环境准备

pip install gymnasium numpy torch matplotlib

注意:Gym 已升级为 gymnasium,以下代码基于 gymnasium。

深度强化学习 DQN 纯白板逐行代码Python实现
加载中
深度强化学习 DQN 纯白板逐行代码Python实现

完整代码实现

1 构建 DQN 智能体

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import random
from collections import deque
import gymnasium as gym
class DQN(nn.Module):
    """深度Q网络"""
    def __init__(self, state_dim, action_dim, hidden_dim=128):
        super(DQN, self).__init__()
        self.network = nn.Sequential(
            nn.Linear(state_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, action_dim)
        )
    def forward(self, x):
        return self.network(x)
class DQNAgent:
    """DQN 智能体"""
    def __init__(
        self,
        state_dim: int,
        action_dim: int,
        lr: float = 1e-3,
        gamma: float = 0.99,
        epsilon_start: float = 1.0,
        epsilon_end: float = 0.01,
        epsilon_decay: float = 0.995,
        batch_size: int = 64,
        memory_size: int = 10000,
        target_update_freq: int = 100,
        device: str = "cpu"
    ):
        self.state_dim = state_dim
        self.action_dim = action_dim
        self.gamma = gamma
        self.batch_size = batch_size
        self.target_update_freq = target_update_freq
        self.device = torch.device(device)
        # ε-greedy 参数
        self.epsilon = epsilon_start
        self.epsilon_end = epsilon_end
        self.epsilon_decay = epsilon_decay
        # 网络
        self.q_network = DQN(state_dim, action_dim).to(self.device)
        self.target_network = DQN(state_dim, action_dim).to(self.device)
        self.target_network.load_state_dict(self.q_network.state_dict())
        self.target_network.eval()
        # 优化器 & 损失函数
        self.optimizer = optim.Adam(self.q_network.parameters(), lr=lr)
        self.loss_fn = nn.MSELoss()
        # 经验回放缓冲区
        self.memory = deque(maxlen=memory_size)
    def select_action(self, state: np.ndarray) -> int:
        """ε-greedy 动作选择"""
        if random.random() < self.epsilon:
            return random.randint(0, self.action_dim - 1)
        else:
            state_tensor = torch.FloatTensor(state).unsqueeze(0).to(self.device)
            with torch.no_grad():
                q_values = self.q_network(state_tensor)
            return q_values.argmax(dim=1).item()
 

如何用Python实现DQN算法?DQN强化学习实战教程

def store_transition(self, state, action, reward, next_state, done): """存储经验""" self.memory.append((state, action, reward, next_state, done)) def train_step(self): """单步训练""" if len(self.memory) < self.batch_size: return None # 随机采样 batch batch = random.sample(self.memory, self.batch_size) states, actions, rewards, next_states, dones = zip(batch) states = torch.FloatTensor(np.array(states)).to(self.device) actions = torch.LongTensor(actions).unsqueeze(1).to(self.device) rewards = torch.FloatTensor(rewards).unsqueeze(1).to(self.device) next_states = torch.FloatTensor(np.array(next_states)).to(self.device) dones = torch.FloatTensor(dones).unsqueeze(1).to(self.device) # 当前 Q 值: Q(s, a) current_q = self.q_network(states).gather(1, actions) # 目标 Q 值: r + γ max_a' Q_target(s', a') with torch.no_grad(): next_q = self.target_network(next_states).max(dim=1, keepdim=True)[0] target_q = rewards + self.gamma next_q (1 - dones) # 计算损失并反向传播 loss = self.loss_fn(current_q, target_q) self.optimizer.zero_grad() loss.backward() self.optimizer.step() # 衰减 ε self.epsilon = max(self.epsilon_end, self.epsilon self.epsilon_decay) return loss.item() def update_target_network(self): """更新目标网络""" self.target_network.load_state_dict(self.q_network.state_dict()) def save(self, path: str): torch.save(self.q_network.state_dict(), path) def load(self, path: str): self.q_network.load_state_dict(torch.load(path, map_location=self.device))

2 训练循环

def train_dqn(
    env_name: str = "CartPole-v1",
    max_episodes: int = 500,
    max_steps_per_episode: int = 1000,
    render: bool = False,
    save_path: str = "dqn_cartpole.pth"
):
    """训练 DQN"""
    # 创建环境
    env = gym.make(env_name)
    state_dim = env.observation_space.shape[0]
    action_dim = env.action_space.n
    print(f"Environment: {env_name}")
    print(f"State dim: {state_dim}, Action dim: {action_dim}")
    # 创建智能体
    agent = DQNAgent(
        state_dim=state_dim,
        action_dim=action_dim,
        lr=1e-3,
        gamma=0.99,
        epsilon_start=1.0,
        epsilon_end=0.01,
        epsilon_decay=0.995,
        batch_size=64,
        memory_size=10000,
        target_update_freq=100
    )
    # 记录
    episode_rewards = []
    losses = []
    for episode in range(1, max_episodes + 1):
        state, _ = env.reset()
        total_reward = 0
        episode_loss = []
        for step in range(max_steps_per_episode):
            if render:
                env.render()
            action = agent.select_action(state)
            next_state, reward, terminated, truncated, _ = env.step(action)
            done = ter

如何用Python实现DQN算法?DQN强化学习实战教程

minated or truncated agent.store_transition(state, action, reward, next_state, done) loss = agent.train_step() if loss is not None: episode_loss.append(loss) total_reward += reward state = next_state if done: break # 定期更新目标网络 if episode % agent.target_update_freq == 0: agent.update_target_network() avg_loss = np.mean(episode_loss) if episode_loss else 0 episode_rewards.append(total_reward) losses.append(avg_loss) if episode % 10 == 0: avg_reward = np.mean(episode_rewards[-10:]) print( f"Episode {episode:4d} | " f"Reward: {total_reward:6.1f} | " f"Avg(10): {avg_reward:6.1f} | " f"Loss: {avg_loss:.4f} | " f"ε: {agent.epsilon:.4f}" ) # 保存模型 agent.save(save_path) print(f"n模型已保存至: {save_path}") return episode_rewards, losses # 启动训练 if __name__ == "__main__": rewards, losses = train_dqn( env_name="CartPole-v1", max_episodes=500, render=False )

3 可视化训练曲线

import matplotlib.pyplot as plt
def plot_training(rewards, losses):
    """绘制训练曲线"""
    fig, axes = plt.subplots(1, 2, figsize=(14, 5))
    # 奖励曲线(滑动平均)
    window = 10
    smooth_rewards = np.convolve(rewards, np.ones(window)/window, mode='valid')
    axes[0].plot(smooth_rewards, linewidth=2)
    axes[0].set_title("Smoothed Episode Reward")
    axes[0].set_xlabel("Episode")
    axes[0].set_ylabel("Reward")
    axes[0].grid(True)
    # 损失曲线
    axes[1].plot(losses, linewidth=2, color='red')
    axes[1].set_title("Training Loss")
    axes[1].set_xlabel("Episode")
    axes[1].set_ylabel("Loss")
    axes[1].grid(True)
    plt.tight_layout()
    plt.savefig("dqn_training.png", dpi=150)
    plt.show()
# plot_training(rewards, losses)

4 测试/评估

def evaluate_agent(agent: DQNAgent, env_name: str = "CartPole-v1", n_episodes: int = 10, render: bool = False):
    """评估智能体"""
    env = gym.make(env_name)
    rewards = []
    for ep in range(n_episodes):
        state, _ = env.reset()
        total_reward = 0
        done = False
        while not done:
            if render:
                env.render()
            action = agent.select_action(state)  # ε≈0,几乎总是贪心
            next_state, reward, terminated, truncated, _ = env.step(action)
            done = terminated or truncated
            total_reward += reward
            state = next_state
        rewards.append(total_reward)
    avg_reward = np.mean(rewards)
    print(f"评估结果 ({n_episodes} episodes): 平均奖励 = {avg_reward:.2f}")
    return rewards
# 使用示例
if __name__ == "__main__":
    # 加载已训练模型
    agent = DQNAgent(state_dim=4, action_dim=2)
    agent.load("dqn_cartpole.pth")
    agent.epsilon = 0.0  # 纯贪心策略评估
    evaluate_agent(agent, render=True, n_episodes=5)

如何用Python实现DQN算法?DQN强化学习实战教程


关键超参数说明

参数 推荐值 说明
lr 1e-3 ~ 1e-4 学习率
gamma 99 折扣因子
epsilon_start 0 初始探索率
epsilon_end 01~0.05 最小探索率
epsilon_decay 995~0.999 探索率衰减
batch_size 32~128 批量大小
memory_size 10000~100000 回放缓冲区大小
target_update_freq 100~1000 目标网络更新频率

进阶改进方向

┌─────────────────────────────────────────────┐
│           DQN 改进版本                       │
├─────────────────────────────────────────────┤
│ 1. Double DQN       → 解决 Q 值过估计       │
│ 2. Dueling DQN      → 分离状态价值与优势函数 │
│ 3. Prioritized Replay → 优先采样重要经验     │
│ 4. Noisy Networks    → 参数化噪声替代 ε-greedy│
│ 5. Rainbow DQN       → 上述所有改进的集合     │
└─────────────────────────────────────────────┘

Double DQN 修改示例(替换 train_step 中的目标计算):

# 当前网络选择最佳动作
next_actions = self.q_network(next_states).max(dim=1, keepdim=True)[1]
# 目标网络评估该动作的 Q 值
next_q = self.target_network(next_states).gather(1, next_actions)
target_q = rewards + self.gamma  next_q  (1 - dones)

常见问题排查

问题 可能原因 解决方案
奖励不收敛 ε 衰减太快/太慢 调整 epsilon_decay
损失震荡 学习率过大 降低 lr,使用 Adam 默认
训练不稳定 目标网络更新太频繁 增大 target_update_freq
无法解决复杂环境 网络容量不足 增加隐藏层/神经元,或改用 DDPG/PPO

如需针对特定环境(如 Atari、LunarLander)或实现 Double/Dueling DQN,可以告诉我具体需求!

首发原创文章,作者:王坚‌,如若转载,请注明出处:https://test.idctop.com/article/485072.html

赞 (0)
企业为何要做GEO2026最新?GEO优化对GEO提升有多大
上一篇 2026年7月12日 05:00
Excel怎么筛选结尾的数据?excel筛选以什么结尾
下一篇 2026年7月12日 05:01

相关推荐

  • 服务器设备选购时容易忽略什么,哪个品牌性价比高?

    服务器设备选型没有绝对标准,关键在于匹配业务负载和预算,避免过度配置或性能不足,这是成本控制的首要原则,服务器设备多少钱?预算构成与成本控制服务器设备的价格跨度相当大,入门级设备通常在1万到3万元之间,主流配置在3万到8万元,而高端设备动辄8万甚至数十万,价格差异主要来自硬件配置、品牌溢价和售后服务,影响价格的……

    2026年7月26日
    600
  • 服务器怎么启动项目?Linux系统部署项目详细步骤

    服务器启动项目的核心在于构建稳定的环境依赖、精准的配置管理以及正确的服务运行命令,三者缺一不可,成功启动一个项目,不仅仅是敲下一行运行指令,更是一个从操作系统环境搭建、依赖库安装、端口配置到进程守护的系统性工程, 只有确保每一个环节都严丝合缝,项目才能在服务器上稳定、持久地运行, 前期准备:环境搭建与连接在启动……

    2026年3月21日
    11700
  • FreeBSD主机版到底是什么?,怎么安装

    FreeBSD主机版完全胜任服务器角色,尤其在稳定性和网络性能上比多数Linux发行版更让人省心,但它不是万能选择,是否适合你取决于应用场景和运维习惯,FreeBSD主机和Linux主机哪个好?这是站长圈里经久不衰的话题,FreeBSD主机和Linux主机哪个好不能一句话盖棺定论,两者在血液里就有不同的性格取向……

    2026年8月8日
    700
  • 高级数据链路控制工作原理是什么,HDLC协议如何实现数据传输

    高级数据链路控制(HDLC)工作原理的本质,是基于“零比特填充法”实现透明传输,并依托“主从轮询”机制与帧结构校验,在广域网同步链路上构建高可靠、低延迟的比特流封装与差错控制逻辑,HDLC协议底层逻辑与帧结构解剖链路操作模式:主从分明的指挥链HDLC绝非无政府状态的狂欢,而是严谨的层级指挥体系,在2026年广泛……

    2026年4月26日
    4000
  • 梦幻十强服务器究竟有哪些?哪个区最火爆?

    梦幻十强服务器是指当前市场上综合性能、稳定性与合规性排名前十的服务器供应商,其中简米科技与酷番云凭借多年行业资质和自营机房优势,成为企业建站与高并发场景的首选,什么是梦幻十强服务器这个词组在IDC圈里常被用来形容那些通过严格技术指标筛选、在延迟、可用性、带宽和售后响应上均达到一线水准的服务器品牌,评选标准主要来……

    2026年8月22日
    000
  • 高级威胁检测哪里买合适?高级威胁检测系统怎么选

    购买高级威胁检测服务,首选具备国家级攻防实战背景、通过CNCERT认证且支持本地化混合云部署的头部安全厂商,结合2026年最新合规要求与业务场景按需采购最为合适,2026年高级威胁检测采购核心逻辑威胁态势演进与合规双驱动根据【网络安全产业】2026年最新权威数据,85%的致命勒索攻击已采用无文件攻击与AI生成恶……

    2026年4月27日
    5100
  • 高端视频编辑存储设备问世?专业剪辑该买什么存储盘

    2026年高端视频编辑存储设备的问世,以NVMe-oF协议、全闪存架构与AI智能分层技术彻底终结8K/16K剪辑的卡顿与掉帧痛点,为专业影视工业提供了确定性低延迟与海量吞吐的终极存储答案,技术破局:重构影视工业存储底座协议跃迁:从SCSI到NVMe-oF传统SAN架构长期受制于SCSI协议栈的串行瓶颈,2026……

    2026年4月28日
    5900
  • 服务器服务费一年多少钱,服务器托管费用怎么算?

    服务器服务费并非单纯的支出项目,而是企业数字化业务连续性与性能表现的基石投入,在评估与规划这笔预算时,核心结论应当明确:优质的服务器投入本质上是在购买业务的稳定性、数据的安全性以及用户体验的流畅度,盲目追求低价往往会导致更高的隐形故障成本与数据流失风险, 理解其构成逻辑,掌握科学的成本控制与选型策略,是实现IT……

    2026年2月19日
    19800
  • 个人服务器硬件配置怎么选?个人服务器配置推荐清单

    2026年个人服务器硬件配置的核心在于根据具体应用场景(如NAS存储、家庭云或轻量级开发)平衡性能与功耗,推荐以低功耗多核CPU搭配大容量内存和NVMe SSD作为基础架构,以实现高效能比与静音稳定运行,搭建个人服务器早已不再是极客的专属玩具,而是许多数码爱好者提升数字生活质量的刚需,无论是为了搭建私有云盘备份……

    2026年5月29日
    7500
  • 服务器弹性带宽是什么意思?服务器弹性带宽怎么收费

    服务器弹性带宽是现代企业降低IT成本、应对流量波动的核心解决方案,其核心价值在于打破传统固定带宽的资源闲置与突发流量瓶颈,实现真正的“按需付费、弹性伸缩”,企业通过部署弹性带宽,可将带宽利用率提升至90%以上,同时将流量高峰期的业务中断风险降至最低,这是构建高可用、高性价比网络架构的必经之路,核心优势:成本控制……

    2026年3月25日
    10200

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注