TensorFlow 2.0 (九) - 强化学习 70行代码实战 Policy Gradient
源代码/数据集已上传到 Github - tensorflow-tutorial-samples
这篇文章是 TensorFlow 2.0 Tutorial 入门教程的第九篇文章。
实战策略梯度算法(Policy Gradient),代码70行
CartPole 简介
在之前的文章中,我们使用过纯监督学习的算法,强化学习算法中的Q学习(Q-Learning)和深度Q网络(Deep Q-learning Network, DQN),这一篇文章,我们选择策略梯度算法(Policy Gradient),来玩一玩 CartPole。
先回顾一下CartPole-v0的几个重要概念。
概念 | 解释 | 示例 |
---|---|---|
State | 状态,[车位置, 车速度, 杆角度, 杆速度] | 0.02, 0.95, -0.07, -1.53 |
Action | 动作(0向左/1向右) | 1 |
Reward | 奖励(每走一步得1分) | 1.0 |
我们在 TensorFlow 2.0 (八) - 强化学习 DQN 玩转 gym Mountain Car这篇文章中,介绍了基于价值(value-based)的强化学习算法 DQN,在 DQN 中,神经网络的输入是状态,输出是每一个动作的价值。每一次从所有可行的动作中选择Q值最大的执行。我们使用了一个公式来不断地计算期望的Q值,训练神经网络。
那有没有可能,直接输出动作呢?这就是我们今天要介绍的基于策略(policy-based)的策略梯度算法(Policy Gradient)。
本文不涉及数学推导,仅介绍如何高效实现。如对该算法该兴趣,推荐 Medium
上有3.8k点赞的一篇文章An introduction to Policy Gradients。
搭建神经网络
Policy Gradient 网络的输入也是状态(State),那输出呢?每个动作的概率。例如 [0.7, 0.3]
,这意味着有70%的几率会选择动作0,30%的几率选择动作1。相对于 Policy Gradient,DQN 的动作更确定,因为 DQN 每次总是选择Q值最大的动作,而Policy Gradient 按照概率选择,会产生更多的不确定性。
废话不多说,神经网络先搭起来吧~
1 | # policy_gradient.py |
我们的神经网络很简单,输入层为4,输出层为2,隐藏层为100。不过这次代码多了一个Dropout
,Dropout(0.1) 的含义是,随机忘记10%的权重。学习初期,一开始的数据质量不高,随着学习的进行,质量才逐步高了起来,一开始容易陷入局部最优和过拟合,使用 Dropout 可以有效避免。
如何选择动作呢?前文已经介绍,按照概率。
1 | # policy_gradient.py |
优化策略
接下来是最大的问题,如何优化策略呢?
1) 衰减的累加期望
我们先想象一下,假如你在玩坦克大战,你的每一步都会对后面的局势产生巨大的影响。比如,敌方攻打你的老巢,你是选择先消灭敌方呢,还是选择坐视不理?很可能一步就决定了结局。因此,需要从整个回合的角度看待这个问题。先引入一个概念 带衰减reward的累加期望
。
discount_reward[i] = reward[i] + gamma * discount_reward[i+1]
某一步的累加期望等于下一步的累加期望乘衰减系数gamma
,加上reward
。
手工算一算。
1 | 最后一步:1 |
假设某个回合只得了10分,那么这个回合的每一步的累加期望都不会高。假设得到了满分200分,那么回合中的大部分步骤的累加期望很会很高,越是前面的步骤,累加期望越高。
代码实现就很简单了,唯一的不同是最后加了中心化和标准化的处理。这样处理的目的是希望得到相同尺度的数据,避免因为数值相差过大而导致网络无法收敛。
1 | # policy_gradient.py |
2) 给loss加权重
一个动作的累加期望
很高,自然希望该动作出现的概率变大,这就是学习的目的。一般,我们通过构造**标签(y_true/label),来训练神经网络。就如在TensorFlow 2.0 (六) - 监督学习玩转 OpenAI gym game这篇文章中做的一样。当然,我们还可以通过改变损失函数(loss function)**达到目的。对于累加期望大的动作,可以放大loss
的值,而对于累加期望小的动作,那么就减小loss的值。这样呢?神经网络就能快速朝着累加期望大的方向优化了。最简单的方法,给loss
加一个权重。
所以我们的最终的损失函数就变成了:
loss = discount_reward * loss
这里的discount_reward
可以理解为策略梯度算法(Policy Gradient)中的梯度(Gradient)。如果对梯度不熟悉,可以看第一篇文章TensorFlow入门(一) - mnist手写数字识别(网络搭建)。
在TensorFlow 1.x的版本中,搭建一个自定义loss的网络很复杂,而使用TensorFlow 2.0,借助Keras
,我们可以写出非常简洁的代码。
1 | # policy_gradient.py |
设置参数sample_weight
,即可给loss设权重。
训练过程与结果
接下来,把 OpenAI gym 的代码融入进来吧。
1 | # policy_gradient.py |
运行一下试一试吧。
1 | $ python policy_gradient.py |
画一张图,感受下学习的过程,这一次稍微多了3行多项式拟合的代码,能够更好地展现整个分数变化的趋势。
1 | # policy_gradient.py |
。
测试
按照惯例,测试下效果。
1 | # test_policy_gradient.py |
1 | python test_policy_gradient.py |
如何优化
教程中,每个回合不管多少条训练数据,直接训练,而没有固定大小的batch
,不利于训练。有时间可以尝试,设置一个大小为2000的队列,存储历史的训练数据,每次固定取32/64条训练集,对比下两者的效果。
代码已上传至 Github - CartPole-v0-policy-gradient