FPS(First Person Shooter),第一人称射击游戏,这里特指的是Doom游戏。Doom是比较早的FPS游戏,经过修改的VizDoom非常适合做增强学习(Reinforcement Learning)方面的学习和实验。

下面的视频中是在一个“小房子”里AI Bot(左)与一个随意行动的Random Bot(右)的对抗过程,在100场Test比赛中战胜Random Bot 95次。可以看到,AI Bot可以做到没有看到敌人时四处寻找,看到敌人时能够快速击毙敌人。而完成这些,模型不需要其他任何输入,仅需要当前游戏的画面,与真实玩家的操作过程没有任何区别。在几次测试中,因为操作不好,我基本没有赢过AI Bot,唯一的可能就是开局赶紧跑到他的背后,否则胜算不大,当然我是比较菜的。




Demo Video: AI bot vs Random bot

1. 模型介绍

对于在某个时刻执行某种动作总是可以从环境获得固定响应的游戏,用NEAT [1] 这样的算法可以获得较好的效果,比如玩马里奥游戏时,如果你一直按着前进键不放,每次重生后的游戏过程都会完全一致(掉入某个悬崖或碰上第一个小怪物),而在Doom游戏中如果你一直在原地射击,你的敌人的行动会不同,出场时你的位置也不同,所以几乎不会出现完全一致的游戏过程(敌人可能会从不同角度射杀你或者一直互不相干)。

同时,与Atari游戏最大的不同是,获取的图像并非游戏的状态State,而是对当前状态的一次观察Observation,即角色周围90°范围内的内容。要解决的问题并不是MDPs(Markov decision processes)问题,而是POMDPs(partially observable MDPs)。DQN [2]在处理action依赖于history states的问题时,不易收敛。实际上在我的多次实验中,稍微复杂的任务都未能收敛。

在实验中发现,将DQN中的Q值网络分为Policy(下图下半部分计算advantage for actions的部分)与Value(上半部分计算state-value的部分)两个共享卷积层的网络(DDQN的做法[3]),并且将二者在网络末尾再次结合获得action value,这样的网络结构收敛能力得到大大的提高。实际上这种方式的优势是收敛性和泛化能力上都得到了提升[4]。



Net Architecture

2. Doom环境

Project: https://github.com/Marqt/ViZDoom

2.1 Quick start

在另一篇Post中,介绍了如何在笔记本上安装CUDA、Theano、Lasagne以及Doom的编译过程。VizDoom在example目录下提供了很多快速上手的示例,对开始使用VizDoom及Lasagne都非常有帮助,建议从learning_theano开始。

2.2 Interfaces

下面是Doom提供的几个比较常用的接口:

make_action: 执行命令并返回reward

get_state: 获取游戏当前状态

get_game_variable: 获取游戏中的某些变量,如血量、子弹数量等

is_episode_finished: 判断当前一局游戏是否已经结束

new_episode: 新开一局

2.3 Doom Builder

VizDoom只是提供一个控制Doom游戏的框架,其控制能力是有限的。实际上要打造一个自己的游戏环境,更合适的方式是用Doom Builder创建自己的地图,可以完成对游戏内容的完全控制。


它包含Vertices、Linedefs、Sectors、Things、Brightness及Make Sectors共6中mode,常用的是前4种,分别编辑地图中的点、线、区域和事物(比如起始位置、补给箱、弹药等)。这些是用来设计地图中的场景,而执行逻辑要靠ACS 脚本。以下面的代码为例,分别定义打开游戏、进入新的episode和角色重生时的动作:角色使用火箭炮,并配备10发炮弹。其他示例可以自行查看scenarios/*.wad文件,所幸这里的代码一般不用很长,所以只要构建出想要的地图和reward机制就可以了(reward机制是重点),Doom中built in的ACS functions可以参见这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "zcommon.acs"
script 1 OPEN
{
}
script 2 ENTER
{
ClearInventory();
GiveInventory("RocketLauncher",1);
GiveInventory("RocketAmmo",10);
}
script 3 RESPAWN
{
ClearInventory();
GiveInventory("RocketLauncher",1);
GiveInventory("RocketAmmo",10);
}

3. 训练过程

视频中的示例的训练过程总共耗时大概四五天时间(GeForce 940M)。训练过程是非常受制于游戏进程的:如果游戏本身运行速度不够快,训练过程中会有相当一部分时间被耽搁在等待游戏返回reward的过程(make_action)。

下图展示的是一些常用的优化方法收敛过程的视图:




Some opt methods

通常训练DQN使用比较多的Rmsprop,不同的优化方法也存在提高训练速度的可能。另一种改进方法是以A3C [5]的方式收集训练transitions,这样游戏进程的速度限制得到缓解。

4. 青出于蓝

在以Random Bot为对手的环境中训练得到AI Bot后,可以尝试以AI Bot为对手,继续训练新的AI Bot2,感兴趣的同学可以试试看青出于蓝是否可以胜于蓝。A被B打败,C又打败B……但是恐怕随着难度的增加,这对训练过程的要求不断提高,是否能够靠同一个模型的不断训练和反馈来得到提高还有待商榷。

相关阅读:

[1]. neuro evolution with mario

[2]. Playing Atari with Deep Reinforcement Learning

[3]. Deep Reinforcement Learning with Double Q-learning

[4]. Dueling Network Architectures for Deep Reinforcement Learning

[5]. Asynchronous Methods for Deep Reinforcement Learning