AI Agent的强化学习:从试错中优化决策策略
AI Agent的强化学习:从试错中优化决策策略
关键词
强化学习, AI Agent, 马尔可夫决策过程, Q学习, 深度Q网络, 策略梯度, 试错学习
摘要
强化学习(Reinforcement Learning, RL)作为人工智能领域最具活力的研究方向之一,使AI Agent能够通过与环境的交互、试错和奖励机制来优化决策策略。本文将深入探讨强化学习的核心概念、数学基础、经典算法以及实际应用,通过生动的比喻和具体的代码示例,帮助读者理解AI Agent如何从环境中学习并不断改进其行为策略。我们将从马尔可夫决策过程开始,逐步介绍Q学习、深度Q网络、策略梯度等关键技术,并通过实际案例展示强化学习在游戏、机器人和推荐系统中的应用。
1. 背景介绍
1.1 主题背景和重要性
想象一下,一个婴儿学习走路的过程:他尝试站立,摔倒,再尝试,调整姿势,逐渐掌握平衡,最终能够独立行走。这个过程中,婴儿通过不断尝试、失败和调整,学会了一项复杂的技能。强化学习正是模拟这种"试错学习"过程的AI技术。
在过去十年中,强化学习取得了令人瞩目的成就。2013年,DeepMind的DQN(深度Q网络)在Atari游戏上超越了人类水平;2016年,AlphaGo击败了世界围棋冠军李世石;2019年,OpenAI的Dota2 AI在团队合作游戏中战胜了职业选手。这些里程碑式的成就展示了强化学习的巨大潜力。
强化学习的重要性不仅体现在游戏领域,它在自动驾驶、机器人控制、资源管理、医疗决策、推荐系统等众多领域都有广泛的应用前景。与监督学习不同,强化学习不需要大量标注数据,而是通过Agent与环境的交互自主学习,这使得它在许多难以获取标注数据的场景中具有独特优势。
1.2 目标读者
本文适合以下读者:
- 对AI和机器学习有基本了解,希望深入学习强化学习的开发者
- 想要将强化学习应用到实际项目中的工程师和研究者
- 对AI Agent决策过程感兴趣的技术爱好者
- 计算机科学、自动化、运筹学等相关专业的学生
虽然我们会尽量使用通俗易懂的语言,但读者最好具备以下基础知识:
- 基本的Python编程能力
- 线性代数和微积分的基础知识
- 机器学习的基本概念
1.3 核心问题或挑战
强化学习面临着许多核心挑战,这些挑战也是我们在本文中需要探讨和解决的问题:
-
探索与利用的平衡(Exploration-Exploitation Trade-off):Agent需要在尝试新动作(探索)和选择已知最好动作(利用)之间找到平衡。过度探索可能导致效率低下,过度利用则可能陷入局部最优。
-
信用分配问题(Credit Assignment Problem):在许多任务中,奖励是延迟的。例如,在围棋中,只有终局时才有胜负奖励。如何将最终奖励正确分配给之前的每一步决策,是强化学习的核心难题之一。
-
状态空间和动作空间的维度灾难:在复杂环境中,状态和动作的数量可能是天文数字,传统方法难以处理。如何高效表示和泛化到未见过的状态,是实际应用中的关键问题。
-
样本效率问题:许多强化学习算法需要大量的交互样本才能学习到好的策略,这在真实世界应用中可能成本过高或不现实。
-
安全探索问题:在真实环境中,某些探索性动作可能导致危险或昂贵的后果。如何确保Agent在学习过程中的安全性,是实际部署的重要考量。
在本文中,我们将逐步探讨这些问题,并介绍解决这些问题的经典方法和最新进展。
2. 核心概念解析
2.1 核心概念
让我们先通过一个生活化的比喻来理解强化学习的基本框架。想象你在训练一只宠物狗:
- Agent(智能体):这就是你的狗,它能够感知环境并做出动作。
- 环境(Environment):这是狗所处的世界,包括你、房间、玩具等。
- 状态(State):这是环境的当前情况,比如狗是坐着还是站着,你手里有没有零食。
- 动作(Action):这是狗可以做的事情,比如坐下、握手、打滚。
- 奖励(Reward):这是你给狗的反馈,比如零食或表扬(正奖励),或轻轻的责备(负奖励)。
- 策略(Policy):这是狗的"决策规则",即在什么状态下应该做什么动作。
这个比喻虽然简单,但抓住了强化学习的核心要素。现在让我们更正式地介绍这些概念:
2.1.1 Agent与环境
强化学习的核心是Agent与Environment的交互循环:
Agent观察环境状态 → 基于策略选择动作 → 环境执行动作 →
环境返回新状态和奖励 → Agent更新策略 → 重复...
这个循环是强化学习的基础,Agent通过这个循环不断学习和改进策略。
2.1.2 状态与观测
状态(State):是环境的完整描述,包含了所有与决策相关的信息。在理想情况下,Agent能够直接观测到完整的状态。
观测(Observation):是Agent实际能够感知到的部分状态信息。在许多实际场景中,Agent无法观测到完整的环境状态,只能获得部分观测。
例如,在扑克游戏中,状态包括所有玩家的手牌和牌堆中的牌,但每个玩家只能观测到自己的手牌和公共牌,这就是部分观测的情况。
2.1.3 动作与策略
动作(Action):Agent在给定状态下可以执行的操作。动作空间可以是离散的(如上下左右)或连续的(如机器人关节的力矩)。
策略(Policy):是Agent的决策规则,定义了在每个状态下选择动作的方式。策略可以是确定性的,即状态到动作的映射 a=π(s)a = \pi(s)a=π(s),也可以是随机的,即状态到动作概率分布的映射 π(a∣s)=P(A=a∣S=s)\pi(a|s) = P(A=a|S=s)π(a∣s)=P(A=a∣S=s)。
2.1.4 奖励与回报
奖励(Reward):是环境对Agent动作的即时反馈,是一个标量值 RtR_tRt。奖励信号定义了任务的目标,是强化学习中最关键的设计元素之一。
回报(Return):是奖励的累积总和,通常考虑时间折扣因子 γ∈[0,1]\gamma \in [0,1]γ∈[0,1],使得未来的奖励权重降低。回报 GtG_tGt 定义为:
Gt=Rt+1+γRt+2+γ2Rt+3+...=∑k=0∞γkRt+k+1G_t = R_{t+1} + \gamma R_{t+2} + \gamma^2 R_{t+3} + ... = \sum_{k=0}^{\infty} \gamma^k R_{t+k+1}Gt=Rt+1+γRt+2+γ2Rt+3+...=k=0∑∞γkRt+k+1
折扣因子 γ\gammaγ 的选择很重要:当 γ\gammaγ 接近0时,Agent更关注即时奖励;当 γ\gammaγ 接近1时,Agent更关注长远奖励。
2.1.5 值函数
值函数是对状态或状态-动作对的"好坏"的估计,有两种主要类型:
-
状态值函数(State Value Function):Vπ(s)V^\pi(s)Vπ(s) 表示从状态 sss 开始,遵循策略 π\piπ 所能获得的期望回报:
Vπ(s)=Eπ[Gt∣St=s]V^\pi(s) = \mathbb{E}_\pi [G_t | S_t = s]Vπ(s)=Eπ[Gt∣St=s] -
动作值函数(Action Value Function)或Q函数:Qπ(s,a)Q^\pi(s,a)Qπ(s,a) 表示从状态 sss 开始,执行动作 aaa,然后遵循策略 π\piπ 所能获得的期望回报:
Qπ(s,a)=Eπ[Gt∣St=s,At=a]Q^\pi(s,a) = \mathbb{E}_\pi [G_t | S_t = s, A_t = a]Qπ(s,a)=Eπ[Gt∣St=s,At=a]
值函数是强化学习算法的核心,许多算法都围绕着估计或优化值函数展开。
2.1.6 最优策略与最优值函数
强化学习的目标是找到一个最优策略 π∗\pi^*π∗,使得对于所有状态 sss,都有 Vπ∗(s)≥Vπ(s)V^{\pi^*}(s) \geq V^\pi(s)Vπ∗(s)≥Vπ(s) 对任意策略 π\piπ 成立。
相应地,我们有最优状态值函数 V∗(s)=maxπVπ(s)V^*(s) = \max_\pi V^\pi(s)V∗(s)=maxπVπ(s) 和最优动作值函数 Q∗(s,a)=maxπQπ(s,a)Q^*(s,a) = \max_\pi Q^\pi(s,a)Q∗(s,a)=maxπQπ(s,a)。
最优值函数之间满足贝尔曼最优方程:
V∗(s)=maxaE[Rt+1+γV∗(St+1)∣St=s,At=a]V^*(s) = \max_a \mathbb{E} [R_{t+1} + \gamma V^*(S_{t+1}) | S_t = s, A_t = a]V∗(s)=amaxE[Rt+1+γV∗(St+1)∣St=s,At=a]
Q∗(s,a)=E[Rt+1+γmaxa′Q∗(St+1,a′)∣St=s,At=a]Q^*(s,a) = \mathbb{E} [R_{t+1} + \gamma \max_{a'} Q^*(S_{t+1}, a') | S_t = s, A_t = a]Q∗(s,a)=E[Rt+1+γa′maxQ∗(St+1,a′)∣St=s,At=a]
这些方程是许多强化学习算法的理论基础。
2.2 概念结构与核心要素组成
现在让我们用Mermaid图来展示强化学习的核心概念及其关系:
这个架构图展示了强化学习系统的主要组成部分及其相互关系。Agent通过Policy与Environment交互,同时利用Value Function来评估和改进Policy。Environment定义了State Space、Action Space和Reward Function,这些共同构成了强化学习问题的设置。
2.3 概念之间的关系
2.3.1 概念核心属性维度对比
让我们用表格对比强化学习中几个核心概念的关键属性:
| 概念 | 定义 | 类型 | 作用 | 时间特性 | 优化目标 |
|---|---|---|---|---|---|
| 策略(Policy) | 状态到动作的映射 | 确定性/随机性 | 决策 | 每个时间步 | 最大化期望回报 |
| 值函数(Value Function) | 状态/动作的价值估计 | 状态值/动作值 | 评估 | 未来累积 | 准确估计价值 |
| 奖励(Reward) | 环境的即时反馈 | 标量 | 激励 | 即时 | 设计合适的信号 |
| 回报(Return) | 奖励的累积 | 标量 | 目标 | 未来 | 最大化 |
| 环境(Environment) | Agent交互的对象 | 确定性/随机性 | 提供反馈 | 持续 | 模拟真实世界 |
这个表格从多个维度对比了强化学习的核心概念,帮助我们理解它们之间的区别和联系。
2.3.2 概念联系的ER实体关系图
让我们用Mermaid ER图来展示这些概念之间的实体关系:
这个ER图展示了强化学习中各个实体之间的关系,Agent拥有Policy和Value Function,与Environment交互并收集Experience,而Experience包含了State、Action、Reward和Next State等信息。
2.3.3 交互关系图
最后,让我们用Mermaid序列图来展示Agent与Environment之间的交互过程:
这个序列图详细展示了强化学习的交互循环:Agent观测状态、选择动作、执行动作,Environment返回奖励和新状态,Agent存储经验并利用这些经验更新值函数和策略。
2.4 马尔可夫决策过程
在介绍了核心概念后,我们需要了解强化学习问题的数学框架——马尔可夫决策过程(Markov Decision Process, MDP)。
2.4.1 MDP的定义
一个马尔可夫决策过程是一个五元组 (S,A,P,R,γ)(S, A, P, R, \gamma)(S,A,P,R,γ),其中:
- SSS 是状态空间,所有可能状态的集合
- AAA 是动作空间,所有可能动作的集合
- P(s′∣s,a)P(s'|s,a)P(s′∣s,a) 是状态转移概率,表示在状态 sss 执行动作 aaa 后转移到状态 s′s's′ 的概率
- R(s,a,s′)R(s,a,s')R(s,a,s′) 是奖励函数,表示在状态 sss 执行动作 aaa 转移到状态 s′s's′ 后获得的即时奖励
- γ∈[0,1]\gamma \in [0,1]γ∈[0,1] 是折扣因子,表示未来奖励的重要性
马尔可夫性是MDP的关键假设:下一个状态只依赖于当前状态和动作,而与更早的历史无关。即:
P(St+1∣St,At,St−1,At−1,...,S0,A0)=P(St+1∣St,At)P(S_{t+1} | S_t, A_t, S_{t-1}, A_{t-1}, ..., S_0, A_0) = 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.4.2 MDP的解
MDP的解是找到一个策略 π\piπ,使得期望回报最大化。根据是否已知MDP的模型(即状态转移概率 PPP 和奖励函数 RRR),我们可以将求解方法分为两类:
-
基于模型的方法(Model-based Methods):如果我们知道MDP的模型,可以使用动态规划(Dynamic Programming)方法,如策略迭代(Policy Iteration)和价值迭代(Value Iteration)。
-
无模型方法(Model-free Methods):如果我们不知道MDP的模型,就需要通过与环境交互来学习,这就是强化学习的主要研究内容,包括蒙特卡洛方法(Monte Carlo Methods)、时序差分学习(Temporal Difference Learning)等。
在本文中,我们主要关注无模型方法,因为它们在实际应用中更为常见。
2.5 探索与利用的平衡
探索与利用的平衡是强化学习的一个核心挑战。让我们用一个简单的例子来说明:
想象你在一个有很多餐厅的城市里,你想找到最好的餐厅。你可以:
- 利用(Exploitation):去你已经知道比较好的餐厅,确保获得不错的体验。
- 探索(Exploration):尝试一家新餐厅,可能发现更好的,也可能失望。
同样,Agent需要在选择已知最好的动作(利用)和尝试新动作(探索)之间找到平衡。下面介绍几种常见的探索策略:
2.5.1 ε-贪婪策略(ε-Greedy)
这是最简单也最常用的探索策略:
- 以概率 1−ε1-\varepsilon1−ε 选择当前估计价值最高的动作(利用)
- 以概率 ε\varepsilonε 随机选择一个动作(探索)
通常,我们会随着时间的推移逐渐减小 ε\varepsilonε,让Agent在初期多探索,后期多利用。
2.5.2 softmax策略
softmax策略根据动作的价值来分配选择概率,价值越高的动作被选中的概率越大:
π(a∣s)=exp(Q(s,a)/τ)∑a′exp(Q(s,a′)/τ)\pi(a|s) = \frac{\exp(Q(s,a)/\tau)}{\sum_{a'} \exp(Q(s,a')/\tau)}π(a∣s)=∑a′exp(Q(s,a′)/τ)exp(Q(s,a)/τ)
其中 τ>0\tau > 0τ>0 是温度参数:
- 当 τ\tauτ 很大时,所有动作的选择概率相近,更多探索
- 当 τ\tauτ 很小时,价值高的动作被选中的概率大,更多利用
2.5.3 上置信界算法(UCB)
UCB(Upper Confidence Bound)算法考虑了两个因素:动作的平均奖励和尝试次数:
At=argmaxa[Qt(a)+clntNt(a)]A_t = \arg\max_a \left[ Q_t(a) + c \sqrt{\frac{\ln t}{N_t(a)}} \right]At=argamax[Qt(a)+cNt(a)lnt]
其中:
- Qt(a)Q_t(a)Qt(a) 是动作 aaa 的平均奖励估计
- Nt(a)N_t(a)Nt(a) 是动作 aaa 被尝试的次数
- ccc 是控制探索程度的参数
- ttt 是当前时间步
这个公式鼓励选择那些平均奖励高且尝试次数少的动作,很好地平衡了探索与利用。
3. 技术原理与实现
3.1 动态规划:当模型已知时
虽然我们主要关注无模型方法,但动态规划(Dynamic Programming, DP)提供了强化学习的理论基础,并且当我们知道环境模型时,它是解决MDP的有效方法。
3.1.1 策略评估(Policy Evaluation)
策略评估是计算给定策略 π\piπ 的值函数的过程。对于状态值函数,我们可以通过迭代应用贝尔曼期望方程来求解:
Vk+1(s)=∑aπ(a∣s)∑s′P(s′∣s,a)[R(s,a,s′)+γVk(s′)]V_{k+1}(s) = \sum_a \pi(a|s) \sum_{s'} P(s'|s,a) [R(s,a,s') + \gamma V_k(s')]Vk+1(s)=a∑π(a∣s)s′∑P(s′∣s,a)[R(s,a,s′)+γVk(s′)]
这个迭代过程会收敛到真实的状态值函数 Vπ(s)V^\pi(s)Vπ(s)。
3.1.2 策略迭代(Policy Iteration)
策略迭代包括两个交替进行的步骤:
- 策略评估:计算当前策略的值函数
- 策略改进:基于值函数改进策略
策略改进通常使用贪婪策略:
π′(s)=argmaxa∑s′P(s′∣s,a)[R(s,a,s′)+γVπ(s′)]\pi'(s) = \arg\max_a \sum_{s'} P(s'|s,a) [R(s,a,s') + \gamma V^\pi(s')]π′(s)=argamaxs′∑P(s′∣s,a)[R(s,a,s′)+γVπ(s′)]
可以证明,每次策略改进都能得到一个更好或相等的策略,最终收敛到最优策略 π∗\pi^*π∗。
3.1.3 价值迭代(Value Iteration)
价值迭代将策略评估和策略改进合并为一个步骤:
Vk+1(s)=maxa∑s′P(s′∣s,a)[R(s,a,s′)+γVk(s′)]V_{k+1}(s) = \max_a \sum_{s'} P(s'|s,a) [R(s,a,s') + \gamma V_k(s')]Vk+1(s)=amaxs′∑P(s′∣s,a)[R(s,a,s′)+γVk(s′)]
在价值迭代收敛后,我们可以通过一次贪婪策略提取得到最优策略:
π∗(s)=argmaxa∑s′P(s′∣s,a)[R(s,a,s′)+γV∗(s′)]\pi^*(s) = \arg\max_a \sum_{s'} P(s'|s,a) [R(s,a,s') + \gamma V^*(s')]π∗(s)=argamaxs′∑P(s′∣s,a)[R(s,a,s′)+γV∗(s′)]
价值迭代通常比策略迭代更高效,因为它不需要完整的策略评估过程。
3.2 蒙特卡洛方法:从完整回合中学习
当我们不知道环境模型时,我们可以使用蒙特卡洛(Monte Carlo, MC)方法,它通过采样完整的回合(episode)来估计值函数。
3.2.1 蒙特卡洛预测
对于预测问题(估计给定策略的值函数),蒙特卡洛方法使用平均回报作为值函数的估计:
Vπ(s)≈平均(Gt for all visits to s)V^\pi(s) \approx \text{平均}(G_t \text{ for all visits to } s)Vπ(s)≈平均(Gt for all visits to s)
有两种常见的实现方式:
- 首次访问蒙特卡洛(First-visit MC):只使用每个回合中第一次访问状态 sss 的回报
- 每次访问蒙特卡洛(Every-visit MC):使用每个回合中所有访问状态 sss 的回报
3.2.2 蒙特卡洛控制
对于控制问题(找到最优策略),我们可以结合策略评估和策略改进。一个经典的算法是在策略(on-policy)蒙特卡洛控制,使用ε-贪婪策略来平衡探索与利用。
另一种方法是离策略(off-policy)蒙特卡洛控制,它使用重要性采样(Importance Sampling)来从其他策略的经验中学习目标策略。
3.3 时序差分学习:从不完整回合中学习
时序差分(Temporal Difference, TD)学习结合了动态规划和蒙特卡洛方法的优点,它不需要环境模型,也不需要等待回合结束,就可以从经验中学习。
3.3.1 TD预测
TD预测方法使用TD目标(TD Target)来更新值函数:
V(St)←V(St)+α[Rt+1+γV(St+1)−V(St)]V(S_t) \leftarrow V(S_t) + \alpha [R_{t+1} + \gamma V(S_{t+1}) - V(S_t)]V(St)←V(St)+α[Rt+1+γV(St+1)−V(St)]
其中 α\alphaα 是学习率,Rt+1+γV(St+1)R_{t+1} + \gamma V(S_{t+1})Rt+1+γV(St+1) 是TD目标,Rt+1+γV(St+1)−V(St)R_{t+1} + \gamma V(S_{t+1}) - V(S_t)Rt+1+γV(St+1)−V(St) 是TD误差(TD Error)。
与蒙特卡洛方法相比,TD方法通常学习更快,因为它不需要等待回合结束就可以更新。
3.3.2 SARSA:在策略TD控制
SARSA是一种在策略的TD控制算法,它学习动作值函数 Q(s,a)Q(s,a)Q(s,a)。名称来源于它使用的五元组 (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)。
SARSA的更新公式是:
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 [R_{t+1} + \gamma Q(S_{t+1}, A_{t+1}) - Q(S_t, A_t)]Q(St,At)←Q(St,At)+α[Rt+1+γQ(St+1,At+1)−Q(St,At)]
SARSA使用ε-贪婪策略来选择动作,保证了持续的探索。
3.3.3 Q学习:离策略TD控制
Q学习是最著名的强化学习算法之一,它是一种离策略的TD控制算法,直接学习最优动作值函数 Q∗(s,a)Q^*(s,a)Q∗(s,a)。
Q学习的更新公式是:
Q(St,At)←Q(St,At)+α[Rt+1+γmaxaQ(St+1,a)−Q(St,At)]Q(S_t, A_t) \leftarrow Q(S_t, A_t) + \alpha [R_{t+1} + \gamma \max_a Q(S_{t+1}, a) - Q(S_t, A_t)]Q(St,At)←Q(St,At)+α[Rt+1+γamaxQ(St+1,a)−Q(St,At)]
与SARSA不同,Q学习在选择下一个状态的动作值时使用了最大化操作,而不是实际选择的动作。这使得Q学习能够学习最优策略,而不依赖于生成数据的策略。
3.4 函数逼近:处理大状态空间
到目前为止,我们假设状态和动作空间是离散且较小的,可以用表格来表示值函数。但在实际问题中,状态空间可能非常大甚至连续,我们需要使用函数逼近(Function Approximation)方法。
3.4.1 参数化值函数
我们用一个参数化的函数 v^(s,w)\hat{v}(s, \mathbf{w})v^(s,w) 来近似状态值函数 Vπ(s)V^\pi(s)Vπ(s),其中 w\mathbf{w}w 是权重向量。类似地,我们可以用 q^(s,a,w)\hat{q}(s, a, \mathbf{w})q^(s,a,w) 来近似动作值函数 Qπ(s,a)Q^\pi(s,a)Qπ(s,a)。
常见的函数逼近方法包括:
- 线性方法:使用特征向量的线性组合 v^(s,w)=wTx(s)\hat{v}(s, \mathbf{w}) = \mathbf{w}^T \mathbf{x}(s)v^(s,w)=wTx(s)
- 神经网络:使用多层神经网络来表示值函数
3.4.2 随机梯度下降
我们使用随机梯度下降(Stochastic Gradient Descent, SGD)来更新权重向量。对于预测问题,我们的目标是最小化均方误差:
J(w)=E[(Vπ(S)−v^(S,w))2]J(\mathbf{w}) = \mathbb{E} \left[ (V^\pi(S) - \hat{v}(S, \mathbf{w}))^2 \right]J(w)=E[(Vπ(S)−v^(S,w))2]
SGD的更新公式是:
wt+1=wt+α[Ut−v^(St,wt)]∇v^(St,wt)\mathbf{w}_{t+1} = \mathbf{w}_t + \alpha \left[ U_t - \hat{v}(S_t, \mathbf{w}_t) \right] \nabla \hat{v}(S_t, \mathbf{w}_t)wt+1=wt+α[Ut−v^(St,wt)]∇v^(St,wt)
其中 UtU_tUt 是值函数的目标值,如蒙特卡洛方法中的 GtG_tGt 或TD方法中的 Rt+1+γv^(St+1,wt)R_{t+1} + \gamma \hat{v}(S_{t+1}, \mathbf{w}_t)Rt+1+γv^(St+1,wt)。
3.5 深度Q网络(DQN)
深度Q网络(Deep Q-Network, DQN)是DeepMind在2013年提出的算法,它成功地将深度学习与强化学习结合,在Atari游戏上取得了超越人类的表现。
3.5.1 DQN的核心创新
DQN有两个关键创新,解决了使用神经网络进行函数逼近的不稳定性问题:
- 经验回放(Experience Replay):将经验存储在回放缓冲区中,训练时随机采样,打破了经验的相关性。
- 目标网络(Target Network):使用两个结构相同但参数不同的网络,主网络用来选择动作,目标网络用来计算TD目标,每隔一定步数将主网络的参数复制到目标网络。
3.5.2 DQN的损失函数
DQN的损失函数是TD误差的平方:
L(w)=E(s,a,r,s′)∼D[(r+γmaxa′q^(s′,a′,w−)−q^(s,a,w))2]L(\mathbf{w}) = \mathbb{E}_{(s,a,r,s') \sim \mathcal{D}} \left[ \left( r + \gamma \max_{a'} \hat{q}(s', a', \mathbf{w}^-) - \hat{q}(s, a, \mathbf{w}) \right)^2 \right]L(w)=E(s,a,r,s′)∼D[(r+γa′maxq^(s′,a′,w−)−q^(s,a,w))2]
其中 D\mathcal{D}D 是经验回放缓冲区,w−\mathbf{w}^-w− 是目标网络的参数。
3.5.3 DQN的算法流程
让我们用Mermaid流程图来展示DQN的算法流程:
这个流程图详细展示了DQN的训练过程,包括经验收集、经验回放、网络更新等关键步骤。
3.6 DQN的改进
DQN有许多改进版本,让我们介绍几个重要的:
3.6.1 Double DQN
Double DQN解决了DQN中Q值过估计的问题。它使用主网络选择动作,目标网络评估动作:
y=r+γq^(s′,argmaxaq^(s′,a,w),w−)y = r + \gamma \hat{q}(s', \arg\max_a \hat{q}(s', a, \mathbf{w}), \mathbf{w}^-)y=r+γq^(s′,argamaxq^(s′,a,w),w−)
3.6.2 优先经验回放(Prioritized Experience Replay)
优先经验回放不是均匀采样经验,而是根据TD误差的大小来优先采样那些更有学习价值的经验。
3.6.3 Dueling DQN
Dueling DQN将Q函数分解为状态值函数和优势函数:
Q(s,a;w,α,β)=V(s;w,β)+A(s,a;w,α)−1∣A∣∑a′A(s,a′;w,α)Q(s, a; \mathbf{w}, \alpha, \beta) = V(s; \mathbf{w}, \beta) + A(s, a; \mathbf{w}, \alpha) - \frac{1}{|A|} \sum_{a'} A(s, a'; \mathbf{w}, \alpha)Q(s,a;w,α,β)=V(s;w,β)+A(s,a;w,α)−∣A∣1a′∑A(s,a′;w,α)
这种分解使得网络能够分别学习哪些状态是有价值的,以及哪些动作在这些状态下是有优势的。
3.7 策略梯度方法
到目前为止,我们介绍的都是基于值函数的方法,即先学习值函数,再根据值函数得到策略。策略梯度(Policy Gradient)方法则直接参数化策略,并通过梯度上升来优化策略。
3.7.1 策略梯度定理
策略梯度定理为我们提供了策略梯度的计算公式:
∇θJ(θ)∝∑sμπ(s)∑aQπ(s,a)∇θπ(a∣s,θ)\nabla_\theta J(\theta) \propto \sum_s \mu^\pi(s) \sum_a Q^\pi(s, a) \nabla_\theta \pi(a|s, \theta)∇θJ(θ)∝s∑μπ(s)a∑Qπ(s,a)∇θπ(a∣s,θ)
其中 J(θ)J(\theta)J(θ) 是策略 πθ\pi_\thetaπθ 的性能指标,如平均奖励或起始状态的价值。
3.7.2 REINFORCE:蒙特卡洛策略梯度
REINFORCE是最经典的策略梯度算法,它使用完整回合的回报作为权重:
∇θJ(θ)≈Eπ[∑t=0T−1Gt∇θlnπ(At∣St,θ)]\nabla_\theta J(\theta) \approx \mathbb{E}_\pi \left[ \sum_{t=0}^{T-1} G_t \nabla_\theta \ln \pi(A_t | S_t, \theta) \right]∇θJ(θ)≈Eπ[t=0∑T−1Gt∇θlnπ(At∣St,θ)]
REINFORCE的更新公式是:
θt+1=θt+αGt∇θlnπ(At∣St,θt)\theta_{t+1} = \theta_t + \alpha G_t \nabla_\theta \ln \pi(A_t | S_t, \theta_t)θt+1=θt+αGt∇θlnπ(At∣St,θt)
3.7.3 Actor-Critic方法
Actor-Critic方法结合了策略梯度和值函数方法,它有两个组件:
- Actor:参数化策略 π(a∣s,θ)\pi(a|s, \theta)π(a∣s,θ),负责选择动作
- Critic:参数化值函数 V(s,w)V(s, \mathbf{w})V(s,w),负责评估Actor的动作
Actor-Critic的优势函数形式更新公式是:
δt=Rt+1+γV(St+1,w)−V(St,w)\delta_t = R_{t+1} + \gamma V(S_{t+1}, \mathbf{w}) - V(S_t, \mathbf{w})δt=Rt+1+γV(St+1,w)−V(St,w)
wt+1=wt+αwδt∇V(St,w)\mathbf{w}_{t+1} = \mathbf{w}_t + \alpha_w \delta_t \nabla V(S_t, \mathbf{w})wt+1=wt+αwδt∇V(St,w)
θt+1=θt+αθδt∇lnπ(At∣St,θt)\theta_{t+1} = \theta_t + \alpha_\theta \delta_t \nabla \ln \pi(A_t | S_t, \theta_t)θt+1=θt+αθδt∇lnπ(At∣St,θt)
其中 δt\delta_tδt 是TD误差,作为优势函数的估计。
3.8 实现:Q学习算法
现在让我们用Python实现一个简单的Q学习算法,来解决经典的"悬崖行走"(Cliff Walking)问题。
首先,让我们了解一下"悬崖行走"环境:
- 这是一个网格世界,Agent需要从起点走到终点
- 有一条悬崖区域,如果掉进去会得到-100的奖励并回到起点
- 每走一步会得到-1的奖励
- 目标是最大化总奖励
让我们开始实现:
import numpy as np
import matplotlib.pyplot as plt
from collections import defaultdict
import random
# 定义环境
class CliffWalkingEnv:
def __init__(self):
self.height = 4 # 网格高度
self.width = 12 # 网格宽度
self.start_state = (3, 0) # 起点
self.goal_state = (3, 11) # 终点
# 定义动作:0=上, 1=右, 2=下, 3=左
self.action_space = [0, 1, 2, 3]
self.action_effects = [(-1, 0), (0, 1), (1, 0), (0, -1)]
def reset(self):
"""重置环境到起点"""
self.current_state = self.start_state
return self.current_state
def step(self, action):
"""执行动作,返回下一状态、奖励、是否终止"""
# 计算新状态
row, col = self.current_state
d_row, d_col = self.action_effects[action]
new_row = max(0, min(self.height - 1, row + d_row))
new_col = max(0, min(self.width - 1, col + d_col))
new_state = (new_row, new_col)
# 计算奖励和终止
if new_state == self.goal_state:
reward = -1
done = True
elif new_row == 3 and 1 <= new_col <= 10: # 悬崖区域
reward = -100
new_state = self.start_state # 回到起点
done = False
else:
reward = -1
done = False
self.current_state = new_state
return new_state, reward, done
def render(self):
"""可视化当前状态"""
grid = [['.' for _ in range(self.width)] for _ in range(self.height)]
# 标记悬崖
for col in range(1, self.width - 1):
grid[3][col] = 'C'
# 标记起点和终点
grid[self.start_state[0]][self.start_state[1]] = 'S'
grid[self.goal_state[0]][self.goal_state[1]] = 'G'
# 标记当前位置
grid[self.current_state[0]][self.current_state[1]] = 'A'
# 打印网格
for row in grid:
print(' '.join(row))
print()
# 定义Q学习Agent
class QLearningAgent:
def __init__(self, env, learning_rate=0.1, discount_factor=0.99, epsilon=0.1):
self.env = env
self.learning_rate = learning_rate
self.discount_factor = discount_factor
self.epsilon = epsilon
self.q_table = defaultdict(lambda: np.zeros(len(env.action_space)))
def get_action(self, state):
"""根据ε-贪婪策略选择动作"""
if random.random() < self.epsilon:
# 探索:随机选择动作
return random.choice(self.env.action_space)
else:
# 利用:选择Q值最大的动作
return np.argmax(self.q_table[state])
def update(self, state, action, reward, next_state, done):
"""更新Q表"""
current_q = self.q_table[state][action]
if done:
target_q = reward
else:
target_q = reward + self.discount_factor * np.max(self.q_table[next_state])
# 更新Q值
self.q_table[state][action] = current_q + self.learning_rate * (target_q - current_q)
def train(self, num_episodes):
"""训练Agent"""
episode_rewards = []
for episode in range(num_episodes):
state = self.env.reset()
total_reward = 0
done = False
while not done:
action = self.get_action(state)
next_state, reward, done = self.env.step(action)
self.update(state, action, reward, next_state, done)
state = next_state
total_reward += reward
episode_rewards.append(total_reward)
# 打印训练进度
if (episode + 1) % 100 == 0:
avg_reward = np.mean(episode_rewards[-100:])
print(f"Episode {episode+1}/{num_episodes}, Average Reward (last 100): {avg_reward}")
return episode_rewards
def test(self, num_episodes, render=False):
"""测试训练好的Agent"""
total_rewards = []
for episode in range(num_episodes):
state = self.env.reset()
episode_reward = 0
done = False
if render:
print(f"Test Episode {episode+1}")
self.env.render()
while not done:
# 测试时不探索,只利用
action = np.argmax(self.q_table[state])
next_state, reward, done = self.env.step(action)
state = next_state
episode_reward += reward
if render:
self.env.render()
total_rewards.append(episode_reward)
avg_reward = np.mean(total_rewards)
print(f"Average Test Reward: {avg_reward}")
return total_rewards
# 可视化训练结果
def plot_training_results(rewards, window=100):
"""绘制训练过程中的奖励曲线"""
plt.figure(figsize=(10, 5))
# 计算移动平均
if len(rewards) >= window:
moving_avg = np.convolve(rewards, np.ones(window)/window, mode='valid')
plt.plot(moving_avg, label=f'Moving Average (window={window})')
plt.plot(rewards, alpha=0.3, label='Episode Rewards')
plt.xlabel('Episode')
plt.ylabel('Total Reward')
plt.title('Q-Learning Training Results on Cliff Walking')
plt.legend()
plt.grid(True)
plt.show()
# 主程序
if __name__ == "__main__":
# 创建环境和Agent
env = CliffWalkingEnv()
agent = QLearningAgent(env, learning_rate=0.1, discount_factor=0.99, epsilon=0.1)
# 训练Agent
print("Starting training...")
rewards = agent.train(num_episodes=500)
# 可视化训练结果
plot_training_results(rewards)
# 测试Agent
print("\nStarting testing...")
agent.test(num_episodes=5, render=True)
这个实现包含了一个简单的悬崖行走环境和一个Q学习Agent。我们可以通过调整学习率、折扣因子和探索率等超参数来观察不同的训练效果。
3.9 实现:简单的DQN
现在让我们实现一个简单的DQN算法,解决OpenAI Gym的CartPole问题。CartPole是一个经典的控制问题,目标是通过左右移动小车来保持杆的平衡。
首先,我们需要安装必要的库:
pip install gym numpy matplotlib torch
然后,让我们实现DQN:
import gym
import numpy as np
import matplotlib.pyplot as plt
from collections import deque, namedtuple
import random
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
# 定义经验元组
Experience = namedtuple('Experience', ('state', 'action', 'reward', 'next_state', 'done'))
# 定义经验回放缓冲区
class ReplayBuffer:
def __init__(self, capacity):
self.buffer = deque(maxlen=capacity)
def push(self, *args):
"""添加经验到缓冲区"""
self.buffer.append(Experience(*args))
def sample(self, batch_size):
"""从缓冲区随机采样批次"""
experiences = random.sample(self.buffer, batch_size)
return experiences
def __len__(self):
return len(self.buffer)
# 定义Q网络
class QNetwork(nn.Module):
def __init__(self, state_size, action_size, hidden_size=64):
super(QNetwork, self).__init__()
self.fc1 = nn.Linear(state_size, hidden_size)
self.fc2 = nn.Linear(hidden_size, hidden_size)
self.fc3 = nn.Linear(hidden_size, action_size)
def forward(self, x):
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
return self.fc3(x)
# 定义DQN Agent
class DQNAgent:
def __init__(self, state_size, action_size, hidden_size=64,
learning_rate=1e-3, buffer_size=10000, batch_size=64,
gamma=0.99, target_update=10, epsilon_start=1.0,
epsilon_end=0.01, epsilon_decay=0.995):
self.state_size = state_size
self.action_size = action_size
self.batch_size = batch_size
self.gamma = gamma
self.target_update = target_update
self.epsilon = epsilon_start
self.epsilon_end = epsilon_end
self.epsilon_decay = epsilon_decay
# 设备配置
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Q网络和目标网络
self.q_network = QNetwork(state_size, action_size, hidden_size).to(self.device)
self.target_network = QNetwork(state_size, action_size, hidden_size).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=learning_rate)
# 经验回放缓冲区
self.memory = ReplayBuffer(buffer_size)
# 步数计数器
self.step_count = 0
def get_action(self, state):
"""根据ε-贪婪策略选择动作"""
# 衰减epsilon
self.epsilon = max(self.epsilon_end, self.epsilon * self.epsilon_decay)
if random.random() < self.epsilon:
# 探索:随机选择动作
return random.randrange(self.action_size)
else:
# 利用:选择Q值最大的动作
state = torch.FloatTensor(state).unsqueeze(0).to(self.device)
self.q_network.eval()
with torch.no_grad():
action_values = self.q_network(state)
self.q_network.train()
return np.argmax(action_values.cpu().data.numpy())
def step(self, state, action, reward, next_state, done):
"""存储经验并学习"""
self.memory.push(state, action, reward, next_state, done)
self.step_count += 1
# 更新目标网络
if self.step_count % self.target_update == 0:
self.target_network.load_state_dict(self.q_network.state_dict())
# 如果缓冲区有足够的经验,就学习
if len(self.memory) > self.batch_size:
experiences = self.memory.sample(self.batch_size)
self.learn(experiences)
def learn(self, experiences):
"""使用经验批次更新Q网络"""
# 将经验转换为张量
states = torch.FloatTensor([e.state for e in experiences]).to(self.device)
actions = torch.LongTensor([e.action for e in experiences]).unsqueeze(1).to(self.device)
rewards = torch.FloatTensor([e.reward for e in experiences]).unsqueeze(1).to(self.device)
next_states = torch.FloatTensor([e.next_state for e in experiences]).to(self.device)
dones = torch.FloatTensor([e.done for e in experiences]).unsqueeze(1).to(self.device)
# 计算当前Q值
q_current = self.q_network(states).gather(1, actions)
# 计算目标Q值
self.target_network.eval()
with torch.no_grad():
q_next_max = self.target_network(next_states).max(1)[0].unsqueeze(1)
q_target = rewards + (1 - dones) * self.gamma * q_next_max
self.target_network.train()
# 计算损失
loss = F.mse_loss(q_current, q_target)
# 更新网络
更多推荐

所有评论(0)