[翻译] Deep Reinforcement Learning: Pong from Pixels

最近心情很浮躁,看不进去英文材料。看一两行就会走神,只能约束自己把看到的东西都逐一记述才能沉静下来。

再者,最近在看GAN相关的内容。用GAN去做Discrete的序列生成任务不能直接使用Gradient Descent。原因可能由于离散的数据对梯度修正没有意义,例如图像可以在像素值上加一个梯度的修正,但是对于文本来说,一个词的表示向量上即便加上这个梯度修正,也对原来值没有太大的影响。

而替代方法就是基于深度强化学习(Deep Reinforcement Learning)的Policy Gradient算法。Karpathy的这篇文章是对PG算法的很好的总结和教程。

其实在这篇文章发布之后,也是有一些翻译的版本在国内的各大网站上流传。但是这篇文章其实后续更新过很多次,似乎并没有找到最新版的翻译。

刚好出于研究目的,要精读这篇文章。那么索性就把这篇文章翻译了,顺便能规诫自己认真读下去。

下面就是正文了。限于译者水平,肯定会有不少翻译甚至是理解上的错误,请多包涵,望多交流。谢谢。


这是一篇迟来很久的关于增强学习(Reinforcement Learning, RL)博文。增强学习最近非常火!你一定有所了解,现在的计算机能不但能够被全自动地训练去玩儿ATARI(译注:一种游戏机)游戏(直接输入游戏的原生的像素数据),还能击败围棋的世界冠军、模拟四足动物上蹿下跳。机器人还能学习如何进行复杂的控制任务,甚至比直接编写的程序效果还要好。这些在各个方面的领先都应该被归功于增强学习遍地开花般的研究。我本人在过去几年中也对增强学习非常感兴趣:我完成了Richard Sutton的书,看完了David Silver的课程,看了John Schulmann的讲义,写了一个基于Javascript的增强学习的库,并且在DeepMind公司的DeepRL组实习了一个夏天,而最近的工作有些涉及到一个全新的增强学习评测工具箱OpenAI Gym的设计和开发。所以我实际上已经在这条贼船上带了一整年,但是直到现在我还没抽出时间去写一篇简短的博客来阐述增强学习是什么、为什么增强学习这么重要、如何开发一个增强学习的程序,以及它的应用背景会是什么。

几个增强学习的例子。从左到右:深度Q学习网络玩儿ATARI,AlphaGo,伯克利堆积木机器人Legos,物理模拟的四足动物在地面奔跑。

回忆一下增强学习近期进展的本质是一件很有趣的事情。我比较直白地认为人工智能受到了以下一个因素的掣肘(此处原文为hold back AI,总觉得有些怪怪的):

  1. 计算能力(显而易见:摩尔定律、显卡、专用集成电路)
  2. 数据(带有良好的结构,而不是散落在互联网上 - 比如说ImageNet)
  3. 算法(研究和一些点子,比如说BP反向传播,CNN,LSTM)
  4. 基础设施(软件,比如Linux,TCP/IP,Git,ROS,PR2,AWS,AMT,TensorFlow等等)

与计算机视觉界所经历的一样,增强学习的发展进程不像想你想的那样起源于什么惊世骇俗的灵感。在计算机视觉界,2012年的AlexNet几乎就是1990年代卷积神经网络(ConvNets)的高规格版本。与之相似的是,2013年ATARI的深度Q学习(Deep Q Learning)的论文实际上在功能上近似于一个标准的算法(Q学习,Q Learning,函数逼近,你可以在Sutton1998年编写的标准的增强学习书籍上找到),函数逼近实际上使用了一个卷积网络。AlphaGo使用了带有蒙特卡洛树搜索的策略梯度(Policy Gradients),其实这些也是惯用的组件。当然,也还需要用到大量的技巧和一点儿耐心让它能够正确地运转起来,同时也需要在那些之前被提出的传统算法中应用上一些聪明的小技巧,但是对于一阶近似来说,能产生如此巨大的成就并不是取决于算法,而是(与计算机视觉类似)据取决计算能力、数据和基础设施。

话题回到增强学习。我很喜欢去写博客来说明一件看上去很梦幻的东西和其背后的简单的原理。【译注:这句话的原话语法结构太复杂了,驾驭不住啊】。我见过很多人不敢相信我们能够全自动地让机器像人类一样学会去玩儿大部分ATARI游戏,甚至只需要一个算法、直接输入像素、从零做起,效果非常震惊,我自己尝试过。但是我们所用的核心方法其实非常的简单粗暴(尽管我知道现在回想起来好像这么说会引发一些争议)。无论如何,我将会带你了解Policy Gradients算法(PG),即在这个时候了解增强学习的最佳切入点。如果还是个怎强学习的门外汉,你一定会好奇为什么我不去介绍DQN算法呢?它可是一个更广为人知的增强学习算法,并且在玩儿ATARI游戏的论文中经常出镜。这是由于Q学习并不是一个非常强大的算法(你可以说DQN在2013年很强大(好吧我是在半开玩笑地说~))。事实上,大多数人更倾向于使用策略梯度算法,甚至包括最早DQN论文的作者,他发现如果参数调的好,则它比Q学习效果还要好。我们首选PG算法是因为它是端到端(End to end)的:有一个显式的策略,和一个能够直接优化期望回报(expected reward)的规范的方法。好的,作为例子,我们将会从零开始,在像素级别,使用一个深度神经网络,并用PG来学习去玩儿ATARI的游戏(Pong!)。所有这些事情只需要130行的Python就能搞定,并且只用到了numpy库。让我们试试吧!

