【机器学习精通】第13章 | 强化学习基础:马尔可夫决策过程与Q学习
声明:本章使用 Python 3.12+ 环境,PyTorch 2.0+ 框架,OpenAI Gymnasium 环境库
学习目标
完成本章学习后,你将能够:
- 理解强化学习与监督学习的本质区别
- 掌握马尔可夫决策过程的数学框架
- 推导贝尔曼方程并理解其价值
- 实现 Q-Learning 和 DQN 算法
- 使用深度强化学习解决实际控制问题
1. 强化学习概述
1.1 三种学习范式的对比
强化学习(Reinforcement Learning, RL)是机器学习三大范式之一,与监督学习和无监督学习形成鲜明对比:
| 学习范式 | 数据形式 | 反馈类型 | 典型应用 |
|---|---|---|---|
| 监督学习 | (输入, 标签) 成对数据 | 即时、明确的正确/错误反馈 | 图像分类、语音识别 |
| 无监督学习 | 无标签数据 | 无外部反馈,发现内在结构 | 聚类、降维、生成模型 |
| 强化学习 | 状态-动作-奖励序列 | 延迟、稀疏的奖励信号 | 游戏AI、机器人控制 |
1.2 强化学习的核心要素
强化学习的本质是一个智能体(Agent)与环境(Environment)持续交互的过程:
核心概念解析:
- 状态(State):环境在某一时刻的完整描述,记为 sts_tst
- 动作(Action):智能体在给定状态下可执行的操作,记为 ata_tat
- 奖励(Reward):环境对智能体动作的即时反馈,记为 rtr_trt
- 策略(Policy):从状态到动作的映射,记为 π(a∣s)\pi(a|s)π(a∣s)
- 回报(Return):从当前时刻开始的累积折扣奖励:
Gt=rt+γrt+1+γ2rt+2+⋯=∑k=0∞γkrt+k G_t = r_t + \gamma r_{t+1} + \gamma^2 r_{t+2} + \cdots = \sum_{k=0}^{\infty} \gamma^k r_{t+k} Gt=rt+γrt+1+γ2rt+2+⋯=k=0∑∞γkrt+k
其中 γ∈[0,1]\gamma \in [0, 1]γ∈[0,1] 是折扣因子,用于平衡即时奖励与未来奖励的重要性。
补充:强化学习之父 Richard Sutton 将 RL 定义为"通过试错来学习映射状态到动作,以最大化数值奖励信号"。
2. 马尔可夫决策过程
2.1 MDP 五元组定义
马尔可夫决策过程(Markov Decision Process, MDP)是强化学习的数学基础框架,由五元组 (S,A,P,R,γ)(S, A, P, R, \gamma)(S,A,P,R,γ) 构成:
| 符号 | 含义 | 数学表示 |
|---|---|---|
| SSS | 状态空间 | 所有可能状态的集合 |
| AAA | 动作空间 | 所有可能动作的集合 |
| PPP | 状态转移概率 | $P(s’ |
| RRR | 奖励函数 | R(s,a)=E[Rt+1∣St=s,At=a]R(s,a) = \mathbb{E}[R_{t+1} | S_t=s, A_t=a]R(s,a)=E[Rt+1∣St=s,At=a] |
| γ\gammaγ | 折扣因子 | γ∈[0,1]\gamma \in [0, 1]γ∈[0,1] |
2.2 马尔可夫性质
马尔可夫性质是 MDP 的核心假设:未来状态仅依赖于当前状态,与历史无关。
P[St+1∣St,At,St−1,At−1,…,S0,A0]=P[St+1∣St,At] \mathbb{P}[S_{t+1} | S_t, A_t, S_{t-1}, A_{t-1}, \ldots, S_0, A_0] = \mathbb{P}[S_{t+1} | S_t, A_t] P[St+1∣St,At,St−1,At−1,…,S0,A0]=P[St+1∣St,At]
通俗理解:就像下棋时,当前棋盘局面已经包含了所有历史信息,无需知道之前的每一步如何走。
2.3 状态转移概率
对于确定性环境,给定状态 sss 和动作 aaa,下一个状态 s′s's′ 是确定的;对于随机环境,状态转移服从概率分布:
∑s′∈SP(s′∣s,a)=1,∀s∈S,a∈A \sum_{s' \in S} P(s'|s,a) = 1, \quad \forall s \in S, a \in A s′∈S∑P(s′∣s,a)=1,∀s∈S,a∈A
3. 价值函数与贝尔曼方程
3.1 状态价值函数 V
状态价值函数 Vπ(s)V^{\pi}(s)Vπ(s) 表示:从状态 sss 出发,遵循策略 π\piπ 所能获得的期望回报:
Vπ(s)=Eπ[Gt∣St=s]=Eπ[∑k=0∞γkRt+k+1∣St=s] V^{\pi}(s) = \mathbb{E}_{\pi}[G_t | S_t = s] = \mathbb{E}_{\pi}\left[\sum_{k=0}^{\infty} \gamma^k R_{t+k+1} \bigg| S_t = s\right] Vπ(s)=Eπ[Gt∣St=s]=Eπ[k=0∑∞γkRt+k+1 St=s]
3.2 动作价值函数 Q
动作价值函数 Qπ(s,a)Q^{\pi}(s,a)Qπ(s,a) 表示:从状态 sss 出发,执行动作 aaa 后遵循策略 π\piπ 的期望回报:
Qπ(s,a)=Eπ[Gt∣St=s,At=a]=Eπ[∑k=0∞γkRt+k+1∣St=s,At=a] Q^{\pi}(s,a) = \mathbb{E}_{\pi}[G_t | S_t = s, A_t = a] = \mathbb{E}_{\pi}\left[\sum_{k=0}^{\infty} \gamma^k R_{t+k+1} \bigg| S_t = s, A_t = a\right] Qπ(s,a)=Eπ[Gt∣St=s,At=a]=Eπ[k=0∑∞γkRt+k+1 St=s,At=a]
V 与 Q 的关系:
Vπ(s)=∑aπ(a∣s)Qπ(s,a) V^{\pi}(s) = \sum_{a} \pi(a|s) Q^{\pi}(s,a) Vπ(s)=a∑π(a∣s)Qπ(s,a)
Qπ(s,a)=R(s,a)+γ∑s′P(s′∣s,a)Vπ(s′) Q^{\pi}(s,a) = R(s,a) + \gamma \sum_{s'} P(s'|s,a) V^{\pi}(s') Qπ(s,a)=R(s,a)+γs′∑P(s′∣s,a)Vπ(s′)
3.3 贝尔曼期望方程
贝尔曼方程是强化学习最重要的方程,它将价值函数递归地表示为即时奖励与折扣未来价值之和。
状态价值的贝尔曼期望方程:
Vπ(s)=∑aπ(a∣s)[R(s,a)+γ∑s′P(s′∣s,a)Vπ(s′)] V^{\pi}(s) = \sum_{a} \pi(a|s) \left[ R(s,a) + \gamma \sum_{s'} P(s'|s,a) V^{\pi}(s') \right] Vπ(s)=a∑π(a∣s)[R(s,a)+γs′∑P(s′∣s,a)Vπ(s′)]
动作价值的贝尔曼期望方程:
Qπ(s,a)=R(s,a)+γ∑s′P(s′∣s,a)∑a′π(a′∣s′)Qπ(s′,a′) Q^{\pi}(s,a) = R(s,a) + \gamma \sum_{s'} P(s'|s,a) \sum_{a'} \pi(a'|s') Q^{\pi}(s',a') Qπ(s,a)=R(s,a)+γs′∑P(s′∣s,a)a′∑π(a′∣s′)Qπ(s′,a′)
3.4 贝尔曼最优方程
最优价值函数 V∗(s)V^*(s)V∗(s) 和 Q∗(s,a)Q^*(s,a)Q∗(s,a) 是在所有策略中的最大值:
V∗(s)=maxπVπ(s),Q∗(s,a)=maxπQπ(s,a) V^*(s) = \max_{\pi} V^{\pi}(s), \quad Q^*(s,a) = \max_{\pi} Q^{\pi}(s,a) V∗(s)=πmaxVπ(s),Q∗(s,a)=πmaxQπ(s,a)
贝尔曼最优方程(非线性,因为包含 max 操作):
V∗(s)=maxa[R(s,a)+γ∑s′P(s′∣s,a)V∗(s′)] V^*(s) = \max_{a} \left[ R(s,a) + \gamma \sum_{s'} P(s'|s,a) V^*(s') \right] V∗(s)=amax[R(s,a)+γs′∑P(s′∣s,a)V∗(s′)]
Q∗(s,a)=R(s,a)+γ∑s′P(s′∣s,a)maxa′Q∗(s′,a′) Q^*(s,a) = R(s,a) + \gamma \sum_{s'} P(s'|s,a) \max_{a'} Q^*(s',a') Q∗(s,a)=R(s,a)+γs′∑P(s′∣s,a)a′maxQ∗(s′,a′)
一句话总结:贝尔曼方程告诉我们,当前状态的价值等于即时奖励加上折扣后的未来价值期望。
4. 动态规划求解
4.1 策略评估(Policy Evaluation)
给定一个策略 π\piπ,计算其状态价值函数的过程称为策略评估。
迭代算法:
Vk+1(s)=∑aπ(a∣s)[R(s,a)+γ∑s′P(s′∣s,a)Vk(s′)] V_{k+1}(s) = \sum_{a} \pi(a|s) \left[ R(s,a) + \gamma \sum_{s'} P(s'|s,a) V_k(s') \right] Vk+1(s)=a∑π(a∣s)[R(s,a)+γs′∑P(s′∣s,a)Vk(s′)]
重复迭代直到收敛:maxs∣Vk+1(s)−Vk(s)∣<θ\max_s |V_{k+1}(s) - V_k(s)| < \thetamaxs∣Vk+1(s)−Vk(s)∣<θ
4.2 策略改进(Policy Improvement)
基于当前价值函数,通过贪心选择改进策略:
π′(s)=argmaxaQπ(s,a)=argmaxa[R(s,a)+γ∑s′P(s′∣s,a)Vπ(s′)] \pi'(s) = \arg\max_{a} Q^{\pi}(s,a) = \arg\max_{a} \left[ R(s,a) + \gamma \sum_{s'} P(s'|s,a) V^{\pi}(s') \right] π′(s)=argamaxQπ(s,a)=argamax[R(s,a)+γs′∑P(s′∣s,a)Vπ(s′)]
策略改进定理:如果 π′\pi'π′ 是通过上述方式从 π\piπ 得到的,则 Vπ′(s)≥Vπ(s)V^{\pi'}(s) \geq V^{\pi}(s)Vπ′(s)≥Vπ(s) 对所有状态成立。
4.3 策略迭代(Policy Iteration)
策略迭代交替进行策略评估和策略改进,直到策略收敛:
1. 初始化:随机选择策略 π
2. 策略评估:计算 V^π
3. 策略改进:基于 V^π 得到 π'
4. 如果 π' ≠ π,令 π = π',返回步骤 2
5. 输出最优策略 π*
4.4 价值迭代(Value Iteration)
价值迭代将策略评估和策略改进合并为一步:
Vk+1(s)=maxa[R(s,a)+γ∑s′P(s′∣s,a)Vk(s′)] V_{k+1}(s) = \max_{a} \left[ R(s,a) + \gamma \sum_{s'} P(s'|s,a) V_k(s') \right] Vk+1(s)=amax[R(s,a)+γs′∑P(s′∣s,a)Vk(s′)]
收敛后,最优策略为:
π∗(s)=argmaxa[R(s,a)+γ∑s′P(s′∣s,a)V∗(s′)] \pi^*(s) = \arg\max_{a} \left[ R(s,a) + \gamma \sum_{s'} P(s'|s,a) V^*(s') \right] π∗(s)=argamax[R(s,a)+γs′∑P(s′∣s,a)V∗(s′)]
对比总结:
| 方法 | 特点 | 收敛速度 |
|---|---|---|
| 策略迭代 | 每次完整评估策略,再改进 | 迭代次数少,但每次计算量大 |
| 价值迭代 | 评估和改进合并,不维护显式策略 | 迭代次数多,但每次计算量小 |
5. 蒙特卡洛与时序差分
5.1 蒙特卡洛方法(MC)
当环境模型 P(s′∣s,a)P(s'|s,a)P(s′∣s,a) 未知时,无法直接使用动态规划。蒙特卡洛方法通过采样完整回合(episode)来估计价值。
首次访问 MC 算法:
对于每个状态 sss,维护一个回报列表。每当 sss 在某回合首次被访问时,计算该回合从 sss 开始的回报 GGG,加入列表。
V(s)=1N(s)∑i=1N(s)Gi(s) V(s) = \frac{1}{N(s)} \sum_{i=1}^{N(s)} G_i(s) V(s)=N(s)1i=1∑N(s)Gi(s)
特点:
- 必须等待回合结束才能更新
- 无偏估计,但方差大
- 只能用于回合制任务(episodic tasks)
5.2 时序差分学习(TD)
TD 方法结合了动态规划和蒙特卡洛的优点,使用自举(bootstrapping)进行在线学习。
TD(0) 更新规则:
V(St)←V(St)+α[Rt+1+γV(St+1)−V(St)] V(S_t) \leftarrow V(S_t) + \alpha \left[ R_{t+1} + \gamma V(S_{t+1}) - V(S_t) \right] V(St)←V(St)+α[Rt+1+γV(St+1)−V(St)]
其中 α\alphaα 是学习率,δt=Rt+1+γV(St+1)−V(St)\delta_t = R_{t+1} + \gamma V(S_{t+1}) - V(S_t)δt=Rt+1+γV(St+1)−V(St) 称为 TD 误差。
5.3 SARSA 算法
SARSA 是 on-policy 的 TD 控制算法,更新 QQQ 函数:
Q(St,At)←Q(St,At)+α[Rt+1+γQ(St+1,At+1)−Q(St,At)] Q(S_t, A_t) \leftarrow Q(S_t, A_t) + \alpha \left[ R_{t+1} + \gamma Q(S_{t+1}, A_{t+1}) - Q(S_t, A_t) \right] Q(St,At)←Q(St,At)+α[Rt+1+γQ(St+1,At+1)−Q(St,At)]
算法名称来源于更新使用的五元组:(St,At,Rt+1,St+1,At+1)(S_t, A_t, R_{t+1}, S_{t+1}, A_{t+1})(St,At,Rt+1,St+1,At+1)
5.4 n-step TD
n-step TD 是 MC 和 TD(0) 的推广,使用前 n 步真实奖励和估计价值:
Gt:t+n=Rt+1+γRt+2+⋯+γn−1Rt+n+γnV(St+n) G_{t:t+n} = R_{t+1} + \gamma R_{t+2} + \cdots + \gamma^{n-1} R_{t+n} + \gamma^n V(S_{t+n}) Gt:t+n=Rt+1+γRt+2+⋯+γn−1Rt+n+γnV(St+n)
V(St)←V(St)+α[Gt:t+n−V(St)] V(S_t) \leftarrow V(S_t) + \alpha \left[ G_{t:t+n} - V(S_t) \right] V(St)←V(St)+α[Gt:t+n−V(St)]
- n = 1:退化为 TD(0)
- n = T-t(回合长度):退化为 MC
6. Q-Learning 与 DQN
6.1 Q-Learning 算法
Q-Learning 是 off-policy 的 TD 控制算法,由 Watkins 于 1989 年提出。
核心更新公式:
Q(St,At)←Q(St,At)+α[Rt+1+γmaxa′Q(St+1,a′)−Q(St,At)] Q(S_t, A_t) \leftarrow Q(S_t, A_t) + \alpha \left[ R_{t+1} + \gamma \max_{a'} Q(S_{t+1}, a') - Q(S_t, A_t) \right] Q(St,At)←Q(St,At)+α[Rt+1+γa′maxQ(St+1,a′)−Q(St,At)]
关键特性:
- Off-policy:学习最优策略的同时,可以遵循探索性策略(如 epsilon-贪心)
- 收敛性:在适当条件下,Q-Learning 以概率 1 收敛到最优 Q∗Q^*Q∗
- 表格形式:仅适用于离散、有限的状态和动作空间
6.2 深度 Q 网络(DQN)
当状态空间巨大或连续时,表格方法失效。DQN 使用神经网络近似 QQQ 函数:
Q(s,a;θ)≈Q∗(s,a)Q(s,a;\theta) \approx Q^*(s,a)Q(s,a;θ)≈Q∗(s,a)
损失函数:
L(θ)=E(s,a,r,s′)∼D[(r+γmaxa′Q(s′,a′;θ−)−Q(s,a;θ))2] L(\theta) = \mathbb{E}_{(s,a,r,s') \sim D} \left[ \left( r + \gamma \max_{a'} Q(s',a';\theta^-) - Q(s,a;\theta) \right)^2 \right] L(θ)=E(s,a,r,s′)∼D[(r+γa′maxQ(s′,a′;θ−)−Q(s,a;θ))2]
6.3 DQN 的三大关键技术
1. 经验回放(Experience Replay)
将交互经验 (s,a,r,s′,done)(s, a, r, s', done)(s,a,r,s′,done) 存储在回放缓冲区,训练时随机采样小批量数据:
- 打破数据相关性,提高样本效率
- 平滑数据分布,稳定训练
2. 目标网络(Target Network)
使用独立的网络参数 θ−\theta^-θ− 计算目标值,定期从主网络复制:
- 减少训练过程中的震荡
- 提高学习稳定性
3. 奖励裁剪与帧堆叠
- 将奖励裁剪到 [-1, 1] 范围,统一不同游戏的奖励尺度
- 堆叠最近 4 帧作为输入,捕捉运动信息
6.4 DQN 的改进算法
| 算法 | 核心改进 | 主要优势 |
|---|---|---|
| Double DQN | 解耦动作选择与动作评估 | 解决 Q 值过估计问题 |
| Dueling DQN | 分离状态价值和优势函数 | 更好地学习哪些状态有价值 |
| Prioritized Experience Replay | 按 TD 误差优先级采样 | 提高重要样本的采样概率 |
| Noisy Nets | 参数化探索噪声 | 替代 epsilon-贪心,自适应探索 |
| C51 | 学习价值分布而非期望值 | 建模 Q 值的完整分布 |
| Rainbow | 整合六种改进 | Atari 游戏上达到 SOTA |
7. 前沿算法简介(2024-2025)
7.1 近端策略优化(PPO)
PPO 由 OpenAI 于 2017 年提出,已成为策略梯度方法的默认选择。
核心思想:限制策略更新的幅度,避免破坏性的大更新。
目标函数:
LCLIP(θ)=Et[min(rt(θ)A^t,clip(rt(θ),1−ϵ,1+ϵ)A^t)] L^{CLIP}(\theta) = \mathbb{E}_t \left[ \min\left( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t \right) \right] LCLIP(θ)=Et[min(rt(θ)A^t,clip(rt(θ),1−ϵ,1+ϵ)A^t)]
其中 rt(θ)=πθ(at∣st)πθold(at∣st)r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)}rt(θ)=πθold(at∣st)πθ(at∣st) 是概率比,A^t\hat{A}_tA^t 是优势函数估计。
2024-2025 进展:
- PPO-Clip 变体:自适应调整 clipping 参数
- 分布式 PPO:在大规模集群上实现高效训练
- 多模态 PPO:结合视觉-语言模型进行复杂任务学习
7.2 软演员-评论家(SAC)
SAC 是最大熵强化学习框架下的 off-policy 算法,在机器人控制任务上表现优异。
核心特点:
- 最大化期望回报的同时最大化策略熵
- 鼓励探索,避免过早收敛到局部最优
- 自动调整温度参数 α\alphaα
目标函数:
J(π)=∑tE(st,at)∼ρπ[r(st,at)+αH(π(⋅∣st))] J(\pi) = \sum_{t} \mathbb{E}_{(s_t,a_t) \sim \rho_\pi} \left[ r(s_t, a_t) + \alpha \mathcal{H}(\pi(\cdot|s_t)) \right] J(π)=t∑E(st,at)∼ρπ[r(st,at)+αH(π(⋅∣st))]
2024-2025 应用:
- 自动驾驶决策:处理连续控制和高维感知输入
- 机械臂操作:实现样本高效的灵巧操作
- 多智能体协作:在团队任务中实现协调行为
7.3 DreamerV3:世界模型学习
DreamerV3 是 DeepMind 于 2023-2025 年推出的世界模型强化学习算法。
核心创新:
- 通用性:无需调整超参数即可在超过 150 种不同任务上训练
- 样本高效:在 Minecraft 中从零开始挖掘钻石,无需人类示例
- 世界模型:学习环境的动态模型,在想象中规划
算法架构:
- 表示学习:将高维观测编码为紧凑的潜在状态
- 动态预测:学习状态转移模型 pθ(st+1∣st,at)p_\theta(s_{t+1}|s_t,a_t)pθ(st+1∣st,at)
- 奖励预测:学习奖励模型 pθ(rt∣st)p_\theta(r_t|s_t)pθ(rt∣st)
- 策略学习:在世界模型中训练策略
2025 年突破:
- 成功在 Minecraft 中完成钻石挖掘任务
- 在机器人仿真中实现零样本迁移到真实环境
- 与大型语言模型结合,实现语言条件控制
7.4 EfficientZero-V2
清华大学高阳团队于 2024 年提出的样本高效强化学习框架。
主要贡献:
- 统一的框架适用于离散和连续控制任务
- 基于采样的树搜索用于动作规划
- 在 50k-200k 交互数据预算下超越 DreamerV3
7.5 应用领域展望
| 应用领域 | 典型场景 | 代表算法 |
|---|---|---|
| 游戏AI | 围棋、星际争霸、Dota2 | AlphaGo、AlphaStar、OpenAI Five |
| 机器人控制 | 行走、抓取、导航 | SAC、PPO、DreamerV3 |
| 自动驾驶 | 路径规划、决策控制 | PPO、Model-based RL |
| 推荐系统 | 序列推荐、动态定价 | Contextual Bandits、DQN |
| 金融交易 | 投资组合管理、高频交易 | PPO、A3C |
| 自然语言处理 | 对话系统、文本生成 | RLHF(基于人类反馈的强化学习) |
8. 实战案例:使用 DQN 解决 CartPole 问题
8.1 环境介绍
CartPole 是强化学习的经典入门环境:
- 目标:通过左右移动小车,保持杆子直立
- 状态空间:4维连续向量(小车位置、速度、杆子角度、角速度)
- 动作空间:2个离散动作(左移、右移)
- 奖励:每步存活获得 +1 奖励
- 终止条件:杆子倾斜超过 15 度,或小车移出边界,或达到 500 步
8.2 DQN 实现代码
import gymnasium as gym
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
from collections import deque
import random
# 设置随机种子,保证实验可复现
SEED = 42
random.seed(SEED)
np.random.seed(SEED)
torch.manual_seed(SEED)
# 超参数配置
class Config:
# 网络结构
STATE_DIM = 4 # CartPole 状态维度
ACTION_DIM = 2 # 动作空间大小
HIDDEN_DIM = 128 # 隐藏层维度
# 训练参数
BATCH_SIZE = 64 # 批量大小
LEARNING_RATE = 1e-3 # 学习率
GAMMA = 0.99 # 折扣因子
EPSILON_START = 1.0 # 初始探索率
EPSILON_END = 0.01 # 最终探索率
EPSILON_DECAY = 0.995 # 探索率衰减
TARGET_UPDATE = 10 # 目标网络更新频率
MEMORY_SIZE = 10000 # 经验回放缓冲区大小
NUM_EPISODES = 500 # 训练回合数
MAX_STEPS = 500 # 每回合最大步数
# 定义 Q 网络
class DQN(nn.Module):
"""
深度 Q 网络:将状态映射到每个动作的价值
输入: 状态 (batch_size, state_dim)
输出: 每个动作的价值 (batch_size, action_dim)
"""
def __init__(self, state_dim, action_dim, hidden_dim):
super(DQN, self).__init__()
self.fc1 = nn.Linear(state_dim, hidden_dim)
self.fc2 = nn.Linear(hidden_dim, hidden_dim)
self.fc3 = nn.Linear(hidden_dim, action_dim)
self.relu = nn.ReLU()
def forward(self, x):
x = self.relu(self.fc1(x))
x = self.relu(self.fc2(x))
return self.fc3(x)
# 经验回放缓冲区
class ReplayBuffer:
"""
存储和采样训练经验的缓冲区
使用 deque 实现固定容量的循环缓冲区
"""
def __init__(self, capacity):
self.buffer = deque(maxlen=capacity)
def push(self, state, action, reward, next_state, done):
"""存储一条经验 (s, a, r, s', 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 (
torch.FloatTensor(np.array(states)),
torch.LongTensor(actions),
torch.FloatTensor(rewards),
torch.FloatTensor(np.array(next_states)),
torch.FloatTensor(dones)
)
def __len__(self):
return len(self.buffer)
# DQN 智能体
class DQNAgent:
"""
DQN 智能体:包含策略网络、目标网络和经验回放
"""
def __init__(self, config):
self.config = config
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 创建策略网络和目标网络
self.policy_net = DQN(
config.STATE_DIM,
config.ACTION_DIM,
config.HIDDEN_DIM
).to(self.device)
self.target_net = DQN(
config.STATE_DIM,
config.ACTION_DIM,
config.HIDDEN_DIM
).to(self.device)
# 目标网络初始化为策略网络的副本
self.target_net.load_state_dict(self.policy_net.state_dict())
self.target_net.eval() # 目标网络不计算梯度
# 优化器和经验回放缓冲区
self.optimizer = optim.Adam(
self.policy_net.parameters(),
lr=config.LEARNING_RATE
)
self.memory = ReplayBuffer(config.MEMORY_SIZE)
self.epsilon = config.EPSILON_START
self.steps_done = 0
def select_action(self, state, training=True):
"""
使用 epsilon-贪心策略选择动作
以 epsilon 概率随机探索,以 1-epsilon 概率选择最优动作
"""
if training and random.random() < self.epsilon:
return random.randrange(self.config.ACTION_DIM)
with torch.no_grad():
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
q_values = self.policy_net(state)
return q_values.argmax().item()
def update_epsilon(self):
"""衰减探索率"""
self.epsilon = max(
self.config.EPSILON_END,
self.epsilon * self.config.EPSILON_DECAY
)
def learn(self):
"""
从经验回放中采样并更新网络参数
使用 MSE 损失计算当前 Q 值与目标 Q 值的差异
"""
if len(self.memory) < self.config.BATCH_SIZE:
return None
# 采样一批经验
states, actions, rewards, next_states, dones = self.memory.sample(
self.config.BATCH_SIZE
)
states = states.to(self.device)
actions = actions.to(self.device)
rewards = rewards.to(self.device)
next_states = next_states.to(self.device)
dones = dones.to(self.device)
# 计算当前 Q 值: Q(s, a)
current_q = self.policy_net(states).gather(1, actions.unsqueeze(1))
# 计算目标 Q 值: r + gamma * max Q(s', a')
with torch.no_grad():
next_q = self.target_net(next_states).max(1)[0]
target_q = rewards + (1 - dones) * self.config.GAMMA * next_q
# 计算 MSE 损失
loss = nn.MSELoss()(current_q.squeeze(), target_q)
# 反向传播和优化
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
return loss.item()
def update_target_network(self):
"""将策略网络的参数复制到目标网络"""
self.target_net.load_state_dict(self.policy_net.state_dict())
# 训练函数
def train_dqn():
"""
主训练循环:与环境交互、存储经验、训练网络
"""
config = Config()
# 创建环境
env = gym.make('CartPole-v1')
agent = DQNAgent(config)
scores = [] # 记录每回合得分
scores_window = deque(maxlen=100) # 最近 100 回合的得分
print("开始训练 DQN...")
print(f"设备: {agent.device}")
for episode in range(config.NUM_EPISODES):
state, _ = env.reset(seed=SEED + episode)
score = 0
for step in range(config.MAX_STEPS):
# 选择动作并执行
action = agent.select_action(state, training=True)
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
# 存储经验
agent.memory.push(state, action, reward, next_state, float(done))
# 更新网络
loss = agent.learn()
state = next_state
score += reward
agent.steps_done += 1
if done:
break
# 更新探索率
agent.update_epsilon()
# 定期更新目标网络
if episode % config.TARGET_UPDATE == 0:
agent.update_target_network()
scores_window.append(score)
scores.append(score)
avg_score = np.mean(scores_window)
# 打印训练进度
if episode % 50 == 0:
print(f"回合 {episode:4d} | 得分: {score:3.0f} | "
f"平均得分: {avg_score:6.2f} | 探索率: {agent.epsilon:.3f}")
# 检查是否解决环境(连续 100 回合平均得分 >= 475)
if avg_score >= 475.0 and episode >= 100:
print(f"\n环境在 {episode} 回合后解决!")
print(f"平均得分: {avg_score:.2f}")
break
env.close()
return agent, scores
# 测试训练好的模型
def test_agent(agent, num_episodes=10):
"""
测试训练好的智能体性能
"""
env = gym.make('CartPole-v1', render_mode='human')
total_score = 0
print("\n测试训练好的智能体...")
for episode in range(num_episodes):
state, _ = env.reset()
score = 0
for step in range(500):
action = agent.select_action(state, training=False)
state, reward, terminated, truncated, _ = env.step(action)
score += reward
if terminated or truncated:
break
total_score += score
print(f"测试回合 {episode + 1}: 得分 = {score}")
print(f"平均得分: {total_score / num_episodes:.2f}")
env.close()
# 主程序
if __name__ == "__main__":
# 训练
agent, scores = train_dqn()
# 保存模型
torch.save(agent.policy_net.state_dict(), 'dqn_cartpole.pth')
print("\n模型已保存到 dqn_cartpole.pth")
# 绘制训练曲线(可选)
try:
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 5))
plt.plot(scores)
plt.title('DQN Training Progress on CartPole')
plt.xlabel('Episode')
plt.ylabel('Score')
plt.grid(True)
plt.savefig('dqn_training_curve.png')
print("训练曲线已保存到 dqn_training_curve.png")
except ImportError:
print("matplotlib 未安装,跳过绘图")
8.3 代码解析
关键设计要点:
- 网络结构:使用两个隐藏层的全连接网络,输入状态输出每个动作的价值
- 经验回放:存储最近 10000 条经验,随机采样打破数据相关性
- 目标网络:每 10 个回合同步一次,稳定学习目标
- 探索策略:epsilon-贪心,从 1.0 逐渐衰减到 0.01
- 终止条件:连续 100 回合平均得分达到 475 分视为解决
9. 避坑小贴士
9.1 常见错误与解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| Q 值发散,损失爆炸 | 学习率过大 | 降低学习率至 1e-4 或 5e-4 |
| 训练不稳定,波动大 | 目标网络更新太频繁 | 增加目标网络更新间隔 |
| 无法收敛,得分停滞 | 探索率衰减过快 | 减缓 epsilon 衰减速度 |
| 内存占用过高 | 回放缓冲区太大 | 减小 MEMORY_SIZE 或定期清理 |
| GPU 利用率低 | 批量太小 | 增大 BATCH_SIZE 到 128 或 256 |
9.2 调试技巧
- 可视化 Q 值:监控 Q 值的变化趋势,正常情况下应逐渐收敛
- 检查探索:确保前期有足够的随机探索,避免陷入局部最优
- 梯度裁剪:添加梯度裁剪防止梯度爆炸
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=10) - 奖励缩放:某些环境需要对奖励进行归一化处理
9.3 超参数调优建议
- 学习率:从 1e-3 开始,如果发散则降低,收敛慢则提高
- 折扣因子:大多数任务使用 0.99,短期任务可尝试 0.9
- 批量大小:64-256 是常见范围,更大的批量需要更多内存
- 网络大小:简单任务用 64-128 隐藏单元,复杂任务用 256-512
10. 本章小结
本章系统介绍了强化学习的基础理论和实践方法:
核心概念回顾:
- MDP 框架:状态、动作、奖励、转移概率、折扣因子五元组
- 贝尔曼方程:价值函数的递归定义,是 RL 算法的理论基础
- 动态规划:策略迭代和价值迭代,适用于已知模型的场景
- TD 学习:结合采样和自举,适用于无模型场景
- Q-Learning:Off-policy 的 TD 控制算法,收敛到最优策略
- DQN:深度神经网络 + 经验回放 + 目标网络,解决高维状态问题
前沿进展:
- PPO:策略梯度方法的默认选择,稳定高效
- SAC:最大熵框架,在连续控制任务上表现优异
- DreamerV3:世界模型学习,实现样本高效和通用性
- RLHF:结合人类反馈,推动大语言模型发展
一句话总结:强化学习是让智能体通过与环境交互、试错学习,最终找到最优决策策略的机器学习方法。
感谢阅读!如有疑问,欢迎在评论区交流讨论。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)