PyTorch强化学习实战(29)------深入解析强化学习中的探索策略
-
- [0. 前言](#0. 前言)
- [1. 探索策略的重要性](#1. 探索策略的重要性)
- [2. 𝜖-贪婪方法的问题](#2. 𝜖-贪婪方法的问题)
- [3. 高效探索策略](#3. 高效探索策略)
-
- [3.1 噪声网络](#3.1 噪声网络)
- [3.2 基于计数的方法](#3.2 基于计数的方法)
- [3.3 基于预测的方法](#3.3 基于预测的方法)
- [4. MountainCar 环境](#4. MountainCar 环境)
-
- [4.1 DQN + 𝜖-greedy](#4.1 DQN + 𝜖-greedy)
- [4.2 DQN + 噪声网络](#4.2 DQN + 噪声网络)
- [4.3 DQN + 状态计数](#4.3 DQN + 状态计数)
- [4.4 近端策略优化方法](#4.4 近端策略优化方法)
- [4.5 PPO + 噪声网络](#4.5 PPO + 噪声网络)
- [4.6 PPO + 状态计数](#4.6 PPO + 状态计数)
- [4.7 PPO + 网络蒸馏](#4.7 PPO + 网络蒸馏)
- [5. Atari 环境](#5. Atari 环境)
- [5.1 DQN + 𝜖-贪婪](#5.1 DQN + 𝜖-贪婪)
-
- [5.2 DQN + 噪声网络](#5.2 DQN + 噪声网络)
- [5.3 PPO](#5.3 PPO)
- 小结
- 系列链接
0. 前言
在本节中,我们将讨论强化学习 (Reinforcement Learning, RL)中的探索问题。探索/利用困境是 RL 的基础性问题,对高效学习至关重要。然而在之前的学习中,我们使用的是相对简单的环境探索方法------大多数情况下采用𝜖-贪婪动作选择。现在我们将深入 RL 的探索子领域,因为更复杂的环境可能需要比𝜖-贪婪策略更高效的探索策略。
我们将通过实现所述方法来解决一个看似简单但仍具挑战性的问题------MountainCar 环境。这将帮助我们更好地理解这些方法的原理、实现方式及其行为特征。此后,我们将尝试解决Atari游戏环境中的更具挑战性的问题。
1. 探索策略的重要性
本专栏已经讨论了许多环境和方法,在几乎每一节中都提到了探索的重要性。我们已经对有效环境探索的重要性有所理解,本节我们将重点阐述其主要原因。
在此之前,可能有必要先达成一致,定义一下"有效探索"这个术语。在理论强化学习 (Reinforcement Learning, RL)中,存在严首先需要明确"有效探索"的定义。理论 RL 中对此有严格界定,但其高层理念简单直观:当智能体不再重复访问已熟悉的环境状态,而是积极寻找新经验时,探索就是有效的。正如我们多次讨论的,探索必须与利用相平衡------后者指运用现有知识以最高效方式获取最佳奖励。接下来,我们快速了解为何有效探索如此关键。
首先,良好的环境探索对学习优质策略具有根本性影响。若奖励稀疏且智能体仅在特定罕见条件下获得良好奖励,可能多轮探索才偶遇一次正向回报。此时,学习过程能否有效且全面地探索环境,将直接决定能否获得更多可供学习的高奖励样本。
其次,在 RL 实际应用中经常出现这种情况:缺乏良好探索可能导致智能体永远无法获得正向奖励,使得所有学习努力付诸东流。如果没有优质样本可供学习,即使采用最高效的 RL 方法,智能体也只能学会"无法获得好奖励"的结论。我们周围的许多实际问题皆属此类。例如 MountainCar 环境------其动态规则简单,但因奖励稀疏而颇具挑战性。
另一方面,即使奖励不稀疏,有效探索也能通过提升收敛性和训练稳定性来加速训练进程。这是因为来自环境的样本变得更加多样,减少了与环境交互的需求。因此,RL 方法有望在更短时间内学到更优策略。
2. 𝜖-贪婪方法的问题
在本专栏中,我们使用了𝜖-贪婪探索策略,这作为一种简单但尚可接受的环境探索方法。其核心思想是以 𝜖 𝜖 𝜖 的概率采取随机动作,否则( 1 − 𝜖 1-𝜖 1−𝜖 概率)根据策略贪婪的选择动作。通过调整超参数 0 ≤ 𝜖 ≤ 1 0 ≤ 𝜖 ≤ 1 0≤𝜖≤1,我们可以改变探索的比例。这种方法被应用于本专栏中大多数基于价值的方法。类似思路也见于基于策略的方法------当网络返回动作概率分布时,为避免网络对特定动作过度确信(对某个动作返回概率 1,其他动作概率 0),我们添加了熵损失项(即概率分布的熵乘以某个超参数)。在训练初期,熵损失通过正则化概率分布促使网络采取随机动作;但在后期阶段,当环境探索充分且奖励较高时,策略梯度会主导熵正则化作用。不过该超参数需要精心调优才能正常工作。
高层面上看,这两种方法本质相同:通过引入动作随机性来探索环境。但最新研究表明这种方法远非最佳方法:
- 在基于价值的迭代方法中,轨迹中随机动作的引入会导致Q值估计出现偏差。贝尔曼方程假设下一状态的Q值来自最大Q值动作,即轨迹剩余部分应遵循最优行为。但𝜖-贪婪策略可能采取非最优的随机动作,这些轨迹片段会在回放缓存中存留很长时间(直到 𝜖 𝜖 𝜖 衰减且旧样本被清除),在此期间我们将学习到错误的Q值
- 当轨迹中注入随机动作时,策略每一步都在变化。以 𝜖 𝜖 𝜖 值或熵损失系数定义的频率,我们的轨迹不断在随机策略与当前策略间切换。当需要多步才能到达环境状态空间中某些孤立区域时,这种切换可能导致状态空间覆盖不足
为说明最后一个问题,我们参考 Strehl 和 Littman 论文《An analysis of model-based interval estimation for Markov decision processes》中的一个简单示例,"River Swim",该环境模拟智能体渡河过程,包含六个状态和两个动作(左/右)。状态 1 和 6 代表河岸,状态 2 至 5 代表水中位置。下图展示了前两个状态( 1 和 2 )的转移示意图:

在第一个状态(标有"1"的圆圈)中,智能体站在河岸地面上。唯一可采取的动作是向右(以实线表示),这意味着进入河流并逆流游向状态 2。但由于水流湍急,从状态 1 执行向右动作的成功概率仅为 60% (状态 1 指向状态 2 的实线)。另有 40% 的概率水流会将我们冲回状态 1 (状态 1 指向自身的实线)。
在第二个状态(标有"2"的圆圈)中,我们有两个动作:向左(以连接状态2和1的虚线表示,该动作总能成功,因为水流会将我们冲回河岸)和向右(以虚线表示),即逆流游向状态3。与之前相同,逆流游泳十分困难,因此从状态 2 到达状态 3 的概率仅为 35% (连接状态 2 和 3 的虚线)。有 60% 的概率,向左动作会使我们停留在原地(连接状态 2 自身的虚线)。但有时尽管我们努力向右,仍有 5% 的概率会意外被冲回状态 1 (连接状态 2 和 1 上方的虚线)。
如前所述,河流游泳环境共有六个状态,但状态3、4和5的转移规则与状态 2 完全相同。最后一个状态 6 与状态 1 类似,只有一个可用动作:向左(即游回)。在下图中可以看到完整的转移示意图(其中向右动作转移用实线表示,向左动作转移用虚线表示):

就奖励机制而言,智能体在状态 1 到 5 之间的转移仅能获得1单位的小额奖励,但进入状态 6 将获得 1000 单位的极高奖励------这是对所有逆流游泳努力的补偿。
尽管环境简单,但其结构对𝜖-贪婪策略完全探索状态空间的能力构成了挑战。为验证这一点,我们实现该环境的简易模拟(代码详见 riverswim.py)。模拟智能体始终采取随机动作 ( 𝜖 = 1 𝜖=1 𝜖=1),模拟结果是各状态访问频次统计。单回合步数限制为 10 步(可通过命令行调整),实验结果如下:
shell
$ python3 riverswim.py
1: 40
2: 39
3: 17
4: 3
5: 1
6: 0
上述输出中每行显示状态编号及其在模拟过程中的访问次数。使用默认命令行选项进行 100 步( 10 回合)模拟时,智能体从未到达状态 6,仅访问状态 5 一次。增加回合数后情况略有改善但仍不理想:
shell
$ python3 riverswim.py -n 1000
1: 441
2: 452
3: 93
4: 12
5: 2
6: 0
即使将模拟回合数增加 10 倍,我们仍未访问状态 6,因此智能体完全不知道该处存在高额奖励。
只有当模拟达到 10000 回合时,我们才得以进入状态 6,但仅 5 次(占全部步数的 0.05%):
shell
$ python3 riverswim.py -n 10000
1: 4056
2: 4506
3: 1095
4: 281
5: 57
6: 5
因此,即使采用最优的 RL 方法,训练效率也难言理想。需要注意的是,本节仅包含六个状态。试想若存在 20 或 50 个状态(这种情况并不罕见,例如在 Atari 游戏中可能需要做出数百次决策才能触发特定事件),效率将会及其低下。我们也可以使用 riverswim.py 工具进行实验,该工具支持修改随机种子、回合步数、总步数甚至环境中的状态数量。
这个简单示例揭示了随机动作探索的缺陷:通过随机行动,智能体并未主动探索环境,而是寄希望于随机动作能带来新经验------这并非总是最优策略。接下来,我们探讨更高效的探索问题解决方案。
3. 高效探索策略
在本节中,概述一系列高效探索策略方案。我们将探讨以下三种探索方法:
- 策略中的随机性,在用于采样的策略中加入随机性,这一方法的代表是噪声网络
- 基于计数的方法,记录智能体访问特定状态的次数。我们将检验两种方法:直接状态计数和伪计数方法
- 基于预测的方法,通过从状态中预测某些信息,并根据预测质量判断智能体对该状态的熟悉程度。为说明该方法,我们将研究策略蒸馏方法------该方法在《Montezuma's Revenge》等难探索型
Atari游戏上展现了先进性能
在具体实现这些方法之前,让我们先深入理解其原理。
3.1 噪声网络
我们首先从已熟悉的方法开始。在讨论深度Q网络 (Deep Q-Network, DQN)扩展时,我们参考 Hessel 等人的研究介绍了噪声网络方法。其核心思想是在网络权重中添加高斯噪声,并通过反向传播学习噪声参数(均值和方差),这与学习模型权重的方式相同。该简单方法在《Pong》游戏训练中取得了显著提升。
高层面上看,这与𝜖-贪婪方法似乎非常相似,但 Fortunato 等人指出了本质区别:随机性的应用方式不同。𝜖-贪婪将随机性添加到动作选择中,而噪声网络将随机性注入网络本身(靠近输出的若干全连接层),这意味着向当前策略添加了随机性。此外,噪声参数可在训练过程中学习,因此训练过程可根据需要增减策略随机性。
根据论文所述,噪声层需要定期采样噪声,这意味着训练样本并非由当前策略产生,而是由策略集合生成。通过这种方式,我们的探索变得具有导向性------因为添加到权重中的随机值会产生不同的策略。
3.2 基于计数的方法
这类方法基于一种直观思想:优先访问尚未探索过的状态。在简单情况下(当状态空间不大且不同状态易于区分时),我们直接统计状态或状态+动作的出现次数,并倾向于访问计数较低的状态。
这可以通过额外奖励来实现------该奖励并非来自环境,而是基于状态访问计数。文献中称此类奖励为内在奖励,而环境提供的奖励则称为外在奖励。一种常见的奖励构建方式是使用赌博机 (bandits) 探索方法: r t = c 1 N ~ ( s ) r_t =c\frac 1{\sqrt {\tilde N(s)}} rt=cN~(s) 1,其中, N ~ ( s ) \tilde N(s) N~(s) 表示状态s的访问计数或伪计数,值 c c c 定义了内在奖励的权重。
如果状态数量较少,比如在表格学习的情况下,我们可以直接对它们计数。在更复杂的情况下(状态过多时),需要引入状态变换机制,如哈希函数或状态嵌入。
对于伪计数方法, N ~ ( s ) \tilde N(s) N~(s) 可分解为密度函数和总访问状态数: N ~ ( s ) = ρ ( x ) n ( x ) \tilde N(s) = ρ(x)n(x) N~(s)=ρ(x)n(x),其中 ρ ( x ) ρ(x) ρ(x) 是表示状态 x x x 似然度的"密度函数",由神经网络近似。
引入内在奖励的特殊情况称为好奇心驱动探索------完全忽略环境奖励,训练和探索 100% 由智能体经验的新奇性驱动。令人惊讶的是,这种方法不仅能高效发现环境中的新状态,还能学习到相当优秀的策略。
3.3 基于预测的方法
第三类探索方法基于另一种思路:通过环境数据预测某些信息。若智能体能做出准确预测,说明已充分熟悉当前情境,无需继续探索。
但当异常情况发生且预测严重偏离时,可能意味着需要关注当前状态。虽然存在多种实现方式,但本节将重点讨论 Burda 等人 2018 年在论文《Exploration by random network distillation》中提出的方法。作者在 Atari 的难探索类游戏中取得了先进性能。
该方法相当简洁:我们添加内在奖励,其计算基于一个神经网络(正在训练)预测另一个随机初始化(未训练)神经网络输出的能力。两个网络的输入都是当前观测值,内在奖励与预测的均方误差 (Mean Squared Error, MSE) 成正比。
4. MountainCar 环境
本节将在一个简单但仍具挑战性的环境中实现并比较不同探索方法的有效性。该环境属于经典强化学习 (Reinforcement Learning, RL)问题,与 CartPole 问题非常相似。但与 CartPole 不同的是,MountainCar 问题从探索角度来看颇具挑战性。
该问题的示意图如下图所示,一辆小车从山谷底部出发,可通过左右移动尝试到达右侧山顶。

此处的难点在于环境动力学和动作空间的特殊设计。要到达山顶,需要以特定方式施加动作使小车来回摆动以加速。换句话说,智能体需要连续多个时间步施加动作才能使小车加速并最终抵达山顶。显然,这种动作协调很难通过随机动作实现,因此从探索角度来看该问题非常困难,与 River Swim 示例非常相似。
在 Gymnasium 中,这个环境的名称是 MountainCar-v0,具有极其简单的观测和动作空间。观测值仅包含两个数字:第一个表示小车的水平位置,第二个表示小车速度。动作为 0、1 或 2,其中 0 表示向左推车,1 表示不施力,2 表示向右推车。以下是在 Python REPL 中的简单演示:
shell
>>> import gymnasium as gym
>>> e = gym.make("MountainCar-v0")
>>> e.reset()
(array([-0.47614264, 0. ], dtype=float32), {})
>>> e.observation_space
Box([-1.2 -0.07], [0.6 0.07], (2,), float32)
>>> e.action_space
Discrete(3)
>>> e.step(0)
(array([-0.47749737, -0.00135472], dtype=float32), -1.0, False, False, {})
>>> e.step(0)
(array([-0.48019674, -0.00269938], dtype=float32), -1.0, False, False, {})
>>> e.step(0)
(array([-0.4842207 , -0.00402398], dtype=float32), -1.0, False, False, {})
可以看到,每一步我们都获得 -1 的奖励,因此智能体需要学会尽快到达目标,以获得尽可能少的总负奖励。默认情况下,步数限制为 200 步,如果我们未能到达目标(这种情况经常发生),总奖励将为 -200。
4.1 DQN + 𝜖-greedy
我们将使用的第一个方法是传统的𝜖-贪婪探索方法,其实现位于源文件 mcar_dqn.py 中。该程序在深度Q网络 (Deep Q-Network, DQN)方法基础上实现了多种探索策略,允许通过 -p 命令行选项进行选择。要启动标准𝜖-贪婪方法,需要传递选项 -p egreedy。在训练过程中,我们会在前 10 5 10^5 105 个训练步数中将 𝜖 𝜖 𝜖 从 1.0 线性衰减至 0.02。
训练速度相当快------完成 10 5 10^5 105 个训练步数仅需两到三分钟。但从下图来看,显然在这 10 5 10^5 105 步(即 500 个回合)中,我们从未到达过目标状态。结果并不理想,因为 𝜖 𝜖 𝜖 值已经衰减完毕,意味着未来将不再进行探索。

我们仍然执行的 2% 随机动作远远不够,因为到达山顶需要数十个协调步骤( MountainCar 环境中最优策略的总奖励约为 -80)。即使继续训练数百万步,从环境中获得的也只会是耗时 200 步、总奖励为 -200 的回合。这再次证明了探索的重要性。无论采用何种训练方法,缺乏适当探索都可能导致训练失败。
那么该如何解决?若坚持使用𝜖-贪婪策略,唯一选择是延长探索时间(通过调整𝜖衰减速度)。我们可以尝试-p egreedy模式的超参数调整,但本节采取了极端方案:实现了 -p egreedy-long 超参数集。在此模式下,我们保持 𝜖 = 1.0 𝜖=1.0 𝜖=1.0 直到至少出现一个总奖励优于 -200 的回合。达到该条件后,我们开始正常训练,在后续 10 6 10^6 106 帧中将 𝜖 𝜖 𝜖 从 1.0 衰减至 0.02。由于初始探索阶段不进行训练,速度通常可提升 5-10 倍。启动该模式的命令为:
shell
$ python mcar_dqn.py -n t1 -p egreedy-long
但即使改进𝜖-贪婪策略,由于环境复杂性仍无法解决问题。实验表明,使用该版本运行了五小时,经过 50 万个回合后仍未遇到任何成功到达目标的样本,最终只能放弃。当然,我们也可以尝试更长的训练时间。
4.2 DQN + 噪声网络
要将噪声网络方法应用于 MountainCar 问题,只需将网络中的两个全连接层之一替换为 NoisyLinear 类,架构将变为如下形式:

噪声线性层 (NoisyLinear) 类与噪声网络一节中的版本的唯一区别是,当前版本具有显式的 sample_noise() 方法来更新噪声张量,因此需要在每个训练迭代中调用该方法;否则训练过程中噪声将保持恒定。这一修改是为后续基于策略方法的实验做准备,因为这些方法要求噪声在相对较长的轨迹周期内保持稳定。无论如何,修改很简单------我们只需定期调用该方法。在 DQN 方法中,每个训练迭代都会调用该方法。与噪声网络一节相同,NoisyLinear 的实现取自 TorchRL 库,代码保持不变。要激活噪声网络,需使用 -p noisynet 命令行参数运行训练。

如图所示,训练过程未能达到代码要求的 -130 平均测试奖励,但仅经过 7000 次训练步骤( 20 分钟训练),我们就发现了目标状态------与𝜖-贪婪方法经过 5 小时试错仍未找到任何目标状态相比,这已是重大进展。
从测试步骤图可以看到,部分测试步骤少于 100 步,这已非常接近最优策略。但这些成功案例出现的频率还不足以将平均测试奖励推至 -130 以下。
4.3 DQN + 状态计数
我们将应用于 DQN 方法的最后一种探索技术是基于状态计数的。由于状态空间仅包含两个浮点值,我们将通过对观测值保留三位小数进行离散化处理,这既能提供区分不同状态的足够精度,又能将相似状态分组。对于每个独立状态,我们将记录其出现次数,并据此给予智能体额外奖励。对于异策略方法而言,在训练过程中修改奖励可能并非最佳方案,但我们仍将检验其效果。
首先,我们通过环境包装器来维护计数器并计算内在奖励值。包装器代码位于 common.py 模块中。
(1) 首先查看构造函数:
python
class PseudoCountRewardWrapper(gym.Wrapper):
def __init__(self, env: gym.Env, hash_function = lambda o: o,
reward_scale: float = 1.0):
super(PseudoCountRewardWrapper, self).__init__(env)
self.hash_function = hash_function
self.reward_scale = reward_scale
self.counts = collections.Counter()
在构造函数中,我们接收待包装的环境、可选的观测值哈希函数以及内在奖励的缩放系数。同时创建计数器容器,用于映射哈希状态到其出现次数。
(2) 然后,我们定义辅助函数:
python
def _count_observation(self, obs) -> float:
h = self.hash_function(obs)
self.counts[h] += 1
return np.sqrt(1/self.counts[h])
此函数将计算状态的内在奖励值。它对观察进行哈希,更新计数器,并使用公式计算奖励。
(3) 包装器的最后一个方法负责环境步进处理:
python
def step(self, action):
obs, reward, done, is_tr, info = self.env.step(action)
extra_reward = self._count_observation(obs)
return obs, reward + self.reward_scale * extra_reward, done, is_tr, info
此处调用辅助函数获取奖励,并返回外在奖励与内在奖励之和。
(4) 应用包装器时需要传递哈希函数:
python
def counts_hash(obs: np.ndarray):
r = obs.tolist()
return tuple(map(lambda v: round(v, 3), r))
三位小数可能过于精细,所以我们可以尝试使用不同的方式对状态进行哈希。
启动训练需向程序传递 -p counts 参数。下图显示了训练和测试奖励图表。由于训练环境被 PseudoCountReward 包装器封装,训练期间的奖励值会高于测试值。

如图所示,使用该方法我们未能达到 -130 的平均测试奖励,但已非常接近该值。仅用 10 分钟就发现了目标状态。
4.4 近端策略优化方法
接下来,我们在 MountainCar 问题上采用同策略方法------近端策略优化 (Proximal Policy Optimization, PPO)。选择该方法的动机如下:
首先,正如在 DQN + 噪声网络 的示例中看到的,当优质样本稀少时,深度Q网络 (Deep Q-Network, DQN)难以快速适应。这可以通过增大回放缓存容量并转向优先缓存来解决,或者我们可以尝试同策略方法------它能根据获得的经验立即调整策略。
选择这种方法的另一个原因是训练过程中对奖励的修改。基于计数的探索和策略蒸馏引入了内在奖励成分,这可能会随时间变化。基于价值的方法对底层奖励的修改可能很敏感,因为它们本质上需要在训练过程中重新学习价值。同策略方法则不应存在此问题,因为奖励增加仅会在策略梯度方面更强调高奖励样本。
为实现该方法,我们在 mcar_ppo.py 文件中实现了 PPO 与多种探索策略的结合,代码与 PPO 一节差异不大。要启动未添加额外探索调整的标准 PPO,应运行命令:python mcar_ppo.py -n t1 -p ppo。该版本未专门进行探索------纯粹依赖训练初期的随机权重初始化。
需要注意的是,PPO 是属于策略梯度方法方法,通过限制新旧策略间的 KL 散度来避免剧烈的策略更新。我们的网络有两个输出头:演员网络 (actor) 和评论家网络 (critic)。演员网络返回动作的概率分布(即策略),而评论员估计状态的价值。评论家使用MSE损失训练,而演员由 PPO 替代目标驱动。除了这两个损失外,我们还通过超参数 β β β 缩放熵损失来正则化策略。PPO 网络结构如下:

在三小时后停止了训练,因为未显示任何改进。目标状态在 1.5 小时和 3 万个回合后被发现。由于 PPO 结果并不突出,接下来我们尝试通过额外探索技巧进行扩展。
4.5 PPO + 噪声网络
与 DQN 方法类似,我们可以将噪声网络探索方法应用于 PPO。为此,需要将演员网络的输出层替换为 NoisyLinear 层。仅需修改演员网络,因为我们希望仅向策略注入噪声,而不影响价值估计。
应用噪声网络存在一个细微差别:随机噪声的采样时机。在首次接触噪声网络时,噪声是在 NoisyLinear 层每次 forward() 传递时采样的。根据原始研究论文,这对异策略方法可行,但对同策略方法需要不同处理。实际上,当进行同策略训练时,我们获取当前策略产生的训练样本并计算策略梯度,该梯度应推动策略改进。噪声网络的目标是注入随机性,但正如我们讨论的,我们更倾向于定向探索而非每步后随机改变策略。考虑到这一点,NoisyLinear 层中的随机成分不需要在每次 forward() 后更新,而应大幅降低频率。在本节中,我们在每个 PPO 批次( 2048 次状态转移)后重新采样噪声。
像之前一样,对 PPO + 噪声网络训练 3 小时。但这次目标状态在 1.8 万个回合后就被发现,此外,根据训练步数统计,训练过程曾数次以最优方式驱动小车(步数少于 100 次)。但这些成功并未最终形成最优策略。
4.6 PPO + 状态计数
在这种情况下,我们为 PPO 方法实现了完全相同的基于计数的探索策略(采用三位小数哈希处理),可通过向训练过程传递 -p counts 参数来触发。
实验表明,该方法能够在 1.5 小时内解决环境问题(获得高于 -130 的平均奖励),共需要 6.1 万个回合。
训练在 2.3 万个回合后发现了目标状态。随后又经过 4 万个回合的优化,最终将策略优化至最佳步数。
4.7 PPO + 网络蒸馏
接下来,实现 Burda 等人提出的网络蒸馏方法。该方法引入了两个额外的神经网络,两者都需要像价值头那样将观测值映射为一个数值,区别在于使用方式:第一个网络随机初始化并保持未训练状态(作为参考网络),第二个网络通过最小化与第一个网络的 MSE 损失进行训练。此外,两个网络输出的绝对差值被用作内在奖励成分。
其核心思想是:智能体对某个状态的探索越充分,第二个(已训练)网络对第一个(未训练)网络输出的预测就越准确,这将导致添加到总奖励中的内在奖励减少,从而降低分配给样本的策略梯度。
论文作者建议训练独立的价值头来分别预测内在和外在奖励成分,但本节中为保持简洁,采用与基于计数的探索方法相同的方式------在包装器中直接合并两种奖励,这将代码修改量降至最低。
关于额外网络架构,我们进行了小型实验并尝试了多种架构组合。最佳结果来自参考网络采用三层结构而训练网络仅使用单层的配置,这有助于防止训练网络过拟合(因为我们的观测空间不大)。两个网络实现在 ppo.py 模块的 MountainCarNetDistillery 类中:
python
class MountainCarNetDistillery(nn.Module):
def __init__(self, obs_size: int, hid_size: int = 128):
super(MountainCarNetDistillery, self).__init__()
self.ref_net = nn.Sequential(
nn.Linear(obs_size, hid_size),
nn.ReLU(),
nn.Linear(hid_size, hid_size),
nn.ReLU(),
nn.Linear(hid_size, 1),
)
self.ref_net.train(False)
self.trn_net = nn.Sequential(
nn.Linear(obs_size, 1),
)
def forward(self, x):
return self.ref_net(x), self.trn_net(x)
def extra_reward(self, obs):
r1, r2 = self.forward(torch.FloatTensor([obs]))
return (r1 - r2).abs().detach().numpy()[0][0]
def loss(self, obs_t):
r1_t, r2_t = self.forward(obs_t)
return F.mse_loss(r2_t, r1_t).mean()
除了返回两个神经网络输出的 forward() 方法外,该类还包含两个辅助方法:用于计算内在奖励和获取两个网络间损失的方法。
要启动训练,需向 mcar_ppo.py 程序传递 -p distill 参数。实验表明,解决问题需要 3.3 万个回合,这比噪声网络方法几乎减少了一半。
与之前相同,由于内在奖励成分的存在,训练回合显示出更高的奖励值。在智能体发现目标状态之前,所有状态都是单调且可预测的;但一旦它掌握了在 200 步之前提前结束回合的方法,损失就显著增长。
可以看到配备探索扩展功能的 DQN 和 PPO 方法都能解决 MountainCar 环境。具体方法选择取决于实际需求,但重要的是要了解可用的不同探索策略。
5. Atari 环境
虽然 MountainCar 环境是测试探索方法的快速实验平台,接下来,我们采用探索策略调整的 DQN 和 PPO 方法解决 Atari 游戏环境,以检验更复杂环境下的表现。
本节主要使用 Seaquest 游戏作为测试环境------该游戏中潜艇需要射击鱼类和敌方潜艇,并营救潜水员。该游戏可以视为中等难度的探索问题:为了持续游戏,需要控制氧气水平。当氧气不足时,潜艇必须上浮至水面补充氧气,否则回合将在 560 步后结束,最高得分仅为 20 分。但一旦智能体学会补充氧气,游戏几乎可以无限继续,获得 1-10 万的高分。令人惊讶的是,传统探索方法很难发现这一点------训练通常会在 560 步后陷入停滞,此时氧气耗尽导致潜艇死亡。
Atari的不足之处在于每个实验至少需要半天训练时间来验证效果,因此本节使用代码和超参数可能并非最优,但可作进行实验的起点。
和之前一样,提供两个程序文件:atari_dqn.py (实现带𝜖-贪婪和噪声网络探索的 DQN 方法)和 atari_ppo.py (实现带可选噪声网络和网络蒸馏方法的 PPO 方法),需使用命令行选项 -p 切换超参数配置。
接下来,我们查看代码运行的结果。
5.1 DQN + 𝜖-贪婪
与 Atari 上尝试的其他方法相比,𝜖-贪婪策略表现最佳,但在 MountainCar 实验中它得到了最差结果,现实中这种情况经常发生。经过训练后,该方法平均奖励达到 18 分,最高奖励为 25 分。从步数统计图表来看,仅有少数回合成功发现了获取氧气的方法,因此若增加训练时间,该方法有机会能够突破 560 步的限制。下图展示了平均奖励和步数的变化曲线:

5.2 DQN + 噪声网络
噪声网络与 DQN 结合的表现较差------训练 6 小时后仅获得 6 分奖励,下图展示了相关曲线:

5.3 PPO
PPO 实验则表现更差,所有组合(原始 PPO、噪声网络和网络蒸馏)均未显示奖励提升,平均奖励仅为 4 分。
小结
在本节中,我们探讨了为何𝜖-贪婪探索在某些情况下并非最优选择,并介绍了常用的更高效的探索策略。
系列链接
PyTorch强化学习实战(1)------强化学习(Reinforcement Learning,RL)详解
PyTorch强化学习实战(2)------强化学习环境库Gymnasium
PyTorch强化学习实战(3)------Gymnasium API扩展功能
PyTorch强化学习实战(4)------PyTorch基础
PyTorch强化学习实战(5)------PyTorch Ignite 事件驱动机制与实践
PyTorch强化学习实战(6)------交叉熵方法详解与实现
PyTorch强化学习实战(7)------表格学习与贝尔曼方程
PyTorch强化学习实战(8)------Q学习详解与实现
PyTorch强化学习实战(10)------强化学习高级组件
PyTorch强化学习实战(11)------N步DQN(N-step DQN)
PyTorch强化学习实战(12)------Double DQN(DDQN)
PyTorch强化学习实战(13)------噪声网络(NoisyNet-DQN)
PyTorch强化学习实战(14)------优先经验回放机制
PyTorch强化学习实战(15)------Dueling DQN
PyTorch强化学习实战(16)------Categorical DQN
PyTorch强化学习实战(17)------强化学习训练加速
PyTorch强化学习实战(18)------基于DQN处理股票交易问题
PyTorch强化学习实战(20)------优势演员-评论家(Advantage Actor-Critic, A2C)
PyTorch强化学习实战(21)------异步优势演员-评论家(Asynchronous Advantage Actor-Critic, A3C)
PyTorch强化学习实战(22)------将强化学习应用于TextWorld互动小说游戏
PyTorch强化学习实战(23)------强化学习在网页导航中的应用
PyTorch强化学习实战(24)------连续动作空间中的强化学习
PyTorch强化学习实战(25)------深度确定性策略梯度(DDPG)
PyTorch强化学习实战(26)------提升随机策略梯度稳定性