Pong from pixels

Pong这个游戏。

Pong是马尔可夫决策过程(Markov Descision Process, MDP)的一个例子:图中的每一个结点都是游戏过程中的一个特定状态,每一条边都是一个(通常是概率意义上的)可能的变换。每一条边带有一个“回报”(reward),而任务的目标是在任意状态计算出一条最优的路径来最大化回报。

Pong这个游戏非常适合用来解释一个简单的增强学习的任务。在ATARI2600版本的这个游戏中,你需要控制一侧的挡板(另一侧由一个还算靠谱的AI控制)然后你需要通过撞击挡板将球反弹给对方(我貌似不用解释的太详细了对吧?)。我们可以用更加底层角度看待这个任务:我们接收到了一帧图像(一个210x160x3的字节数组(从0到255的整数值表示像素值)),然后我们来决定是要让挡板向上还是向下(这是一个二进制的选择)。在每一次选择之后,游戏模拟器执行动作然后给我们一个回报:一个+1回报表示我们成功将球击向对手,一个-1回报表示丢了球,或者一个0回报表示其他情况。当然,我们的目标是通过移动挡板来得到很多很多的回报。

在我们解决这个问题的时候,我们要做几个假设,因为我们的关注点并不是Pong这个游戏,而是在于研究一个复杂的高维问题,比如说操纵机器人,装配和导航。Pong只是一个有趣的测试例子,而总有一天我们会写出一个非常通用并且能做很多有用的任务的AI系统。

策略网络(Policy Network)

首先,我们要去定义“策略网络”这个概念。它能作为我们的“玩家”(或者“代理”,即agent)。这个网络能够以当前的游戏状态为输入,并且能够决定我们应该采取什么动作(向上或者向下)。为了计算方便,我们使用了一个两层神经网络,接受原生的图像像素作为输入(一共100,800个数(210*160*3)),然后产生一个数字来表示“向上移动挡板”的概率。通常情况下我们会用“stochastic”策略,就是说我们只产生一个向上移动的概率值。每一次迭代我们都会从这个概率分布去采样(就好像投掷一枚不均匀的硬币)来决定到底是向上还是向下(译注:这个产生出来的值只是一个向上的概率,而不是说当这个值大于某个阈值就向上,小于阈值就向下。真正使用的时候就是以这个概率值去抛硬币。也就是说几遍这个概率告诉你百分之90要向上,你也是有可能采样获得“向下”这个决策的)。这么做的原因我们会在讲解训练算法部分仔细说明。

我们的策略网络(Policy Network)是一个两层的全连接网络。

为了更加详实地阐述,这里我们用Python/Numpy来实现这个策略网络。假设我们有一个变量x,里面有(经过预处理之后的)像素信息。这时候我们可以计算:

h = np.dot(W1, x) # 计算隐层单元激活值  
h[h<0] = 0 # 使用Relu非线性变换:阈值是0  
logp = np.dot(W2, h) # 第二层  
p = 1.0 / (1.0 + np.exp(-logp)) # sigmoid 函数(输出一个概率值)  

在代码中,W1W2是两个随机初始化的矩阵。这里没有用到偏置因为不是很重要。要注意我们在最后一层使用了sigmoid非线性变换,它能将输出值修整到[0,1]区间(译注:这样才是概率嘛!)。从直觉上来说,隐藏单元的神经元(在W1上的每一行权重)应该能探测到不同的游戏场景(比如说球在顶部,挡板在中间),而在W2中的权值应该能在各种情况下决定我们是要向上移动还是向下移动。我们现在讲W1W2随机初始化之后,将会导致这个AI抽风式地运动。所以我们需要做的就是找到一个合适的W1W2让这个AI称为Pong的专家!

额外说一下preprocessing。我们其实需要至少将两帧输入到策略网络中,否则网络将没法探测到运动(译注:一张照片并不能表示球的运动方向等)。为了让这件事更简单一些(我在我的Macbook上测试过),我将会做一个微小的预处理。比如说我们实际输入的是两帧的“区别”(比如说将当前帧减上一帧得到的结果)。

听起来不太可能

