Q函数近似
前面所讲的基本算法均只针对离散状态的马尔科夫决策过程:状态集$S$通过集合形式表示。离散状态MDP下的动作值函数一般保存为表格:行对应状态,列对应动作,这种表示方法无法应用在连续状态MDP的情况下,因此在连续状态空间下,需要通过函数近似的方式来表示动作值函数$Q(S,A)$。
DQN
算法
上一小节说到可以通过函数来近似动作值函数,但是我们不知道最优的动作值函数的形式,因此近似函数形式的选择比较麻烦,特别在状态是高维度时,寻找合适的近似函数更加困难。因为深度神经网络理论上能够良好的近似任意形式的函数,而且自动抽取高维特征,因此考虑直接通过神经网络来近似最优的动作值函数,我们称这个网络为Q-network,假设它的参数为$w$,我们希望[1]:
有两种Q-network结构能够用来近似最优动作值函数[1],如下图所示:

第一种网络的输入包括状态和动作,输出对应的值函数估计;第二种网络仅仅输入状态,输出所有可能动作对应的值函数估计值。显然应该选择第二种结构,理由有两个:我们希望Q-网络能够抽取高维状态中的特征,没必要加入动作;另外第一种结构中,为了得到状态$s$下所有动作的值函数估计值,需要针对每个动作进行前向计算,而在第二种结构下,只需要一次前向计算便可以得到所有的动作值估计值,因此更加高效。
用Q-network近似最优值函数$Q^{\star}(s, a)$时,需要知道$Q^{\star}(s, a)$的形式,但是我们不知道它的具体形式(否则也没有必要用Q-网络近似表示),但我们知道它必须满足Belleman最优方程:
根据原始Q-learning算法的思路,我们用$ r + \gamma \; max_{a'} Q^{\star}(s', a') $来近似最优动作值函数,然后通过与环境的在线(on-line)交互进行增量式学习。将$ r + \gamma \; max_{a'} Q^{\star}(s', a')$ 中的$Q^{\star}(s', a')$替换为Q-网络$Q^{\star}(s', a', w)$,通过平方误差来衡量当前Q-网络$Q^{\star}(s, a)$的误差:
当我们尝试用梯度类算法来优化上述损失函数时,会面临如下两个问题,导致优化平方差损失函数时很难收敛:
第一,神经网络理论上要求输入的状态样本是独立同分布的,而在增量式学习过程中,环境的状态转移概率具有马尔可夫性,因此得到的状态样本不满足独立性。
第二个问题称为non-stationary target[1],在原始论文[2]中作者对该问题的描述为:
“ … small updates to Q may significantly change the policy and therefore change the data distribution, and the correlations between the action-values (Q) and the target values $\; r + \gamma \; max_{a’} Q^{\star}(s’, a’, w)$ …”
离散MDP下的$Q(s, a)$是一个表格,当我们修改$Q(s, a)$时不会影响$Q(s’, a’)$的值,而在这里,Q-network用于近似整体状态空间下的动作值函数,当我们修改$Q(s, a, w)$的网络参数时,影响的是整体的值函数,从而影响$Q(s’, a’)$,也就是说我们在调整网络参数尝试靠近目标时,(网络参数的调整)同时也改变了我们希望靠近的目标$\; r + \gamma \; max_{a’} Q^{\star}(s’, a’, w)$,这与有监督学习的学习过程完全不同。
论文[2]针对第一个问题的解决方法称为:Experience Replay。为了打破状态之间的相关性,在内存中保存一定量的Transition $(s, a, r, s’)$,然后从里面随机采集一个样本batch用于优化损失函数,如下图所示:

针对第二个问题,问题根源在于参数更新发生在同一个Q-网络中,因此作者[2]引入另一个网络Target network,这个网络的作用是周期性的记录Q-network的参数,以保证Q-network上的参数更新时Target network上对应的$Q^{\star}(s’, a’, w)$不变,从而减小这两个网络的关联性。
于是优化的目标函数变为:
其中$w$表示发生参数更新的Q-network的权重参数,$w^-$表示Target-network 上次保存的Q-network的权重参数。算法整体的流程如下:

简单DQN实现
步骤
从上面的算法流程来看,要实现DQN的步骤如下所示:
- 搭建两个网络结构完全相同的网络:目标网络Target_Network和计算网络Evaluate_Network;
- Evaluate_Network根据当前的状态S选择动作A;
- 动作A作用于环境后得到Transition(S, A, R, S’),将该Transition存入Transition池中;
- 从Transition池中随机选择Batch_size个Transition样本,记为Batch_Sample,将Batch_Sample送入网络Target_Network和Evaluate_Network中,分别得到两组输出结果q_next和q_eval;
- 根据结果q_next计算Evaluate_Network需要逼近的目标值:
- 将结果q_target送入网络Evaluate_Network进行参数更新;
- 重复步骤2) — 6),每隔一定迭代次数将Evaluate_Network的网络参数复制给Target_Network.
实现
按照DQN算法步骤,整体算法可以用下面的图来说明,建议配合我绘制的这张图来分析对应部分的代码:
搭建网络
Evaluate_Network和Target_Network的结构需要保持完全一致,不同的地方在于前者需要定义损失函数和相应的优化器,而后者只是作为前者网络参数的暂存器,不需要更改网络参数。因为DQN需要周期性的将Evaluate_Network的各层网络参数复制给Target_Network,因此需要在两个网络中建立两个独立的collection,便于从中提取网络参数Tensor。
选择动作
这里采用的行为策略依旧是$\epsilon$-贪婪策略,因此与前一篇博文中Q-学习算法选择动作的思路一致,不同的地方在于,这里的Q函数是Evaluate_Network,因此需要在Session中进行前向传播,得到当前状态下的输出动作。需要注意的是需要将状态observation的shape转换成[1, self.n_features]的维度,方便输入到网络中(参见搭建网络时对self.S的说明)。
Transition Experience存储
存储Transition时,通过一个array存储,每一行对应一条(S,A,R,S_)记录。列的存储顺序上,该array的前后 self.n_features 列存储状态S和S’,第self.n_features+1列的位置存储动作A,紧挨着后面一列存储回报值R。行的存储顺序上,在给定最大行self.memory_size的array中从前往后循环存储,如果array存满,则重新从头开始覆盖掉旧的Transition进行存储。
网络学习
这里包含了步骤4)—6),建议结合上面的示意图进行理解,特别是q_target部分的计算。
网络参数复制
通过两个网络不同的collection分别得到两个网络各层参数的Tensor,再通过tf.assign()逐层的将Evaluate_Network的参数复制给Target_Network的对应层。