学习 Breakout:高级主题





5.00/5 (2投票s)
在本系列的最后一篇文章中,我们将研究一些更高级的主题:最小化我们的打砖块游戏代理的“抖动”,以及执行超参数的网格搜索。
在本系列的前几篇文章中,我们尝试了各种方法来学习 OpenAI Gym Atari 打砖块环境,既可以从像素学习,也可以从其 RAM 的内容学习。
现在我们将探索一些高级的打砖块学习主题。
最小化抖动
Atari 打砖块代理玩起来不是很流畅。 例如,通常情况下,球拍会毫无明显原因地来回“抖动”。
在本节中,我们将尝试通过惩罚代理的这种行为来最小化这种不必要的移动。
这是我使用的代码
import gym
import ray
from gym import Wrapper
from ray import tune
from ray.rllib.agents.impala import ImpalaTrainer
from ray.tune.registry import register_env
ray.shutdown()
ray.init(include_webui=False, ignore_reinit_error=True)
class PenaliseMovement(Wrapper):
def __init__(self, env):
super().__init__(env)
self._call_count = 0
def step(self, action):
observation, reward, done, info = super().step(action)
# manually reimpose effect of clipping on upper bound
if reward > 1.0:
reward = 1.0
threshold = 375_000 # want to kick in after about 2.5m iterations: 2_500_000 / 8 == 375_000
if self._call_count >= threshold and action not in (0, 3):
# phase it in slowly
multiplier = min((self._call_count - threshold) / 100_000, 1.0)
reward -= 0.0001 * multiplier
self._call_count += 1
return observation, reward, done, info
def env_creator(env_config):
env = gym.make('BreakoutNoFrameskip-v4')
env = PenaliseMovement(env)
return env
register_env("penalise_movement_breakout", env_creator)
ENV = "penalise_movement_breakout"
TARGET_REWARD = 200
TRAINER = ImpalaTrainer
tune.run(
TRAINER,
stop={"episode_reward_mean": TARGET_REWARD},
config={
"env": ENV,
"monitor": True,
"evaluation_num_episodes": 25,
# based on https://github.com/ray-project/ray/blob/master/rllib/tuned_examples/impala/atari-impala.yaml
"rollout_fragment_length": 50,
"train_batch_size": 500,
"num_workers": 7,
"num_envs_per_worker": 5,
"clip_rewards": False, # if True, any small negative reward will get clipped to -1
"lr_schedule": [
[0, 0.0005],
[20_000_000, 0.000000000001],
],
}
)
这使用了一个环境包装器,设置为对每个既不是 NO-OP (0) 也不是 FIRE (3) 的动作引入一个小的负奖励。 学习的挑战更大,因为移动的负奖励会立即交付,而击倒砖块的正奖励会在球从球拍上弹起后的某个时间应用。
我获得的最佳结果是等待学习者掌握标准环境后再引入惩罚(上面代码中的阈值),然后分 100,000 步而不是一次性引入惩罚。 这是一种课程学习的形式。 分别学习这两种方式比从一开始就混合它们要容易得多。
惩罚的大小很重要:太小不会有任何效果;太大代理可能只会学会静止不动。 我们想要的是足够的推动力来充当阻尼因素。
性能图看起来像这样(请注意,将这些奖励与标准环境直接比较已不再有意义)
当分数达到 80 左右时,会引入更复杂的奖励结构,并且学习速度会降低。
当我停止该过程时,该代理在 3.6 小时后达到了 116 的性能。 它是否真的更流畅还有待商榷。 如果您在此方法中取得任何成功,我很乐意在评论中听到。
参数网格搜索
在本系列中,我们一直在使用 RLlib 的 tune
函数作为运行训练的便捷方式,但没有实际使用它来调整参数。
这是一个进行简单网格搜索的示例。 它在 cartpole 环境中设置了一个调整会话,如下所示(字母表示代码行)
- 运行每个训练会话五次迭代 (A)
- 搜索学习率的三个候选值 (B)
- 搜索神经网络隐藏层架构的三个候选值 (C)
- 运行每次试验两次,以抵消对随机性初始状态的敏感度 (D)
import ray
from ray import tune
from ray.rllib.agents.dqn import DQNTrainer
ray.shutdown()
ray.init(
include_webui=False,
ignore_reinit_error=True,
object_store_memory=8 * 1024 * 1024 * 1024 # 8GB
)
ENV = 'CartPole-v0'
TRAINER = DQNTrainer
analysis = tune.run(
TRAINER,
stop={"training_iteration": 5}, # (A)
config={
"env": ENV,
"num_workers": 0,
"num_gpus": 0,
"monitor": False, # go faster by not monitoring
"lr": tune.grid_search([0.001, 0.0003, 0.0001]), # (B)
"hiddens": tune.grid_search([[256], [128], [200, 100]]) # (C)
},
num_samples=2, # (D)
)
print("Best config: ", analysis.get_best_config(metric="episode_reward_mean"))
df = analysis.dataframe()
print(df)
当我运行此命令时,结果如下
Best config: {'env': 'CartPole-v0', 'num_workers': 0, 'num_gpus': 0, 'monitor': False, 'lr': 0.0003, 'hiddens': [256]}
这告诉我们,0.0003 的学习率和 [256] 的隐藏层配置是执行五次迭代的最佳选择,并且它们是训练模型完成的有希望的设置。
要查看 get_best_config 的所有可用指标,请参阅数据框的列
print(df.columns)
要找到要搜索的好的候选参数,以下内容可能会有所帮助。 对于 RLlib,有两组互补的“已知良好”设置,用于学习不同的环境
- RLlib 本身中的tuned_examples 目录。 它侧重于各种环境的合理工作参数。
- Ray 项目中一个单独的存储库,称为rl-experiments。 在那里,您将找到参考结果,以便与科学论文中发表的性能进行比较。
对于每种算法,这两种来源都建议了一组 Atari 游戏参数。 如果您想要一个挑战,请调查是否可以找到适用于特定环境的更好参数。
网格搜索是一项昂贵的练习,尤其是在比 cartpole 更具挑战性的环境中。 搜索空间很快就会加起来,需要大量的试验。 这有时被称为维数灾难。
我希望您喜欢对强化学习的介绍,并且它鼓励您自己进行一些实验。 我期待在评论中阅读任何问题或建议。