Dueling DQN—一种简单有效提高DQN效果的方法

2019-07-24  本文已影响0人  CristianoC

1.前言

关于DQN我们还会讲最后一种升级办法,我们只需要稍微改动DQN中的神经网络的结构,就能大幅提升学习效果,加速收敛,这种新方法叫做Dueling DQN。用一句话概括Dueling DQN就是:它将每一个动作的Q拆分成了state的Value加上每个动作的Advantage。

2.算法

2.1Dueling 算法

我们看一下Dueling DQN的神经网络架构和普通DQN有什么不同:


image

我们可以很明显地看出,上面普通的DQN只有一个输出,就是每一个动作的Q值;而Dueling DQN则拆成了state的Value和每个动作的Advantage。

我们再来看一下公式:


image

它分成了这个state的值,加上每个动作在这个state上的advantage。因为有时候在某种state,无论做什么动作,对下一个state都没有多大的影响,比如论文当中的上面这个图:


image
这是开车的游戏,左边是state value,发红的部分证明了state value和前面的路线有关,右边是advantage,发红的部分说明了advantage很在乎旁边要靠近的车子,这时的动作会受advantage的影响。发红的地方左右了自己车子的移动原则。

2.2 更新方法

image
class DuelingDQN:
    def __init__(..., dueling=True, sess=None)
        ...
        self.dueling = dueling  # 会建立两个 DQN, 其中一个是 Dueling DQN
        ...
        if sess is None:    # 针对建立两个 DQN 的模式修改了 tf.Session() 的建立方式
            self.sess = tf.Session()
            self.sess.run(tf.global_variables_initializer())
        else:
            self.sess = sess
        ...

    def _build_net(self):
        def build_layers(s, c_names, n_l1, w_initializer, b_initializer):
            with tf.variable_scope('l1'):   # 第一层, 两种 DQN 都一样
                w1 = tf.get_variable('w1', [self.n_features, n_l1], initializer=w_initializer, collections=c_names)
                b1 = tf.get_variable('b1', [1, n_l1], initializer=b_initializer, collections=c_names)
                l1 = tf.nn.relu(tf.matmul(s, w1) + b1)

            if self.dueling:
                # Dueling DQN
                with tf.variable_scope('Value'):    # 专门分析 state 的 Value
                    w2 = tf.get_variable('w2', [n_l1, 1], initializer=w_initializer, collections=c_names)
                    b2 = tf.get_variable('b2', [1, 1], initializer=b_initializer, collections=c_names)
                    self.V = tf.matmul(l1, w2) + b2

                with tf.variable_scope('Advantage'):    # 专门分析每种动作的 Advantage
                    w2 = tf.get_variable('w2', [n_l1, self.n_actions], initializer=w_initializer, collections=c_names)
                    b2 = tf.get_variable('b2', [1, self.n_actions], initializer=b_initializer, collections=c_names)
                    self.A = tf.matmul(l1, w2) + b2

                with tf.variable_scope('Q'):    # 合并 V 和 A, 为了不让 A 直接学成了 Q, 我们减掉了 A 的均值
                    out = self.V + (self.A - tf.reduce_mean(self.A, axis=1, keep_dims=True))     # Q = V(s) + A(s,a)
            else:
                with tf.variable_scope('Q'):    # 普通的 DQN 第二层
                    w2 = tf.get_variable('w2', [n_l1, self.n_actions], initializer=w_initializer, collections=c_names)
                    b2 = tf.get_variable('b2', [1, self.n_actions], initializer=b_initializer, collections=c_names)
                    out = tf.matmul(l1, w2) + b2

            return out
        ...

3.对比结果

这次我们看看累计奖励reward,杆子立起来的时候奖励=0,其他时候都是负值,所以当累积奖励没有在降低的时候,说明杆子已经被成功立起来很久了。

image
我们可以发现当可用工作越高,学习难度就越大,不过Dueling DQN还是会比Natural DQN学习得更快,收敛效果更好。
完整代码:https://github.com/cristianoc20/RL_learning/tree/master/Dueling_DQN
参考:https://github.com/MorvanZhou
上一篇下一篇

猜你喜欢

热点阅读