现在我需要让你明白一个增强学习任务到底有多难。我们有100,800个数(210*160*3)在我们的策略网络中(这个网络的W1W2很容易就会包含上百万个参数)。假设我们决定向上移动挡板。在当前时间帧,游戏认为获得0回报(译注:因为球还没撞到挡板,所以并不是成功反击的+1回报,也不是丢球的-1回报,而是0回报),而在下一个时间帧,又将会有100,800个数字。我们需要重复这个步骤高达上百次,直到出现一次非0的回报(译注:成功反弹或者丢球)。假设我们最终得到了+1回报。这很棒,但是我们如何知道究竟是什么导致了这个回报的发生?我们在刚刚那帧做了些什么?或者说在第76帧做了什么?或者是不是我们在第10帧时的操作和第90帧时做了某个至关重要的动作?那么我们该如何知道这上百万的参数该调整那些、该如何调整才能后续的测试中达到更好的效果?我们将这个问题称为“credit assignment”问题(credit assignment problem)。以Pong为例我们知道,一个+1回报表示球成功地传给了对手。真实情况是我们将球反击出了一个正确的轨迹,但实际上这是因为我们很久以前某一帧做出了正确的决定,比如说,有可能是20帧之前的某个操作就决定了最后能将球击中。而在这之后的操作可能对是否能得到回报并不起作用(译注:如果能预测球的落点,在地20帧的时候把挡板放对就ok了,后面不论挡板怎么调整,只要击球时还在这个位置,就能得到回报了)。说了这么多就是为了告诉你,我们面临的这个问题非常之复杂,并且看起来是很难解决的。

有监督学习 (Supervised Learning)

在深入了解用到Policy Gradients的解决方案之前,我还想带你回忆一下有监督学习的基本知识,因为它跟增强学习非常相似。参考下图。在传统的有监督学习中,我们将一张图片输入到网络中,然后获取一些概率值。比如说“向上”、“向下”的二类分类问题。我所展示的是log概率(-1.2, -0.36)而不是概率(30%和70%),因为我们经常去优化正确类别的log概率,这在数学上要更好一些,而且这对于优化问题来说是等价的,因为log是单调的。现在,在有监督学习中,我们必须要用到label。例如,我们被告知了现在应该让挡板向上(label 0)。此时我们需要将UP对应的log概率设为1,然后运行梯度下降算法去计算\(\nabla_W\log p(y=UP|x)\)。这个梯度将会告诉我们这上百万个参数中的每一个都应该如何调整,来让网络稍微倾向于输出“向上”。例如。网络中上百万个参数中的一个有可能具有梯度-2.1,这就意味着如果我们在这个参数上增加一个很小的正值(比如0.001),那么log概率将会增加2.1*0.001(如果是负值则就会减小)。如果我们这样更新参数,那么网络在下次看到一张非常相似的图片的时候,就会更加倾向于去输出“向上”。

策略梯度 (Policy Gradients)

但是,在增强学习的过程中,我们没有正确的label那该怎么办?所以我要介绍一种被称作是Policy Gradients的方法(如下图所示)。当我们的策略网络计算出向上的概率是30%(log概率是-1.2),向下的概率是70%(log概率是-0.36).我们现在从这个分布中去采样;比如说,假设我们采样得到的结果是“向下”(译注:采样方法比如说,产生一个0~1的均匀分布的随机数,判断这个数>=0.7还是<0.7,对应决定输出是“向上”还是“向下”) ,然后我们在游戏中执行这个操作。这时候我们会注意到一个有趣的现象:我们可以像有监督学习中那样,立刻在“向下”的梯度上赋值1.0,然后去找到能让网络以后略微倾向做出“向下”动作的梯度向量。但是问题是,直到目前为止,我们也不知道“向下”这个操作是好是坏。但这不是个大问题,我们可以等一阵然后看看结果。比如对Pong这个游戏来说,我们能够一直等到游戏结束,然后获得收益(如果赢了就是+1,如果输了就是-1),然后认为相对应的值就是我们所做的操作的梯度(本例中是“向下”)。在下面的例子里,“向下”最终会导致游戏失败。所以如果我们用-1表示“向下”对应的log概率然后进行反向传播我们将会让网络对这个输入在以后不鼓励做出“向下”的操作(显然是这样,因为这个动作将会导致最后输掉游戏)。

也就是说:我们用一个随机的策略来产生动作,如果这个动作在未来最终带来好的结果那么将会被鼓励,如果这个动作在未来最终带来坏的结果那么将会被压制。除此之外,甚至在我们赢得或输掉游戏时,回报不一定要求是+1或者-1。它可以是任何表示最终效果的度量手段。例如我们可以认为如果任务做的好,那么回报就是10.0,如果最终结果还不错,那我们就把它作为梯度而不用-1,然后进行反向传播算法。这就是神经网络美妙的地方;使用它们就好像在作弊一样:你能够将一百万个参数绑定在一个运算速度高达1 teraflop的机器上然后用上SGD(随机梯度下降)。它看起来并不靠谱,但神奇的是我们正生活在一个让他能靠谱运转的世界。

Training Protocol (译注:实在不知道怎么翻译了)

下面详细介绍一下训练过程。首先我们初始化策略网络的W1W2然后进行100次Pong的游戏(我们称这些策略叫做“rollouts”)。假设每一次游戏由200帧组成,所以我们一共要进行20,000次“向上”或者“向下”的决定,而且对于每个决定,我们都能够知道梯度,这个梯度则告诉了我们想要在未来做出更好的决策应该做出怎样的调整。现在只剩下去对我们做的每个决策标记上是“好”还是“坏”了。例如我们赢了12场游戏,输了88场游戏。我们将会用在赢的游戏过程中做出的200*12=2400个决策做出“正”的更新(对采样得到的动作赋值+1的梯度,然后进行BP算法,参数更新将会鼓励我们所采取的这些动作)。然后我们会将其他在输掉的游戏中所做的200*88=17600个决策标记为“负”的更新(不鼓励采取这些动作)。然后网络将会逐渐偏向做出有用的动作,而减少做出无用的动作。然后我们再用我们轻微修正过的新的策略网络进行100场游戏,然后再去轻微地更新策略网络。

