政策梯度如何让您登上月球

实践 RL 课程 – 第 7 部分 – 策略梯度是一系列强大的强化学习算法,可以解决复杂的控制任务。在今天的课程中,我们将从头开始实施原版策略梯度并登陆月球🌗。如果您不熟悉强化学习,请查看课程简介以了解基础知识……

政策梯度如何让您登上月球

强化学习实践课程——第 7 部分

策略梯度是一系列强大的强化学习算法,可以解决复杂的控制任务。在今天的课程中,我们将从头开始实施原版策略梯度并登陆月球🌗。

如果您不熟悉强化学习,请查看课程介绍以了解基础知识和术语。[0]

同样,您可以在此存储库中找到今天课程的所有代码👇🏽[0]

Contents

  1. 如何通过政策梯度登月? 🚀🌙
  2. The LunarLanderenvironment
  3. Baseline agent
  4. Welcome policy gradients 🤗
  5. Policy gradients agent
  6. Key take-aways
  7. Homework 📚
  8. What’s next?

1、如何通过政策梯度登月? 🚀🌙

为了登陆月球,我们使用宇宙飞船,这是一个巨大的模块化工程,结合了令人难以置信的力量和温和的精度。

在我们旅程的开始,我们使用强大的火箭(如下图所示)帮助宇宙飞船摆脱地球的引力。

一旦火箭被消耗,宇宙飞船就会将它们安全地释放到地球的海洋中,并开始其 385,000 公里(239,000 英里)的月球之旅。

3 天后,我们的宇宙飞船接近月球并开始绕月球运行。这场高超的戏剧的最后一幕是着陆。

宇宙飞船释放了一个装置,即月球着陆器,月球的引力场将其拉向其表面。我们整个任务的成功取决于这个装置的轻柔着陆。

在今天的讲座中,我们将使用强化学习来训练一个代理(又名控制器)以将该设备降落在月球上。

此外,我们将使用 OpenAI 的 LunarLander 环境作为着陆问题的方便(和简化)抽象。

最后,这就是您今天将实施的代理如何登陆月球。

2. The LunarLander environment

在进入建模部分之前,让我们快速熟悉一下 LunarLander 环境。

State

代理得到一个包含 8 个分量的状态向量:

  • 2D 世界中的当前位置:x 和 y
  • 沿这两个轴的速度:vx 和 vy
  • 相对于垂直方向的角位置 θ:
  • 角速度 ω,捕捉设备围绕其自身轴的旋转。
  • 两个二进制信号(s1 和 s2),用于指示设备的 2 条腿中的哪条腿(如果有)与地板接触。

Actions

有 4 种可能的操作:

  • 0 Do nothing.
  • 1 点燃左侧发动机。
  • 2 点燃主机(中央)。
  • 3 Fire right engine.

Rewards

在这种环境下,奖励功能旨在为代理提供足够的信号。这使训练更容易。

这是着陆器在每个时间步获得的奖励:

  • 如果着陆器离开着陆台,它会得到负奖励。
  • 如果发生崩溃,它会获得额外的 -100 分。
  • 如果涉及到休息,它会获得额外的 +100 分。
  • 每条与地面接触的腿+10分。
  • 启动主引擎是每帧 -0.3 点。
  • 启动侧引擎是每帧 -0.03 点。
  • 最后,解决的是200分。

奖励工程的优缺点

奖励工程是一种建模技巧,通过向代理提供频繁的反馈来帮助 RL 代理学习。奖励不频繁(又名稀疏)的环境更难解决。

但是,它有两个缺点:

👉🏽 奖励功能很难设计,就像你看到 LunarLander-v2 奖励后可能想象的那样。在找到鼓励智能体学习正确行为的奖励函数之前,通常需要进行大量的反复试验。

👉🏽 奖励塑造是特定于环境的,这限制了受过训练的代理对稍微不同的环境的泛化。此外,这限制了经过模拟训练的 RL 代理在现实世界环境中的可迁移性。

3. Baseline agent

👉🏽 notebooks/01_random_agent_baseline.ipynb[0]

像往常一样,我们使用随机代理获得问题的快速基线。

# create environment
import gym
env = gym.make('LunarLander-v2')

# create random agent
class RandomAgent:

    def __init__(self, env):
        self.env = env

    def act(self, state) -> int:
        """
        No input arguments to this function.
        The agent does not consider the state of the environment when deciding
        what to do next.
        """
        return self.env.action_space.sample()

