在强化学习 (九)Deep Q-Learning 进阶之 Nature DQN 中, 我们讨论了 Nature DQN 的算法流程, 它通过使用两个相同的神经网络, 以解决数据样本和网络训练之前的相关性. 但是还是有其他值得优化的点, 文本就关注于 Nature DQN 的一个改进版本: Double DQN 算法 (以下简称 DDQN).
本章内容主要参考了 ICML 2016 的 deep RL tutorial 和 DDQN 的论文 < Deep Reinforcement Learning with Double Q-learning>.
1. DQN 的目标 Q 值计算问题
在 DDQN 之前, 基本上所有的目标 Q 值都是通过贪婪法直接得到的, 无论是 Q-Learning, DQN(NIPS 2013) 还是 Nature DQN, 都是如此. 比如对于 Nature DQN, 虽然用了两个 Q 网络并使用目标 Q 网络计算 Q 值, 其第 j 个样本的目标 Q 值的计算还是贪婪法得到的, 计算入下式:$$y_j= \begin{cases} R_j& {is\_end_j\; is \;true}\\ R_j + \gamma\max_{a'}Q'(\phi(S'_j),A'_j,w') & {is\_end_j \;is\; false} \end{cases}$$
使用 max 虽然可以快速让 Q 值向可能的优化目标靠拢, 但是很容易过犹不及, 导致过度估计 (Over Estimation), 所谓过度估计就是最终我们得到的算法模型有很大的偏差 (bias). 为了解决这个问题, DDQN 通过解耦目标 Q 值动作的选择和目标 Q 值的计算这两步, 来达到消除过度估计的问题.
2. DDQN 的算法建模
DDQN 和 Nature DQN 一样, 也有一样的两个 Q 网络结构. 在 Nature DQN 的基础上, 通过解耦目标 Q 值动作的选择和目标 Q 值的计算这两步, 来消除过度估计的问题.
在上一节里, Nature DQN 对于非终止状态, 其目标 Q 值的计算式子是:$$y_j= R_j + \gamma\max_{a'}Q'(\phi(S'_j),A'_j,w')$$
在 DDQN 这里, 不再是直接在目标 Q 网络里面找各个动作中最大 Q 值, 而是先在当前 Q 网络中先找出最大 Q 值对应的动作, 即 $$a^{max}(S'_j, w) = \arg\max_{a'}Q(\phi(S'_j),a,w)$$
然后利用这个选择出来的动作 $a^{max}(S'_j, w) $ 在目标网络里面去计算目标 Q 值. 即:$$y_j = R_j + \gamma Q'(\phi(S'_j),a^{max}(S'_j, w),w')$$
综合起来写就是:$$y_j = R_j + \gamma Q'(\phi(S'_j),\arg\max_{a'}Q(\phi(S'_j),a,w),w')$$
除了目标 Q 值的计算方式以外, DDQN 算法和 Nature DQN 的算法流程完全相同.
3. DDQN 算法流程
这里我们总结下 DDQN 的算法流程, 和 Nature DQN 的区别仅仅在步骤 2.f 中目标 Q 值的计算.
算法输入: 迭代轮数 $T$, 状态特征维度 $n$, 动作集 $A$, 步长 $\alpha$, 衰减因子 $\gamma$, 探索率 $\epsilon$, 当前 Q 网络 $Q$, 目标 Q 网络 $Q'$, 批量梯度下降的样本数 $m$, 目标 Q 网络参数更新频率 $C$.
输出: 所有的状态和动作对应的价值 $Q$
1. 随机初始化所有的状态和动作对应的价值 $Q$. 随机初始化当前 Q 网络的所有参数 $w$, 初始化目标 Q 网络 $Q'$ 的参数 $w' = w$. 清空经验回放的集合 $D$.
2. for i from 1 to T, 进行迭代.
a) 初始化 S 为当前状态序列的第一个状态, 拿到其特征向量 $\phi(S)$
b) 在 Q 网络中使用 $\phi(S)$ 作为输入, 得到 Q 网络的所有动作对应的 Q 值输出. 用 $\epsilon-$ 贪婪法在当前 Q 值输出中选择对应的动作 $A$
c) 在状态 $S$ 执行当前动作 $A$, 得到新状态 $S'$ 对应的特征向量 $\phi(S') 和奖励 $R$, 是否终止状态 is_end
d) 将 $\{\phi(S),A,R,\phi(S'),is\_end\}$ 这个五元组存入经验回放集合 $D$
e) $S=S'$
f) 从经验回放集合 $D$ 中采样 $m$ 个样本 $\{\phi(S_j),A_j,R_j,\phi(S'_j),is\_end_j\}, j=1,2.,,,m$, 计算当前目标 Q 值 $y_j$:$$y_j= \begin{cases} R_j& {is\_end_j\; is \;true}\\ R_j + \gamma Q'(\phi(S'_j),\arg\max_{a'}Q(\phi(S'_j),a,w),w')& {is\_end_j\; is \;false} \end{cases}$$
g) 使用均方差损失函数 $\frac{1}{m}\sum\limits_{j=1}^m(y_j-Q(\phi(S_j),A_j,w))^2$, 通过神经网络的梯度反向传播来更新 Q 网络的所有参数 $w$
h) 如果 T%C=1, 则更新目标 Q 网络参数 $w'=w$
i) 如果 $S'$ 是终止状态, 当前轮迭代完毕, 否则转到步骤 b)
注意, 上述第二步的 f 步和 g 步的 Q 值计算也都需要通过 Q 网络计算得到. 另外, 实际应用中, 为了算法较好的收敛, 探索率 $\epsilon$ 需要随着迭代的进行而变小.
4. DDQN 算法实例
下面我们用一个具体的例子来演示 DQN 的应用. 仍然使用了 OpenAI Gym 中的 CartPole-v0 游戏来作为我们算法应用. CartPole-v0 游戏的介绍参见这里 https://github.com/openai/gym/wiki/CartPole-v0 . 它比较简单, 基本要求就是控制下面的 cart 移动使连接在上面的 pole 保持垂直不倒. 这个任务只有两个离散动作, 要么向左用力, 要么向右用力. 而 state 状态就是这个 cart 的位置和速度, pole 的角度和角速度, 4 维的特征. 坚持到 200 分的奖励则为过关.
完整的代码参见我的 GitHub:
这里我们重点关注 DDQN 和上一节的 Nature DQN 的代码的不同之处. 代码只有一个地方不一样, 就是计算目标 Q 值的时候, 如下:
- # Step 2: calculate y
- y_batch = []
- current_Q_batch = self.Q_value.eval(feed_dict={self.state_input: next_state_batch})
- max_action_next = np.argmax(current_Q_batch, axis=1)
- target_Q_batch = self.target_Q_value.eval(feed_dict={self.state_input: next_state_batch})
- for i in range(0,BATCH_SIZE):
- done = minibatch[i][4]
- if done:
- y_batch.append(reward_batch[i])
- else :
- target_Q_value = target_Q_batch[i, max_action_next[i]]
- y_batch.append(reward_batch[i] + GAMMA * target_Q_value)
而之前的 Nature DQN 这里的目标 Q 值计算是如下这样的:
- # Step 2: calculate y
- y_batch = []
- Q_value_batch = self.target_Q_value.eval(feed_dict={self.state_input:next_state_batch})
- for i in range(0,BATCH_SIZE):
- done = minibatch[i][4]
- if done:
- y_batch.append(reward_batch[i])
- else :
- y_batch.append(reward_batch[i] + GAMMA * np.max(Q_value_batch[i]))
除了上面这部分的区别, 两个算法的代码完全相同.
5. DDQN 小结
DDQN 算法出来以后, 取得了比较好的效果, 因此得到了比较广泛的应用. 不过我们的 DQN 仍然有其他可以优化的点, 如上一篇最后讲到的: 随机采样的方法好吗? 按道理经验回放里不同样本的重要性是不一样的, TD 误差大的样本重要程度应该高. 针对这个问题, 我们在下一节的 Prioritised Replay DQN 中讨论.
来源: https://www.cnblogs.com/pinard/p/9778063.html