策略梯度:让动作执行一会儿,看看什么动作将会产生高回报,那么就提高这部分动作在当前情况下出现的概率。

图中展示了4场游戏。每一个黑色的圆圈就是一个游戏的状态(图的下部有三个例子),每一个箭头代表一次状态转换,上面标注的是采样得到的动作。在这个例子中,我们赢了两场游戏,输了两场游戏。通过策略梯度算法,我们用两场赢了的游戏的数据来稍微鼓励网络做出这两个episode(译注一场游戏的一系列决策称为一个episode)中的每一步。相反的,我们用输掉的两场游戏的数据来稍微抵制网络做出这两个episode中的每一步。

如果你仔细想想这个过程,你会发现一些有趣的事情。比如说如果我们在第50帧时做了一个“正确”的动作(比如将球正确地反击回去),但是在第150帧的时候却丢了球该怎么办?如果此时每一个动作都被标记成“不好”的动作(因为我们丢了球),那么会不会在第50帧的动作也会被压制?是的,它会被抵制。然而其实你可以考虑一下当游戏进行了成千次甚至上百万次时,虽然你第一次正确的反击本应让你偏向赢得游戏,但平均来说,正确更新的数量还是要大于错误更新的数量的,所以最终你的策略将会学习到做出正确的动作。

2016年12月9日更新:从另一个视角解释

在上面解释中,我用到了诸如“将梯度设置成xx然后用反向传播算法”这样的话,这是基于你们很熟悉如何编写你自己的反向传播的算法,或者经常使用Torch之类的需要自己显示地写出梯度的框架。然而,如果你经常用的是Theano或者TensorFlow,你有可能会对这句话有些困惑,因为在这些框架中,代码是围绕着损失函数组织的,而反向传播算法是全自动进行的,并且很难去对它做修改。在这种情况下,从另一个角度解释这件事或许更加直观。在最简单的有监督学习任务中,目标函数是最大化\(\sum_i\log p(y_i|x_i)\),其中\(x_i\)和\(y_i\)是训练样本(比如说是图片和它们的标签)。策略梯度与有监督学习除了这两点以外是完全一样的:1)我们没有正确的标签\(y_i\),所以我们用网络看到\(x_i\)时采样的动作作为“假标签”来代替。2)我们基于最终的结果(译注:赢或输)来调整基于多个样本相乘的损失函数(译注:因为最终的损失函数会由多个样本相乘组成,参见极大似然估计MLE),就是说,我们要对有效的动作提高它的log概率,对无效的动作降低它的log概率。综上所述,我们的损失函数现在应该是这样:\(\sum_iA_i\log p(y_i|x_i)\),其中\(y_i\)是我们采样得到的动作,\(A_i\)是一个被称为“advantage”的数。比如在Pong例子中,当我们经过整个episode最终赢得了胜利\(A_i\)就是+1,反之如果我们输了就是-1.这能保证我们动作的log概率在好结果中会被最大化,坏结果中会被最小化。所以说,增强学习完全就是有监督学习,只不过区别在于数据集是在不断变化的(每次都会有不同的episode),然后与"advantage"相乘,然后我们每次采样得到的数据集只会做一次(或者少量的)更新。 (译注:与有监督学习相比,RL重点在于在整个episode结束前,是不能确定每个标签的。所以说,产生一个episode->更新策略网络->再基于最新的策略网络产生新的episode->循环进行。)

更加通用的“advantage”函数

我之前说过要多讲一讲“回报”。目前为止,我们去评价一个动作的好坏都是依据最终会不会赢得游戏。一个更加通用的增强学习的设计是我们将会在每一个时间步都获得一个回报\(r_t\)。一个常用的选择就是“discounted reward”。所以上图的最终回报(eventual reward)就会变成\(R_t=\sum_{k=0}^\infty\gamma^kr_{t+k}\)。其中,\(\gamma\)是一个0~1之间的数,称作是discount factor(取值比如说0.99)。这个表达式说明,我们对于一个采样动作的鼓励强度,是后续所有回报的加权求和决定的,但是后面回报的重要性是以指数级衰减的。在实践中,标准化这些回报也非常重要。比如说我们要计算所有100场Pong游戏中的20,000个动作的回报\(R_t\)。一个比较好的做法就是在把这些数据送到BP算法之前,对这些回报做一下标准化的操作(比如通过减均值除标准差的方法)。这样我们总能大约鼓励一半的动作、压制一半的动作。从数学上来说,这也可以被认为是一种控制策略梯度的方差的一个技巧。更加相近的解释可以参考这里

推导策略梯度