agent = RandomAgent(env)

接下来,我们使用 100 集来评估这个代理,以获得总奖励和着陆器的成功率:

from tqdm import tqdm

n_episodes = 100
reward_per_episode = []
success_per_episode = []

for i in tqdm(range(0, n_episodes)):

    state = env.reset()
    total_reward = 0
    done = False
    reward = None
    
    while not done:
        action = agent.act(state)
        next_state, reward, done, info = env.step(action)
        total_reward += reward
        state = next_state
        
    reward_per_episode.append(total_reward)
    success_per_episode.append(1 if reward > 0 else 0)

然后我们绘制结果

import numpy as np
reward_avg = np.array(rewards).mean()
reward_std = np.array(rewards).std()
print(f'Reward average {reward_avg:.2f}, std {reward_std:.2f}')

success_rate = np.array(success_per_episode).mean()
print(f'Succes rate = {success_rate:.2%}')

终于意识到我们的代理是一场彻头彻尾的灾难

Reward average -198.35, std 103.93
Succes rate = 0.00%

*由于代理的固有随机性,当您在笔记本电脑上运行笔记本时,这些数字不会完全相同。无论如何,成功率几乎肯定是0%。

此外,您可以观看此随机代理的视频,以说服自己我们远未解决问题。

# Workaround for pygame error: "error: No available video device"
# See https://stackoverflow.com/questions/15933493/pygame-error-no-available-video-device?rq=1
# This is probably needed only for Linux
import os
os.environ["SDL_VIDEODRIVER"] = "dummy"
from src.viz import show_video

show_video(agent, env, sleep_sec=0.01, seed=12345)

事实证明,登陆月球并不是小菜一碟(真是惊喜)

让我们看看政策梯度如何帮助我们登上月球。

4. Welcome policy gradients 🤗

任何强化学习问题的目标都是找到最大化累积奖励的最优策略。

到目前为止,在课程中,我们使用了基于价值的方法,例如 Q-learning 或 SARSA。这些方法以间接的方式找到最优策略,首先找到最优 Q 函数,然后使用这个 Q 函数按照 epsilon-greedy 策略选择次优动作。通过这种方式,他们提出了最优策略。[0][1]

另一方面,基于策略的方法直接找到最优策略,而不必估计最优 Q 函数。

基于策略的方法中的两个基本步骤如下:

Step 1. Policy parameterization

我们使用神经网络模型来参数化最优策略。该策略被建模为随机策略,而不是确定性策略。我们称其为策略网络,我们将其表示为

随机政策和探索与开发的权衡

随机策略将给定的观察(输入)映射到概率列表,每个概率对应一个可能的动作。

为了选择下一步要采取的行动,代理会根据其概率对行动进行抽样。因此,随机策略自然地处理了探索与利用的权衡,没有像 epsilon 这样的超参数技巧(如 Q 学习中使用的 epsilon-greedy 技巧)。

在 LunarLander-v2 环境中,我们的随机策略将有 8 个输入和 4 个输出。 4 个输出中的每一个代表采取 4 个动作中的每一个的概率。

策略网络的架构是您需要试验的超参数。我们今天将使用的那个有一个包含 64 个单元的隐藏层。

解决我们的 RL 问题相当于找到这个神经网络的最优参数。

但是我们究竟如何找到这个网络的最佳参数呢?

步骤 2. 找到最优参数的策略梯度

评估策略的好坏很容易:

  • 首先,我们对 N 个随机剧集遵循此策略并收集每集的总奖励
  • 其次,我们对这 N 个总奖励进行平均,并获得一个整体绩效衡量标准。这个表达式是总奖励的经验平均值。

此外,总奖励和 J 取决于策略参数 θ,我们的目标是找到使 J 最大化的参数 θ。

同样,有不同的算法可以解决这个最大化问题(例如遗传优化),但在深度学习中,基于梯度的方法才是王道👑。

基于梯度的方法如何工作?

我们从策略参数 θ₀ 的初始猜测开始,我们希望找到一组更好的参数 θ₁

θ₁ 和 θ₀ 之间的差异是我们在参数空间中行进的方向。

问题是,增加 J 的最佳方向是什么?

答案是……梯度。

What is it?

函数 J 相对于参数向量 θ 的梯度是我们需要从 θ 采取的方向,以产生 J 的最大增加。

