处理时间限制#

在使用强化学习代码和 Gymnasium 环境时,常见的问题是如何处理时间限制。在 OpenAI Gym 的早期版本(< 0.26)中,从 env.step 收到的 done 信号表示一个回合是否结束。然而,这个信号无法区分回合是由于“终止”(termination)还是“截断”(truncation)而结束。

终止#

终止指的是回合在到达定义为环境一部分的终端状态后结束。例如任务成功、任务失败、机器人摔倒等。值得注意的是,这也包括由于环境的固有时间限制而在有限视野环境中回合结束的情况。请注意,为了保持马尔可夫属性,在有限视野环境中,代理的观察中必须包含剩余时间的表示。(参考文献)

截断#

截断是指回合在外部定义的条件(即超出马尔可夫决策过程的范围)后结束。这可以是时间限制、机器人越界等。

无限视野环境是显而易见的例子,这里需要这种机制。我们不能无限期地等待回合完成,因此我们设定实际的时间限制,在此之后强制停止回合。在这种情况下,最后状态不是终端状态,因为它根据定义 RL 问题的马尔可夫决策过程有一个非零的转移到另一个状态的概率。这也不同于有限视野环境中的时间限制,因为在这种情况下,代理并不知道这个时间限制。

备注

“Terminal state” 是强化学习中的一个关键概念,用于描述一个特定状态,在这个状态下,智能体(agent)无法再采取任何行动。换句话说,终端状态标志着一个回合(episode)的结束。以下是对终端状态的详细解读:

  1. 定义和特征

    • 终端状态 是指智能体达到某个特定条件后,不再需要继续进行决策的状态。这种状态通常意味着当前回合的结束。

    • 在有限视野环境中,终端状态可能是任务成功或失败的标志,例如机器人到达目标位置或掉落到地面。

    • 在无限视野环境中,终端状态可能不存在,因为环境没有明确的结束点,但可以通过设置时间限制来强制结束回合。

  2. 与截断的区别

    • 终止 是由于智能体达到了环境的固有条件而自然结束的回合,而 截断 则是由于外部条件(如时间限制)强制结束的回合。

    • 在终端状态,智能体的观察包含了关于剩余时间的信息,以保持马尔可夫属性。而在截断情况下,智能体通常不知道时间限制。

  3. 重要性

    • 终端状态对于确保强化学习算法的正确性和效率至关重要。它们帮助定义了智能体的目标和奖励结构。

    • 在设计和评估强化学习环境时,正确处理终端状态和截断是非常重要的,因为它们直接影响智能体的学习过程和性能。

总结来说,终端状态是强化学习中的一个重要概念,它标志着回合的自然结束,并帮助定义智能体的目标和奖励结构。正确理解和应用终端状态对于开发有效的强化学习算法至关重要。

在代码学习中的重要性#

Bootstrapping(使用一个或多个变量的估计值来更新同一变量的估计值)是强化学习的关键方面。价值函数会告诉你,如果你遵循某个策略,从一个特定状态获得的折扣奖励是多少。当一个回合在任何给定时刻停止时,通过查看最终状态的价值,智能体能够估计如果该回合继续进行,可以获得多少折扣奖励。这是处理截断的一个示例。

更正式地说,强化学习中常见的 Bootstrapping 示例是更新 Q 值函数的估计,

\[ \begin{align}Q_{target}(o_t, a_t) = r_t + \gamma . \max_a(Q(o_{t+1}, a_{t+1}))\end{align} \]

在经典 RL 中,新的“Q”估计是 Q_target 和之前的“Q”估计的加权平均值,而在 Deep Q-Learning 中,最小化 Q_target 和之前“Q”估计之间的误差。

然而,在终止状态上不进行 bootstrapping,

\[ \begin{align}Q_{target}(o_t, a_t) = r_t\end{align} \]

这就是终止和截断之间的区别变得重要的地方。当一个回合由于终止而结束时,我们不进行 bootstrapping,而当它由于截断而结束时,我们进行 bootstrapping。

在使用 gymnasium 环境时,“done”信号(默认为< v0.26)经常用于确定是否进行 bootstrapping。然而,这是错误的,因为它无法区分终止和截断。

下面是价值函数的简单示例。这是说明性的例子,不是任何特定算法的一部分。

# 错误示例
vf_target = rew + gamma * (1 - done) * vf_next_state

在由于截断导致回合结束的情况下,这是不正确的,需要进行 bootstrapping,但不会这样做。

解决方案#

从 v0.26 开始,Gymnasium 的 env.step API 明确返回了终止和截断的信息。在早期版本中,截断信息是通过 infoTimeLimit.truncated 提供的。现在处理终止和截断的正确方式是:

# terminated = done and 'TimeLimit.truncated' not in info
# 这在早期版本中是必需的。
vf_target = rew + gamma * (1 - terminated) * vf_next_state