下面我会大概说一说策略梯度是怎样从数学中得到的。策略梯度是一个更加广义的概念——“score function gradient estimator”的特殊形式。通常的情况下我们需要计算形如\(E_{x\sim p(x|\theta)}[f(x)]\)的表达式,比如说计算一个标量值函数(scalar valued score function) \(f(x)\)服从以\(\theta\)为参数的概率分布\(p(x;\theta)\)的期望。提示:\(f(x)\)可以作为我们的回报函数(或者更通用地说,可以作为advantage函数),而\(p(x)\)是策略网络,也就是给定任意图象\(I\)后对动作\(a\)的分布函数\(p(a|I)\)。那么,我们感兴趣的是我们应该如何通过参数\(\theta\)来修正这个分布,使得它能对样本有着更高的得分,而这个得分是由\(f\)决定的(即,我们应该如何修改网络的参数才能让动作样本得到更高的分数)。我们有下面的公式:

$$ \begin{align} \nabla_\theta E_x[f(x)] & = \nabla_\theta\sum_xp(x)f(x) & \text{definition of exception} \\ & = \sum_x\nabla_\theta p(x)f(x) & \text{swap sum and gradient} \\ & = \sum_x p(x)\frac{\nabla_\theta p(x)}{p(x)}f(x) & \text{both multiply and divide by} p(x) \\ & = \sum_x p(x)\nabla_\theta\log p(x)f(x) & \text{use the face that} \nabla_\theta\log(z)=\frac{1}{z}\nabla_\theta z \\ & = E_x[f(x)\nabla_\theta\log p(x)] & \text{definition of exception} \end{align} $$

译注:经过上面的公式,我们想要计算 \(\nabla_\theta E[f(x)]\),不再需要真的去计算带有函数\(f(x)\)的梯度了。只需要1)按照某个概率分布采样,2)直接计算\(E_x[f(x)\nabla_\theta\log p(x)]\)来代替即可。这么做的好处是我们可以定义一个非常复杂的打分函数\(f(x)\),甚至这个函数可以不可微,因为我们在后续的求导中,完全没有这个函数的参与,只要概率分布可微就行了。所以,在实际应用中,\(f(x)\)常常是一个复杂的函数,而\(p(x)\)则可以是一个由神经网络组成的策略网络等模型,因为神经网络一般是可微的。

说人话就是:我们有某个概率分布\(p(x;\theta)\)(为了简便所以写成\(p(x)\)),并且可以从中进行采样(比如说从一个高斯分布)。对于每一个样本,我们可以对其用评分函数(score function)\(f\)打分,即输入样本到函数中,得到一个标量值作为分数。这个等式告诉了我们应该如何调整分布(由参数\(\theta\)控制)才能得到用\(f\)评价的更高的分数。特别地,它像是在说:抽一些样本\(x\),然后评价一下它们的分数\(f(x)\),然后对每个\(x\)再计算一下第二项\(\nabla_\theta\log p(x;\theta)\)。第二项的内容是啥?它是一个向量:也就是能够知道我们在参数空间应该如何调整才能使\(x\)出现的概率尽可能大。换句话说,如果我们有在\(\nabla_\theta\log p(x;\theta)\)的方向有连续不断的\(\theta\),我们将会看到产生x的概率将会稍稍增加。在回顾一下公式,它还告诉我们我们要把这个方向与那个标量打分函数(scalar-valued score)\(f(x)\)相乘。这会使得具有更高得分的样本对概率密度的“拖拽”力度要比分数低的样本大一些。所以如果我们基于多个从\(p\)中采样的样本去更新概率密度函数,我们将会在想得分更高的样本方向多偏离一些,这能够让得分更高的样本更有可能被采样出来。

一张关于分数梯度函数的可视化图。:一个高斯分布,在其中采样了几个样本(蓝色点)。在每一个蓝色点上,我们也画出了对应高斯均值参数的log概率的梯度(译注:即 \(\nabla_\theta\log p(x;\theta)\))。箭头指向了想要使分布更容易采样到样本的调整方向的均值。:一些打分函数对样本给出了-1分,而在一些小区域中给出了+1分(注意此处不必须是一个可微的标量值函数)。现在箭头被染色了,因为在更新中包含了乘法,我们会将所有绿色和红色的箭头分别取平均。:经过参数更新之后,绿色的箭头和相反的红色箭头(译注:与-1相乘表示取向量的反方向)会将分布向左下角轻轻推动。正如我们所希望的一样,现在再从这个分布中取值,将会更容易得到更高的分数。

希望我讲明白了这与增强学习之间的连接。我们的策略网络就是让我么能够采样出一系列比其他动作更好的动作(通过advantage函数判断好坏)。这些数学公式的片段告诉我们了一种能够修正策略参数的方法就是先让动作执行一会儿,然后计算采样的动作的梯度,乘上它们的得分,然后将他们加起来,这就是上面做的事情。想要了解更详尽的来龙去脉,我推荐你去阅读John Schulman's lecture.

学习

好了,我们已经介绍了策略梯度的直观想法并且简单了解了它的起源。我实现了一个总共130行的Python脚本,它使用了OpenAI Gym的ATARI2600 Pong游戏。我训练了一个包括了200个隐层单元的两层策略梯度网络,并且用RMSProp算法在batch大小为10的episodes上训练(每一个episode都包含了几场游戏,因为比赛最高成绩就是21分)。我并没有调整太多的代码,然后在我的Macbook(非常慢)上运行了3个晚上,最终得到的策略网络能比AI玩家稍微好一点。Episode的数量共计8,000左右,算法大约玩儿了200,000场Pone的游戏(非常多!对不?),大约做了800次梯度的更新。我朋友告诉我说如果你在GPU上用卷积神经网络训练几天,你能更加频繁地打败AI玩家,如果你要是能把参数调的好一些,你就能永远处于支配地位(就是说,赢得每一场游戏)。然而,我没有话太多的时间去计算或者调优,在用Pong AI成功验证了主要的思想并且效果还不错我就停掉了程序。