但是,我们应该在梯度方向移动的距离呢?

这是另一个(非常流行的)超参数:学习率 α

配备梯度和适当调整的学习率,我们执行梯度上升的一步以得出更好的参数向量 θ₁

如果我们迭代地重复这个公式,我们将获得更好的策略参数,并有望解决环境问题。

这就是梯度上升如何帮助我们找到最优策略网络参数。

剩下的问题是,我们如何计算这些梯度?

估计策略性能的梯度

为了实现这个算法,我们需要一个可以数值计算的策略性能梯度表达式。

可以通过一点微积分和统计在数学上证明,可以使用使用当前策略收集的轨迹样本来近似策略参数,如下所示:

其中轨迹 𝛕 是观察到的状态序列和直到剧集结束所采取的行动

重要的是要强调一个事实,这些轨迹必须使用当前的政策参数来收集。

On-policy 与 off-policy 算法

策略梯度方法是策略上的算法,意味着我们只能使用在当前策略下收集的经验来改进当前策略。

另一方面,使用诸如 Q-learning 之类的离策略算法,我们可以保留轨迹的回放记忆,收集旧版本的策略,并使用它们来改进当前策略。

此外,上面的公式对应于最简单的策略梯度方法。然而,这个公式有一些变化,它取代了每条轨迹的总回报 R(τᵢ)

与其他成功指标。

特别是,我们将在本课程中看到 2 个版本,一个今天,另一个在下一课:

  • 给定动作后的累积奖励,即奖励执行
  • 优势函数,即当前策略的 Q 函数和 V 函数之间的差异。

如果我们已经有一个,为什么我们需要替代公式来估计梯度?

策略梯度公式的所有 3 个变体都具有相同的期望值,因此如果您使用非常大的轨迹样本,这 3 个数字将非常相似。然而,对于计算性能,我们只能提供一小组轨迹,因此这些估计的可变性很重要。

In particular

  • 奖励执行版本的方差低于原始公式
  • 并且优势函数版本的方差甚至小于reward-to-go版本。

较低的方差导致更快和更稳定的策略学习。

今天,我们将实现reward-to-go 版本,因为它几乎和原始的带有累积奖励的普通策略梯度公式一样复杂。在下一节课中,我们将看到如何估计优势函数并获得更好的结果。

想了解详情吗?

由于本课程的目的是为您提供对该主题的实践方法,因此我将数学技术保持在最低限度。

如果您想了解从上面推导策略梯度公式的所有步骤,我建议您阅读 Wouter van Heeswijk 的这篇出色的博客文章📝。[0]

5. Policy gradients agent

👉🏽 notebooks/03_vanilla_policy_gradient_with_rewards_to_go.ipynb[0]

让我们实现最简单的策略梯度代理,其中策略梯度公式中的权重是情节奖励。

超参数只有3个:

  • learning_rate 是策略梯度更新的大小。
  • hidden_​​layers 定义策略网络架构。
  • gradient_weights 是每个时间步的奖励:即从当前时间步到剧集结束的奖励总和。
ENV_NAME = 'LunarLander-v2'

from src.vpg_agent import VPGAgent
agent = VPGAgent(
    env_name=ENV_NAME,
    learning_rate=3e-3,
    hidden_layers=[64],
    gradient_weights='rewards',
)

我们使用 Tensorboard 设置基本日志记录并开始训练我们的 VPGAgent:

# unique agent_id to identify this run in tensorboard
from src.utils import get_agent_id
agent_id = get_agent_id(ENV_NAME)

# tensorboard logger to see training curves
from src.utils import get_logger, get_model_path
logger = get_logger(env_name=ENV_NAME, agent_id=agent_id)

# path to save policy network weights and hyperparameters
model_path = get_model_path(env_name=ENV_NAME, agent_id=agent_id)

# let's train!
agent.train(
    n_policy_updates=5000,
    batch_size=256,
    logger=logger,
    model_path=model_path,
)

训练逻辑封装在您可以在 src/vgp_agent.py 中找到的 agent.train() 函数中。这个函数本质上是一个带有 n_policy_updates 迭代的循环,在每次迭代中我们:[0]

  1. 收集 batch_sizetrajectories 的样本
  2. 使用随机梯度上升更新策略参数。
  3. 将指标记录到 Tensorboard
  4. 可能评估当前代理,如果它是我们迄今为止找到的最佳代理,则将其保存到磁盘。
