65.9K
CodeProject 正在变化。 阅读更多。
Home

从 RAM 中学习 Breakout - 第 1 部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2020年7月2日

CPOL

3分钟阅读

viewsIcon

8971

在本文中,我们将从游戏 RAM 的内容而不是像素学习。

上一篇文章中,我们使用了RLlib 的 IMPALA 代理,以在一个体面的时间内从像素中学习Atari 打砖块环境

在这里,我们将更进一步,尝试从游戏 RAM 的内容而不是像素中学习。

作为一名软件工程师,我期望 RAM 环境更容易学习。 毕竟,内存中的一个位置似乎很可能保存球拍的 x 坐标,另外两个位置将保存球的位置。 如果我试图编写一些代码来玩这个游戏,并且不使用机器学习,这可能是我想开始的地方。 如果被迫使用图形,我将只是处理它们以提取此信息,所以跳过这一步肯定更简单。

事实证明我错了! 从图像学习比从 RAM 学习更容易。 现代卷积神经网络架构擅长从图像中提取有用的特征。 相比之下,使用这么少内存的程序员习惯于提出各种“巧妙的技巧”来尽可能多地将信息打包到空间中。 一个字节可能代表一个数字,或者两个数字,每个数字四位,或者八个标志...

从 RAM 学习

这是我使用的代码

import ray
from ray import tune
from ray.rllib.agents.dqn import DQNTrainer

ray.shutdown()
ray.init(include_webui=False, ignore_reinit_error=True)

ENV = "Breakout-ramDeterministic-v4"
TARGET_REWARD = 200
TRAINER = DQNTrainer

tune.run(
    TRAINER,
    stop={"episode_reward_mean": TARGET_REWARD},
    config={
      "env": ENV,
      "monitor": True,
      "evaluation_num_episodes": 25,
      "double_q": True,
      "hiddens": [128],
      "num_workers": 0,
      "num_gpus": 1,
      "target_network_update_freq": 12_000,
      "lr": 5E-6,
      "adam_epsilon": 1E-5,
      "learning_starts": 150_000,
      "buffer_size": 1_500_000,
    }
)

这是我停止该过程之前的进度

这并不是一个巨大的成功。 我让训练运行了 54 个小时,得到了 40 分。 因此它学到了一些东西,并且该图表表明它正在继续改进,但进展非常缓慢。 在下一篇文章中,我们将看到如何做得更好。

从 RAM 的一个子集学习

即使 Atari 只有 128 字节的内存,许多存储的值也只是噪声,这很有诱惑力。 例如,其中某处将是玩家当前的得分,而将其用作输入特征将无助于学习。

因此,我尝试识别一个包含有用信息的位子集。 通过记录观察结果并查看哪些观察结果似乎有意义地变化(即,在前一百个时间步内有很多不同的值),我选择了以下列索引作为“有趣”:70、71、72、74、75、90、94、95、99、101、103、105 和 119。

这是我用于训练仅使用这些值的模型的代码。 我切换到使用 PPO 算法,因为它似乎比 DQN 表现更好。

有趣的部分是TruncateObservation类,它将观察空间从 128 字节简化为 13 个字节。

import pyvirtualdisplay
_display = pyvirtualdisplay.Display(visible=False, size=(1400, 900))
_ = _display.start()

import ray
from ray import tune
from ray.rllib.agents.ppo import PPOTrainer

ray.shutdown()
ray.init(include_webui=False, ignore_reinit_error=True)

import numpy as np
import gym
from gym.wrappers import TransformObservation
from gym.spaces import Box
from ray.tune.registry import register_env
from gym import ObservationWrapper


class TruncateObservation(ObservationWrapper):
  interesting_columns = [70, 71, 72, 74, 75, 90, 94, 95, 99, 101, 103, 105, 119]

  def __init__(self, env):
    super().__init__(env)
    self.observation_space = Box(low=0, high=255, shape=(len(self.interesting_columns),), dtype=np.uint8)

  def observation(self, obs):
    # print(obs.tolist())  # print full observation to find interesting columns
    return obs[self.interesting_columns]  # filter


def env_creator(env_config):
    env = gym.make('Breakout-ramDeterministic-v4')
    env = TruncateObservation(env)
    return env


register_env("simpler_breakout", env_creator)
ENV = "simpler_breakout"
TARGET_REWARD = 200
TRAINER = PPOTrainer


tune.run(
    TRAINER,
    stop={"episode_reward_mean": TARGET_REWARD},
    config={
      "env": ENV,
      "num_workers": 1,
      "num_gpus": 0,
      "monitor": True,
      "evaluation_num_episodes": 25
    }
)

学习表现是这样的

在 27 小时的训练后,它达到了 42 分,在这一点上我停止了该过程。 这看起来比尝试在所有字节上进行训练更有希望。 如果您设法使用稍有不同的内存位置子集做得更好,请在评论中告诉我。 例如,Jakub Sygnowski 和 Henryk Michalewski 撰写的“从 Atari 2600 的内存中学习”将内存位置 95 到 105 称为特别有影响。

下一篇文章中,我们将看到如何通过以略有不同的方式处理 RAM 来改进。

© . All rights reserved.