学习到的权重

我们可以看一看学习到的权重的内容。经过预处理,我们输入的都是80x80大小的图片(当前帧减去上一帧。译注:保留一部分运动信息)。我们现在取W1的每一行,把他们展开成80x80的大小然后画出来(译注:此处可能跟之前提到的210x160x3的结构不太一样。这里的输入图片单张只存为80x80=6400,然后传输到200个神经元中,所以矩阵W1的维度是6400x200)。下图表示了40个(共计200个)神经元的可视化结果。白色的像素表示正值,黑色的像素表示负值。我们会发现有几个神经元被调整出了特定的球的运动路径,并被编码成了“黑白相间”的线段。由于球只能在一点处被击中,所以这些神经元能处理多个任务并且会在球沿着线的多个位置“激发”(译注:此处的激发应该不是指击球。而是说当球沿着这些路径的时候,这些神经元将会活跃起来,处于激发的状态)。“黑白相间”很有趣,因为在球运动的过程中,神经元像sin函数一样波动是由于使用了ReLU作为激活函数,它会在轨迹的不同位置上离散式地激活(译注:ReLU在x大于0的时候是线性,小于0时就是0,所以不是随时激活)。图片看起来有一些噪声,我觉得或许用上L2正则效果会好一些(译注:加上L2正则项能一定程度上避免过拟合,即避免了图像“记住”一些特例,更容易分辨一些“普遍的特征”。这些特例可能就会产生图像上的噪声)。

哪些事情没发生

所以说,到目前为止,我们从原始像素点去学习到了如何利用策略梯度去进行Pong游戏,并且效果也非常好。这个方法时“guess-and-check”的一种理想形式。“guess”表示从我们当前的策略出发产生一个动作序列,而“check”表示我们要估计能产生正回报的动作。讲的更详细一些,这代表了我们目前这个增强学习问题所能达到的最好的效果。能够学习到这样的行为令人印象深刻,但是如果你能直观地了解到这个算法并且知道它如何工作的,你会有一点儿遗憾。尤其是,什么情况下它会不好使?

与人类如何学习进行Pong游戏相对比。你向人们展示这个游戏,然后告诉他们规则“你要控制这个挡板,它能向上向下运动,你的任务就是将球传给其他由AI控制的玩家”,然后就准备就绪可以开始了。注意其中由这么几个区别:

  • 在实践种,我们通常会用某种方式来交流沟通这个任务,但是对于标准的增强学习来说,你只能假设一个回报函数然后从与环境的交互中进行探索。而当人类参与到Pong中时,如果没有明确的关于回报函数的知识(尤其是回报函数是静态但是随机的)时,人类在学习的过程中将会面临很多困难。但是策略梯度就则不关心这些,并且可能比人类做的还优秀。相似地,如果我们把各帧的像素随机地置换,对于人类来说很有可能玩儿不好,但是对于策略梯度来说甚至可能都感觉不出什么区别(如果它们用的都是全连接网络的话)
  • 人类是带有大量先验知识的,比如基本的物理学(球会反弹而不会瞬间移动,也不太可能会突然停住,因为它带有一些速度)。和一些基本的心理学(AI对手“渴望”去赢,那就有可能用某种显而易见的策略去移动球等等)。而且你心中有“控制”挡板的这个概念,并且会反映在向上向下的按键上。与此相反,我们的算法从零开始但很快就让人印象深刻(因为它确实有效)和沮丧(因为我们并不知道它什么时候就失效了)
  • 策略梯度是一种暴力搜索的方法,正确的动作最终会被发现并且固化到策略中。人类会构建一个强大又抽象的模型,然后用它做规划。在Pong中,我们能推理出对手比较慢,所以“将球高速反击”将会是一个比较好的策略,这可能会导致对手无法在足够的时间内碰到球。然而,我们也会最终将这策略以肌肉记忆的形式固化在身体里。例如,当我们想要学习一种新的需要移动的任务(比如说学习驾驶一辆手动变速的车)你在刚开始会需要经常思考,但是最终会变得不需要思考,自动就能进行了。
  • 策略梯度必须直接输入正回报,并且需要经常体验到正汇报,以便于它能慢慢地推动策略参数偏向能重复产生高回报动作的状态。而人类能在不需要直接体验的回报(译注:知道最终结果前)就能知道那些动作更容易获得高回报。我不用将我的汽车撞毁几百次才能缓慢地学会如何避免碰撞。