for i in range(n_policy_updates):

    # use current policy to collect trajectories
    states, actions, weights, rewards = self._collect_trajectories(n_samples=batch_size)

    # one step of gradient ascent to update policy parameters
    loss = self._update_parameters(states, actions, weights)

    # log epoch metrics
    print('epoch: %3d \t loss: %.3f \t reward: %.3f' % (i, loss, np.mean(rewards)))
    if logger is not None:
        # we use total_steps instead of epoch to render all plots in Tensorboard comparable
        # Agents wit different batch_size (aka steps_per_epoch) are fairly compared this way.
        total_steps += batch_size
        logger.add_scalar('train/loss', loss, total_steps)
        logger.add_scalar('train/episode_reward', np.mean(rewards), total_steps)

    # evaluate the agent on a fixed set of 100 episodes
    if (i + 1) % freq_eval_in_epochs == 0:
        rewards, success = self.evaluate(n_episodes=100)

        avg_reward = np.mean(rewards)
        avg_success_rate = np.mean(success)
        if save_model and (avg_reward > best_avg_reward):
            self.save_to_disk(model_path)
            print(f'Best model! Average reward = {avg_reward:.2f}, Success rate = {avg_success_rate:.2%}')

            best_avg_reward = avg_reward

什么是正确的 batch_size 使用?

每个策略更新的轨迹数(又名 batch_size)是您需要调整的超参数,以确保您的训练在合理的时间内收敛到最佳解决方案。

👉🏽 如果 batch_size 太低,你会得到非常嘈杂的梯度估计,并且训练不会收敛到最优策略。

👉🏽 相反,如果batch_size 太大,训练循环会不必要地变慢,你会坐等🥱

Plotting training results

在模型训练时,我们转到终端并启动 Tensorboard 服务器,如下所示:

$ tensorboard --logdir tensorboard_logs

如果您访问打印在控制台上的 URL,您将看到与此类似的图表,其中横轴是代理到目前为止收集的轨迹总数,纵轴是每集的总奖励。

我们清楚地看到代理正在学习,并且在 100 万步后获得了大约 250 的平均总奖励。

由于总奖励不是一个非常直观的衡量标准,我们在 100 个新集的训练结束时直接评估代理,并检查着陆成功率:

# evalute on 100 episodes
rewards, success = agent.evaluate(n_episodes=100)

# print results
import numpy as np
reward_avg = np.array(rewards).mean()
reward_std = np.array(rewards).std()
print(f'Reward average {reward_avg:.2f}, std {reward_std:.2f}')

success_rate = np.array(success).mean()
print(f'Succes rate = {success_rate:.2%}')
Reward average 245.86, std 30.18
Succes rate = 99.00%

哇! 99% 的准确率相当不错,不是吗?

让我们休息一下,回顾一下,然后展示作业。我们可以?

6. Key takeaways

这些是我今天想让你睡觉的三件事:

  • 首先,策略梯度(PG)算法直接参数化最优策略并使用梯度上升算法调整其参数。
  • 其次,与深度 Q 学习相比,PG 算法更稳定(即对超参数不太敏感),这很好。
  • 最后,PG 算法的数据效率较低,因为它们不能重用旧轨迹来更新当前策略参数(即它们是策略上的方法)。这是我们需要改进的地方

7. Homework

👉🏽 notebooks/04_homework.ipynb[0]

  • 你能找到一个更小的网络来解决这个环境吗?我使用了一个有 64 个单位的隐藏层,但我觉得这有点过头了。
  • 您可以通过正确调整 batch_size 来加快收敛速度​​吗?

8. What’s next

在下一课中,我们将介绍一个新的技巧来提高策略梯度方法的数据效率。

Stay tuned.

Love,

Live.

Learn.

Wanna support me?

你喜欢阅读和学习机器学习、人工智能和数据科学吗?

无限制地访问我在 Medium 上发布的所有内容并支持我的写作。

👉🏽 立即使用我的推荐链接成为会员。[0]

👉🏽 订阅数据机器通讯。[0]

👉🏽 在 Medium 上关注我。[0]

有一个美好的一天🤗

Pau

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
心中带点小风骚的头像心中带点小风骚普通用户
上一篇 2022年5月13日
下一篇 2022年5月13日