在上篇文章中【PaddlePaddle】 强化学习(CartPole-v1),我们介绍了如何使用PaddlePaddle在CartPole-v1游戏上实现强化学习,但是对实现思想讲解的不是很多,也不是很清晰。于是,这篇文章主要记录实现上述强化学习的细节以及具体实现思路。
目录
Q-Learning
在传统的图搜索中,我们一般用一个Q-table矩阵来记录每一步学到的经验(我们一般称“经验”为“最大收益”,也就是走这一步的可以获得的最大收益是多少),每走一步就更新一次Q-table,也就是将这一步学到的知识加入Q-table中。在Q-table迭代到一定程度时,Q-table趋于稳定状态,随后,我们使用Q-table来进行决策,因为我们可以通过Q-table了解到每一步怎么走可以获得最大收益。
更新的方法是Bellman Equation,这个算法在上篇文章中已经有所提及,这里就不多讲了,Q-Learning的具体理解可以参考这篇文章:理解Q-learning,一篇文章就够了,这篇文章中有例子,能更好理解一些。
DQN
DQN全称是Deep Q-Learning Network,这个思想是将Q-Learning和深度学习相结合,提倡不使用Q-table记录最大收益Q值,而是使用神经网络预测执行某个action(action=动作)对应的收益值Q。完整的算法步骤如下:
实现思路
第一步:创造记忆库
我们先让系统随机玩几次游戏,通过定义的epsilon-greedy搜索策略,根据训练的进度,选择自动执行动作或者是使用模型预测的动作作为动作输入。通过搜索策略,让训练前期多一些随机动作,训练后期多一些神经网络预测的动作。
然后把在当前状态下执行这个动作的信息(比如:当前状态、动作、执行后的下一个状态、动作的奖励、游戏是否结束等信息)记录到记忆库中,以供后续训练使用。训练的数据是从记忆库中随机提取的。我们在python中使用deque,当数据存满的时候下一数据就会覆盖掉记忆库中的第一个数据。
在填充记忆库的过程中,如果当前状态下执行当前动作导致游戏结束,那我们就将done_data(用来标记游戏是否结束)置为1,reward置为-10,这样的奖励相当于一种惩罚措施。如果当前状态下执行当前动作没有导致游戏结束,那么done_data置为0,reward根据游戏规则自动计算(肯定是一个大于0的奖励)。
当记忆库填充的差不多了,就可以开始训练了,在训练的过程中,记忆库还在被一直填充,只不过在循环中多一个反向传播优化参数的过程。
第二步:搭建神经网络的思路
我们将网络分为两个,一个是反向传播更新参数,我们称之为state_model,一个只进行前向传播我们称之为target_model。但是根据状态值预测Q值的神经网络,他们二者使用的是同一个。
根据状态预测Q值的神经网络DQNetWork
我们定义了一个简单的神经网络,输入的是状态,输出的是在这个状态下,所有动作对应的Q值。对于CartPole-v1游戏来说,有两个动作(向左或者向右移动),所以最终生成的Q值的维度是2,对应于当前状态下分别执行两个动作得到的收益Q1和Q2。代码片段如下:
def DQNetWork(ipt, variable_field):
fc1 = fluid.layers.fc(input=ipt, size=24, act='relu', param_attr=ParamAttr(name='{}_fc1'.format(variable_field)),
bias_attr=ParamAttr(name='{}_fc1_b'.format(variable_field)))
fc2 = fluid.layers.fc(input=fc1, size=24, act='relu', param_attr=ParamAttr(name='{}_fc2'.format(variable_field)),
bias_attr=ParamAttr(name='{}_fc2_b'.format(variable_field)))
# size=2,即输出维度为2,对应两个动作的Q值(收益值)
fc3 = fluid.layers.fc(input=fc2, size=2, param_attr=ParamAttr(name='{}_fc3'.format(variable_field)),
bias_attr=ParamAttr(name='{}_fc3_b'.format(variable_field)))
return fc3
这样我们就可以得到在一个状态下,分别执行两个action,对应的收益值Q1和Q2。
target_model的作用
target_model只进行前向传播。
再讲state_model之前,我们先来看看target_model,因为state_model的标签是由target_model生成的,具体的方式如下:
1、向DQNetWork中输入执行动作后的下一个状态,获取两个收益值Q1和Q2,这个收益值是next_state的收益。
2、通过Bellman Equation公式计算出当前状态可达的最大收益。具体公式如下:
q_target = reward_data + gamma * best_v * (1 - done_data)
其中reward_data,是执行当前动作的奖励,best_v是下一状态分别执行两个操作获得的收益值Q1和Q2中较大的那个收益值,gamma是best_v(下一状态对应的最大收益值)所占的比重,done_data是这个动作下游戏是否结束。通过Bellman Equation公式的定义我们可知,q_target是执行当前动作获得的reward加上后续状态的收益gamma×best_v,其实就是 当前状态执行当前行动的最大收益(q_target)=显式奖励(reward)+ 潜在最大收益(gamma×best_v),当前动作的执行不但参考当前动作能获得的奖励,也参考了下一状态的潜在最大收益,使得模型有了前瞻性。q_target即为在当前状态下,执行当前动作可以获得的理论上的最大收益。
如果输入数据的done_data为1,那么q_target就为-10,因为对于done_data=1的数据,我们设置了惩罚reward=-10,可以看到在这个情况下,q_target是一个很小的值,也就是理论上的收益很小,属于惩罚收益。
如果输入数据的done_data为0,那么q_target就会结合显式奖励reward和潜在收益gamma*best_v,计算出在当前状态下,理论上能获得的最大收益。
state_model的作用
state_model用来反向传播优化参数。state_model计算出当前状态下,输入当前动作能得出的预测收益值。我们向DQNetWork输入的是当前状态,预测在当前状态下,分别执行两个操作的收益值Q1和Q2。然后将[Q1,Q2]和输入action的one_hot矩阵进行逐元素相乘,获得在特定输入action下的收益值q_eval。
state_model预测出的结果q_eval要向target_model预测出的q_target靠近。也就是说state_model计算出的q_eval是向理论上的最大收益或者惩罚收益靠近。
DQNetWork如何指导某个状态下应该执行的action
我们向DQNetWork输入一个状态,分别执行两个操作,得到两个收益值,我们取两个收益值中较大的那个收益对应的action作为当前状态应该执行的动作。因为在这个动作下,获得的收益最高。
第三步:训练神经网络
前面有提到过,q_eval要向target_model预测出的q_target靠近,所以我们用一个简单的均方误差计算q_eval和q_target之间的差距,并将其作为损失函数进行反向传播,从而优化参数。
在这期间,target_model的DQNetWork网络参数定期根据state_model的DQNetWork参数进行复制,修剪。因为target_model是不进行反向传播来优化参数的。