蒙特祖玛的复仇(Montezuma's Revenge):一个对我们的增强学习算法来说非常复杂的游戏。玩家必须跳跃、攀爬、拿到钥匙然后打开门。计算机随机采样了几十亿次的移动,但是99%都是会掉下去摔死或者被怪物杀死。换句话说,它非常难以进入到奖励状态(译注:绝大部分的采样最终都是负回报,几乎没有正回报,所以没法训练)。

另一个非常难的游戏Frostbite,人类能理解运动,一些东西可以接触,另外一些东西不能接触,游戏的目标是一块一块搭建一个冰屋。Building Machines That Learn and Think Like People是一个不错的关于这个游戏的分析和关于人与机器之间区别的讨论。

我想要强调一点,相反地,有很多游戏策略梯度能轻易打败人类。特别是任何会经常有回报信号,并且需要精准操控、快速反应的游戏。那些不需要做长期规划的游戏很合适。这些短期的游戏的动作与回报之间的关系能够被算法轻易地“发掘”到,然后悄悄地被策略驱使着完善自己。这在我们开发的Pong的智能体中已经出现:它能学习到一种策略,等待球靠近然后在边缘将它接到,因为这样能以很高的速度将球传回去。我们的智能体多次用到了这个策略。深度Q学习在很多ATARI游戏上都用这样的方式打败了人类,比如说Pinball, Breakout等等。

总结一下,一旦你理解这些算法工作的“技巧”,你就能推理出它们的强项和弱项。现在人类距离构建一个抽象、并且具有丰富的游戏表示来帮助我们进行规划并且能快速学习的系统还差得远。总有一天,一台计算机将会意识到一个像素数组是一把钥匙、一扇门,并且能意识到要捡起钥匙去开门。但是现在我们里这个还是非常遥远的,而这个领域是一个热门的研究领域。

神经网络中的不可导运算

我想提一个跟策略梯度玩儿游戏无关的应用:它能够让我们去设计并训练一个由不可导单元组成的神经网络。这个概念第一次由Williams于1992年介绍,并且最近在《Recurrent Models of Visual Attention》中用“hard attention”这个名字重新发展起来。这个模型用一系列低解析度的“foveal glances”来处理图像。特别是在每一次迭代中,RNN都会收到图像的一个小片段,然后随机采样一个下一次会看到的位置。比如说,RNN有可能正在看(5, 30)这个位置,然后看到了图像的一个小片段,然后决定去看(24, 50)这个未知,以此类推。这个想法有个问题就是这将会产生一个“下一次看哪里”的分布,然后从这个分布采样一个位置。但不幸的是,这个操作是一个不可导的操作,因为我们不知道如果采样得到其他的位置将会有什么变化。具体一些地说,考虑一个神经网络,接受一些输入产生一些输出:

我们会发现大部分的箭头(蓝色的)像往常一样都是可导的,但是一些变换形式有可能包含一些不可导的采样操作(红色的)。我们能正确地对蓝色箭头用BP算法,但是红色箭头所表示的依赖关系我们是没法用BP算法的。

而策略梯度则则解决了这个问题!我们可以将网络的一部分看作是嵌入在一个大网络中的随机策略。因此,在训练过程中我们将会产生几个样本(就是下图中的分支),然后我们会去鼓励产生那些最终能带来正回报的样本(比如说需要在整个过程结束后才能评价损失函数值的时候)。换句话说,我们将会像平时一样去用BP算法来训练蓝色箭头表示的参数,而对于红色箭头中的参数,我们现在会用策略梯度的方法来向前传递,然后鼓励能采样出使最终损失降低的样本。这个想法有一个很好的形式化说明:《Gradient Estimation Using Stochastic Computation Graphs》

可训练的存储I/O

你可能会在很多其他的论文中见到这个想法。比如说,神经图灵机(Neural Turing Machine)有一个“磁带存储器”,并且能从上面读取数据,向上面写入数据。执行写操作一般会执行形如m[i] = x的操作,其中的ix是通过一个RNN控制器网络(RNN controller network)来产生的。然而,这个操作是一个不可导操作,因为并没有一个信号告诉我们如果我们向一个不同的位置j != i写入,损失函数会有什么变化。因此,神经图灵机必须去做“soft”读和写操作。它要预测产生一个分布a(每一个元素都是0~1之间,而且和为1,然后在我们要写入的索引附近的位置应该有很多峰值。译注:表示这个位置更容易被采样得到),然后执行for all i: m[i] = a[i]\*x。虽然现在可导了,但是我们不得不付出很大的计算代价,因为必须访问每一个内存单元去写入。想象一下,当每一次赋值操作都会访问整个内存! (译注:这一段是说,神经图灵机不能像真的计算机一样,直接就能向某个内存单元写入。因为这样的操作是不可导的。解决方法就是通过产生一个分布,然后从这个分布中采样得到每一个“每一个内存单元内是否写入数据”的样本,然后通过遍历每一个内存单元与这个样本共同控制是否写入内容。这样虽然让这个问题变得可导、能用BP算法运算,但是每一次操作都会伴随着对所有内存单元的访问。)

然而,我们能用策略梯度(在理论上)来解决这个问题,而RL-NTM也就是这么做的。我们现在还是预测一个attention分布a,但是现在我们不再做soft写入,而是采样得到一个写入的位置:i=sample(a); m[i]=x。在训练过程中,我们将会对一个小批i进行这个操作,而最终要看哪一个分支工作的最好。而且在测试阶段,我们只需要对一个位置进行写入/读取就可以了,这回打来巨大的计算力的解放。然而,就像这篇论文中指出的一样,这个策略非常难以发挥作用,因为采样总会导致意外的错误。现在,大家一致认为策略网络只有在只包含了几个离散的选择时才有效,当需要从巨大的搜索空间中搜索时,这个方法就变得毫无希望了。

然而,我们拥有策略梯度算法和大量的数据和计算能力,我们现在能想的长远一些,比如说我们能够设计一个神经网络,去学习如何与一个巨大的、不可导的模块进行交互,例如LaTeX的编译器(比如说你可以做一个字符级的RNN然后产生LaTeX代码然后编译),或者一个SLAM(Simultaneous Localization and Mapping)系统、一个LQR(Linear Quadratic Regulator)求解器,或者其他的什么东西。再比如说,一个超级智能的东西能够学习如何用TCP/IP协议与互联网进行交互(这显然是一个没法求导的问题)去了解如何接管这个世界的致命信息。

结语

我们看到了策略梯度这个功能强大,适用范围广泛的算法,并且作为例子我们从零开始用130行Python代码实现并训练了一个ATARI Pong的像素级智能体。同样的算法还可以被用来训练很多其他的游戏,总有一天它会为很多现实世界中复杂控制问题的解决带来希望。我想要在这篇文章的结尾处再写几句:

关于推动AI

我们已经了解到这些算法使用了包里搜索的方法,也就是刚开始随机地游走,然后一定会跌跌撞撞地得到至少一次回报,然后再理想情况下,这个过程要重复很多次,直到策略分布通过调整它的参数能够做出合理的动作。我们也了解到这些问题对人类来说也是非常困难的。这就好像快速构建一个抽象模型——我们仅仅在研究中触及冰山一角(尽管很多然都在尝试)。这些抽象模型非常难(如果不是不可能的话)以明确地被评注,这也就是为什么我们最近在(无监督)生成模型和程序归纳(Program Induction)领域表现出极大兴趣的原因。

关于复杂机器人的应用

算法并不能简单地推广到需要大量探索的场景中。比如说,一个机器人应用中有可能包含了一个(或几个)机器与现实世界中的实时交流。我们上面提到的算法在这个场景中可能没法使用。一个试图解决这个问题的相关工作是deterministic policy gradients。这个方法不是通过从随机策略中采样然后鼓励这个采样能获得更高的分数,而是通过确定的策略直接从第二个网络(称为critic)中获得梯度信息并对打分函数建模。这个方法能够在高维动作场景中取得更好的效果,而动作采样的方法收敛性不强。但是目前从经验上看来想要让它有效果还是需要很多繁琐的工作需要做的。另一个相关的方法是规模化机器人(scale up robotics),就像谷歌的机械臂农场,或许还包括Tesla的Model S和自动驾驶技术

还有另一种方法能使得搜索过程更加容易,那就是加上额外的监督信息。在一些特定的情况,机器能获取到人类专家的指导。比如说AlphaGo就首度利用了监督学习的方法去预测人类围棋专家的落子,然后去再通过策略梯度针对赢得游戏的“真实的”目标函数微调之后,模仿人类的落子策略。在一些情况下只需要不多的专家指导(比如远程操作机器人),话有一些其他技术通过apprenticeship learning利用这些数据。最后如果没有任何人类提供的监督数据,仍然有些代价很高的优化算法可以用。比如说trajectory optimization就是一个广为人知的动力模型(比如说物理引擎中的\(F=ma\)),或者其他可以通过学习局部近似的动态模型(常见于非常靠谱的框架Guided Policy Search

关于使用策略梯度的最佳实践

在上面我提到过,我想做一些在RNN那篇博文中做过的事情。我在那篇文章中展示了RNN的魔力,并且展示了一个自动序列生成问题。但事实上,想要让这些模型正确工作起来是需要很多小技巧的,它需要小心而又专业地调参,并且在很多时候,用这么复杂的模型就像大炮轰蚊子。可能一个更简单的方法在90%的情况下都是有效的。同样,对于策略梯度来说,它也不是全自动就能用的:你需要大量的样本,然后永远训练下去,当它不好使的时候也很难去debug。你应该在决定使用火箭筒之前先试试玩具枪。在增强学习的例子中,有一个很厉害的基准方法你应该先试试,那就是交叉熵方法(Cross-entropy Method, CEM)。它是一个由进化算法启发而成的简单的随机向上的“猜测、验证”方法。如果你一定要在你的问题中尝试使用策略梯度,那就一定要多花些经历在很多论文tricks的章节。从最简单的开始,使用一种PG的变种TRPO。它在实践中几乎总是比最普通的策略梯度算法有效。它的核心思想是避免因参数更新导致策略变化的太频繁,强制增加了一个子一组数据上得到的新老两个分布KL距离的约束(而不是结合上所有的梯度,关于这个想法最简单的例子就是线搜索(line search)然后一直检查KL距离)。

好的!我希望我已经大致给你讲明白了什么是增强学习,以及其中的难点是什么。如果你非常渴望去帮助增强学习的发展,那我邀请你在OpenAI Gym中尝试一下:)下次见。

Friskit

继续阅读此作者的更多文章