强化学习 Reinforcement Learning(二)—— 是时候用 PARL 框架玩会儿 DOOM 了!!!(上)
写在前面
突然发现一直好像还没有人用百度的 PARL 玩过毁灭战士,所以这两天在学习之余娱乐一下,给大家整个活儿:)
游戏环境 VIZDoom
访问 VIZDoom 官网链接请 点击这里
VIZDomm 简介
ViZDoom 是强化学习常用的游戏平台之一。
但 ViZDoom 这个环境出不出名不重要,因为能玩 DOOM 的模型真的超级 COOOOOOOOL !!!(超大声)
VIZDoom 安装
友情提示:VIZDoom 暂时不支持 ARMv8 (例如 Jetson NX)的计算设备
这里提供 Ubuntu 与 MacOS 两种安装方式,本人电脑的操作系统是 Ubuntu 18.04 LTS ,pip 直接就装上了。
更详细的安装步骤建议上官网看一下。
pip 安装
Installation of Ubuntu
sudo apt install cmake libboost-all-dev libsdl2-dev \
libfreetype6-dev libgl1-mesa-dev libglu1-mesa-dev \
libpng-dev libjpeg-dev libbz2-dev \
libfluidsynth-dev libgme-dev libopenal-dev \
zlib1g-dev timidity tar nasm wget
#("pip3" for python3)
pip install vizdoom
Installation of MacOS
brew install cmake boost sdl2 wget
#("pip3" for python3)
pip install vizdoom
安装测试
安装成功后,测试文件如下:
from vizdoom import *
import random
import time
game = DoomGame()
game.load_config("vizdoom/scenarios/basic.cfg")
game.init()
shoot = [0, 0, 1]
left = [1, 0, 0]
right = [0, 1, 0]
actions = [shoot, left, right]
episodes = 10
for i in range(episodes):
game.new_episode()
while not game.is_episode_finished():
state = game.get_state()
img = state.screen_buffer
misc = state.game_variables
reward = game.make_action(random.choice(actions))
print "\treward:", reward
time.sleep(0.02)
print "Result:", game.get_total_reward()
time.sleep(2)
不过我遇到了 .cfg 配置文件读取不到的问题,如果遇到了可以尝试将其改成绝对路径(非常粗暴)
#game.load_config("vizdoom/scenarios/basic.cfg")
doom_path="/usr/local/lib/python3.6/dist/packages/vizdoom/scenarios/basic.cfg"
game.load_config(doom_path)
VIZDoom 配置
VIZDoom 有丰富的配置选项,需要提前设定好
DoomGame类
该类的实体就是交互的环境,可通过如下代码制定wad文件以及相应的地图。
game = DoomGame()
game.set_doom_scenario_path("../../scenarios/basic.wad")
game.set_doom_map("map01")
wad包含了多个构建的虚拟环境,因此在指定wad文件后还要通过game.set_doom_map(“map01”)来指定具体需要哪张地图,若不指定,则默认使用第一张地图。
还可以设置显示参数:
是否显示弹夹
game.set_render_hud(False)
是否显示瞄准线
game.set_render_crosshair(False)
是否显示武器
game.set_render_weapon(True)
是否显示贴花纸
game.set_render_decals(False)
是否显示微粒(粒子特效)
game.set_render_particles(False)
此外,还可以设置整个环境的迭代回合数,以及奖励。
game.set_episode_timeout(200)
game.set_episode_start_time(10)
game.set_window_visible(True)
game.set_living_reward(-1)
ScreenFormat 与 ScreenResolution 类
设置图像分辨率
game.set_screen_resolution(ScreenResolution.RES_640X480)
通道数,即彩色GRB24,或灰度图GRAY
game.set_screen_format(ScreenFormat.RGB24) # GRAY8
Button类
特别注意:这一步不能省略,否则智能体将没有可以动的 action
game.add_available_button(Button.MOVE_LEFT) # 左移
game.add_available_button(Button.MOVE_RIGHT) # 右移
game.add_available_button(Button.ATTACK) # 攻击
GameVariable类
此外,还可以通过game_variable获得玩家的health, ammo, weapon availability以及location.
game.add_available_game_variable(GameVariable.AMMO2)
# 获取X坐标
game.add_available_game_variable(GameVariable.POSITION_X)
# 获取Y坐标
game.add_available_game_variable(GameVariable.POSITION_Y)
初始化
在完成上述环境配置后,初始化环境。初始化仅需一次即可,之后每次重新开始新的一个回合,通过 game.new_episode() 即可。
game.init()
state类
当需要获取环境的状态是,可通过如下操作:
# Gets the state
state = game.get_state()
包含了
n = state.number
misc = state.game_variables # 音乐
screen_buf = state.screen_buffer # 主视角图
depth_buf = state.depth_buffer # 主视角深度图
labels_buf = state.labels_buffer # 主视角的标签图(目标分割图)
automap_buf = state.automap_buffer # 俯视地图
labels = state.labels
终止环境
game.close()
自定义环境配置文件
上面介绍了在程序中通过 http://game.xxx 来配置环境,从而定义在环境中显示或获取什么信息。其实也可以通过修改和加载cfg配置文件,实现一样的功能。
这里同样需要注意路径问题。
game.load_config("vizdoom/scenarios/basic.cfg")
配置文件的修改必须满足以下规则:
- 一行一项:one entry per line (except for list parameters),
- 大小写无关:case insensitive
- 开头为注释:lines starting with # are ignored,
- 忽视下划线:underscores in keys are ignored (episode_timeout is the same as episodetimeout),
- 字符串无需引号:string values should not be surrounded with apostrophes or quotation marks.
示例:
#doom_game_path = ../../scenarios/doom2.wad
doom_scenario_path = ../../scenarios/basic.wad
doom_map = map01
# Rewards
living_reward = -1
# Rendering options, RES_160X120
screen_resolution = RES_320X240
screen_format = CRCGCB
render_hud = True
render_crosshair = false
render_weapon = true
render_decals = false
render_particles = false
window_visible = true
# make episodes start after 20 tics (after unholstering the gun)
episode_start_time = 14
# make episodes finish after 300 actions (tics)
episode_timeout = 300
# Available buttons
available_buttons =
{
MOVE_LEFT
MOVE_RIGHT
}
available_buttons += { ATTACK }
# Game variables that will be in the state
available_game_variables = { AMMO2}
mode = PLAYER
doom_skill = 5
#auto_new_episode = false
#new_episode_on_timeout = false
#new_episode_on_player_death = false
#new_episode_on_map_end = false
百度强化学习框架 PARL
访问 PARL 的 GitHub 地址请 点击这里
PARL 介绍
PARL是一个主打高性能、稳定复现、轻量级的强化学习框架,集成了多种主流的强化学习算法。更详细的介绍可以访问 PARL 的地址进行查看。
DQN 介绍
论文链接:https://www.nature.com/articles/nature14236
DQN是一种 value based 的强化学习算法,是第一个成功地将深度学习和强化学习结合起来的模型。
首先让我们来看看DQN的前身 Q-Learning 。
Q-Learning 的算法如下:
对于 Q-Learning,首先就是要确定如何存储 Q 值,最简单的想法就是用矩阵,一个 s 一个 a 对应一个 Q 值,所以可以把 Q 值想象为一个很大的表格,横列代表 s,纵列代表 a,里面的数字代表 Q 值,如下表示:
这样大家就应该清楚 Q 值是怎样的了。
但是如果使用表格来表示 Q(s,a) ,在现实的很多问题上是几乎不可行的,因为状态实在是太多。使用表格的方式根本存不下。
举 Atari 的打砖块为例。
如果让计算机玩 Atari 游戏的要求是输入原始图像数据,也就是 210x160 像素的图片,然后输出按键动作。和对人类的要求一样,纯视觉输入,然后让计算机自己玩游戏。
那么这种情况下,到底有多少种状态呢?从理论上看,如果每一个像素都有 256 种选择,那么就有:
25 6 210 x 160 256^{210x160} 256210x160
全宇宙的原子数量加起来都没那么多。所以,我们是不可能通过表格来存储状态的。我们有必要对状态的维度进行压缩,解决办法就是价值函数近似Value Function Approximation,然后用深度神经网络来替换掉之前的表格。具体算法原理这里不详细展开了。
本人实现的 DQN 基本属于第一个版本的DQN,也就是 NIPS 2013 提出的 DQN。
具体的算法主要涉及到经验回放( Experience Replay) ,也就是经验池的技巧,即如何存储样本及采样问题。
由于玩游戏采集的样本是一个时间序列,样本之间具有连续性,如果每次得到样本就更新 Q 值,受样本分布影响,效果会不好。因此,一个很直接的想法就是把样本先存起来,然后随机采样。这就是 Experience Replay 的意思。按照脑科学的观点,人的大脑也具有这样的机制,就是在回忆中学习。
实际上就是让模型反复试验,然后存储数据。接下来数据存到一定程度,就每次随机采用数据,进行梯度下降。
写在最后
本文主要介绍了一下用到的仿真环境 VIZDoom ,所用到的强化学习框架 PARL ,以及 DQN 的一些原理与算法。
在下一篇文章里,本人会放上 DQN 玩 DOOM 的具体代码。