漫谈深度强化学习之手写Deep Q-Network解决迷宫问题

1. Q-Learning回顾

上一期我们讲了Q-Learning以及Sarsa的算法流程,同时我们还手写了基于Q-Learning以及Sarsa来解决OpenAI gym中的FrozenLake问题。今天,我们将借助神经网络来重新解决这个问题。(FrozenLake问题简单来说就是走迷宫,走错了将不会有任何奖励,走到了目标位置就会获得1的奖励。关于FrozenLake问题的更多描述,请参阅https://gym.openai.com/进行了解)

Q-Learning是一种off-policy的强化学习算法,如前所述,Q-Learning的做法是通过观察一组序列(s,a,r,s’),使得Q(s,a)越来越靠近R+gamma*maxQ(s’,:),这里的行动a可以任意挑选,意思就是它是与策略无关的。Q-Learning的核心算法可以使用如下公式来表示:
Q(St,At) = Q(St,At) + lr [R(t+1) + discount * max Q(St+1,a) - Q(St,At)]

传统的做法是将Q看作一张状态-动作的二维表格,然后通过时序差分算法来更新表格中的每一个元素。那么这种传统的做法有什么缺点呢?显然最大的缺点就是对于状态数目较多的问题,更新Q表的做法很不现实。取而代之的是我们将使用神经网络来对Q值建模,借助神经网络强大的表征能力以及随机梯度下降算法,加上细心的优化调参,我们可以对Q值进行很好地建模。

2. 运用神经网络来模拟Q值

这里我们的实验仍然是基于OpenAI gym的FrozenLake环境,由于强化学习的两大核心主体是Environment和Agent,这里的Environment已经由OpenAI给我们编写好了,因此我们只用专心编写Agent的代码即可。

我们需要使用神经网络来取代传统的Q表学习,我们把要编写的类取名为QNetworkAgent类,QNetworkAgent类虽然将Q表换成了神经网络,但是它仍然是一种Q-Learning算法,因此,QNetworkAgent类中的方法相比父类QAgent而言应该是类似的。我们之前已经编写了QAgent类(代码请见:https://github.com/zzw922cn/Deep-Reinforcement-Learning-in-TensorFlow ),现在只需要把要编写的QNetworkAgent类继承自QAgent类即可,这样我们就可以省去一些代码,但是也要改动或增加一些代码。具体的改动细节,我们分以下三点来说明:

  • 首先,就环境部分而言,QNetworkAgent类无需重写render()、step()、reset()等方法,因为这些是由环境决定的,与Agent算法毫无关系,我们直接继承自QAgent类即可;

  • 其次,我们需要构建神经网络来模拟Q值,那么我们必然需要增加一个方法,即build_network(),而这个方法只在类内部调用,因此我们增加下划线变成_build_network()方法;

  • 最后,对于QAgent父类中已经存在的pick_action()方法以及learn()方法我们需要在子类中重写,原因是父类中的这两个方法都是基于Q表来操作的,而这里我们不存在Q表,因此需要改写一下,不过算法核心还是不变,只是换了一个操作对象而已。

总结一下,QNetworkAgent类中需要写的就是以下四个方法,我们会在下文中一一讲解。

__init__()
pick_action()
_build_network()
learn()

__init__函数

就构造函数而言,由于QNetworkAgent类中会涉及到神经网络,因此,除了继承父类QAgent的构造函数之外,我们有必要在构造函数中增加激活函数、损失函数、梯度下降优化器等参数。于是,整个构造函数如下所示:

pick_action函数

与之前QAgent类中代码类似,这里我们仍然设定两种选取动作的算法,分别是e-epsilon算法add noise算法,而这里我们操作的对象由二维的Q表转变成了一维的动作概率分布向量,此向量由Q-Network产生的。除此之外,我们仍然增加了shuffle选项和softmax选项,供读者在其他复杂环境中选用。整个pick_action的代码如下:

_build_network函数

由于我们需要使用神经网络来代替Q表,从而实现对Q值的模拟,那么我们需要的输入是什么?要训练的参数怎么构造?目标函数又是什么?这些都是我们在运用神经网络进行建模之前所要思考的问题。(话说,人工选取神经网络结构相当困难,也是一项十分繁琐的任务,最近,Google Brain团队提出了使用深度学习的方法来选取神经网络架构,通过seq2seq模型与强化学习相结合,可以找到比较合适应用于cifar-10及PTB语言建模的CNN或RNN结构,具体论文请搜索:Neural Architecture Search with
Reinforcement Learning

回到这里的FrozenLake问题上,我们知道,环境给予我们的反馈主要是价值和状态,因此这里我们要构建的网络的目的就是通过输入当前的状态就可以得到未来的Q的估计值。由于这里问题相对简单,我们只用构建一层神经网络即可,输出就是动作空间的概率分布。

如何确定损失函数呢?由于这一层神经网络输出的是Q的估计值,而我们最终要接近的是Q的目标值,因此,我们可以把Q的估计值与Q的目标值的差的平方作为损失函数。而这个Q的目标值怎么得到呢?今天我们只讲通过传统的公式得到,这种做法与之前的Q-Learning中一样,Q的目标值是不是也可以通过神经网络得到呢?答案是可以的,不过这个问题要留到下次再说,这里我们将Q的目标值也设为输入,因为中间我们需要将Q的估计值和回报值一起代入公式计算才能得到Q的目标值,这一步在下文的learn函数中会提到。

训练神经网络的过程就是选取优化算法,让该损失函数达到最小。整个函数的代码如下:

learn函数

在之前的QAgent中,learn函数的作用是根据目标Q表来更新当前的Q表,而在QNetworkAgent这里,我们操作的对象不是Q表,而是由神经网络产生的Q值向量,尽管操作对象变了,但是我们仍然运用Q-Learning的思想,基于Q的估计值来得到Q的目标值,代码如下:

3. 运行QNetworkAgent

运行QNetworkAgent仍然是使用单步更新的方法,在每一步中,我们依次执行以下算法:

获取agent的当前状态

首先我们调用神经网络得到agent当前状态的Q的估计值

然后由当前状态的Q的估计值选取合理的行动;

紧接着执行此行动并得到新的状态和回报

通过这个新的状态我们再调用神经网络获取新的状态的Q的估计值

将新的状态的Q的估计值和回报传入到learn函数中得到当前状态的Q的目标值

得到了当前状态的Q的估计值和Q的目标值以后,我们就可以将它们传入到loss中并且经随机梯度下降算法来更新神经网络的参数;

运行QNetworkAgent的核心代码如下:

4. 实验结果

经过10000回合的模拟过后,我们发现回合成功率已经达到了57.76%。回合成功率的意思就是截至目前所有的回报值总和除以已经经历的回合数目,由于回报值只在成功达到目标时为1,其余情况均为0,因此回合成功率也就是成功达到目标的回合数除以总的回合数。回合成功率如下图所示:


下一期我们将走进DeepMind的DQN,深入解读其玩转Atari游戏的两大利器:Fixed Target NetworkExperience Reply

本文中所有代码请点击阅读原文即可查阅,整个项目的完整代码的github链接:
https://github.com/zzw922cn/Deep-Reinforcement-Learning-in-TensorFlow

题图:Pawel Kuczynski

你可能感兴趣的:(RL)