DQN三大改进(一)-Double DQN

Double-DQN原文:https://arxiv.org/pdf/1509.06461v3.pdf 代码地址:https://github.com/princewen/tensorflow_practice/tree/master/Double-DQN-demo

1、背景

这篇文章我们会默认大家已经了解了DQN的相关知识,如果大家对于DQN还不是很了解,可以参考文章https://www.jianshu.com/p/10930c371cac。

我们简单回顾一下DQN的过程(这里是2015版的DQN):

DQN中有两个关键的技术,叫做经验回放和双网络结构。

DQN中的损失函数定义为:

其中,yi也被我们称为q-target值,而后面的Q(s,a)我们称为q-eval值,我们希望q-target和q-eval值越接近越好。

q-target如何计算呢?根据下面的公式:

上面的两个公式分别截取自两篇不同的文章,所以可能有些出入。我们之前说到过,我们有经验池存储的历史经验,经验池中每一条的结构是(s,a,r,s'),我们的q-target值根据该轮的奖励r以及将s'输入到target-net网络中得到的Q(s',a')的最大值决定。

我们进一步展开我们的q-target计算公式:

也就是说,我们根据状态s'选择动作a'的过程,以及估计Q(s',a')使用的是同一张Q值表,或者说使用的同一个网络参数,这可能导致选择过高的估计值,从而导致过于乐观的值估计。为了避免这种情况的出现,我们可以对选择和衡量进行解耦,从而就有了双Q学习,在Double DQN中,q-target的计算基于如下的公式:

我们根据一张Q表或者网络参数来选择我们的动作a',再用另一张Q值表活着网络参数来衡量Q(s',a')的值。

2、代码实现

本文的代码还是根据莫烦大神的代码,它的github地址为:https://github.com/MorvanZhou/Reinforcement-learning-with-tensorflow

这里我们想要实现的效果类似于寻宝。

其中,红色的方块代表寻宝人,黑色的方块代表陷阱,黄色的方块代表宝藏,我们的目标就是让寻宝人找到最终的宝藏。

这里,我们的状态可以用横纵坐标表示,而动作有上下左右四个动作。使用tkinter来做这样一个动画效果。宝藏的奖励是1,陷阱的奖励是-1,而其他时候的奖励都为0。

接下来,我们重点看一下我们Double-DQN相关的代码。

定义输入

# ------------------------input---------------------------self.s = tf.placeholder(tf.float32, [None, self.n_features], name='s')self.q_target = tf.placeholder(tf.float32, [None, self.n_actions], name='Q-target')self.s_ = tf.placeholder(tf.float32,[None,self.n_features],name='s_')

定义双网络结构

这里我们的双网络结构都简单的采用简单的全链接神经网络,包含一个隐藏层。这里我们得到的输出是一个向量,表示该状态才取每个动作可以获得的Q值:

def build_layers(s,c_name,n_l1,w_initializer,b_initializer):
    with tf.variable_scope('l1'):
        w1 = tf.get_variable(name='w1',shape=[self.n_features,n_l1],initializer=w_initializer,collections=c_name)
        b1 = tf.get_variable(name='b1',shape=[1,n_l1],initializer=b_initializer,collections=c_name)
        l1 = tf.nn.relu(tf.matmul(s,w1)+b1)
    with tf.variable_scope('l2'):
        w2 = tf.get_variable(name='w2',shape=[n_l1,self.n_actions],initializer=w_initializer,collections=c_name)
        b2 = tf.get_variable(name='b2',shape=[1,self.n_actions],initializer=b_initializer,collections=c_name)
        out = tf.matmul(l1,w2) + b2    return out

接下来,我们定义两个网络:

# ------------------ build evaluate_net ------------------with tf.variable_scope('eval_net'):
    c_names = ['eval_net_params',tf.GraphKeys.GLOBAL_VARIABLES]
    n_l1 = 20
    w_initializer = tf.random_normal_initializer(0,0.3)
    b_initializer =tf.constant_initializer(0.1)    self.q_eval = build_layers(self.s,c_names,n_l1,w_initializer,b_initializer)# ------------------ build target_net ------------------with tf.variable_scope('target_net'):
    c_names = ['target_net_params', tf.GraphKeys.GLOBAL_VARIABLES]    self.q_next = build_layers(self.s_, c_names, n_l1, w_initializer, b_initializer)

定义损失和优化器 接下来,我们定义我们的损失,和DQN一样,我们使用的是平方损失:

with tf.variable_scope('loss'):    self.loss = tf.reduce_mean(tf.squared_difference(self.q_target,self.q_eval))

with tf.variable_scope('train'):    self.train_op = tf.train.RMSPropOptimizer(self.lr).minimize(self.loss)

定义我们的经验池 我们使用一个函数定义我们的经验池,经验池每一行的长度为 状态feature * 2 + 2。

def store_transition(self,s,a,r,s_):    if not hasattr(self, 'memory_counter'):        self.memory_counter = 0
    transition = np.hstack((s, [a, r], s_))
    index = self.memory_counter % self.memory_size    self.memory[index, :] = transition    self.memory_counter += 1

选择action 我们仍然使用的是e-greedy的选择动作策略,即以e的概率选择随机动作,以1-e的概率通过贪心算法选择能得到最多奖励的动作a。

def choose_action(self,observation):
    observation = observation[np.newaxis,:]
    actions_value = self.sess.run(self.q_eval,feed_dict={self.s:observation})
    action = np.argmax(actions_value)    if np.random.random() > self.epsilon:
        action = np.random.randint(0,self.n_actions)    return action

选择数据batch 我们从经验池中选择我们训练要使用的数据。

if self.memory_counter > self.memory_size:
    sample_index = np.random.choice(self.memory_size, size=self.batch_size)else:
    sample_index = np.random.choice(self.memory_counter, size=self.batch_size)

batch_memory = self.memory[sample_index,:]

更新target-net 这里,每个一定的步数,我们就更新target-net中的参数:

t_params = tf.get_collection('target_net_params')
e_params = tf.get_collection('eval_net_params')self.replace_target_op = [tf.assign(t, e) for t, e in zip(t_params, e_params)]if self.learn_step_counter % self.replace_target_iter == 0:    self.sess.run(self.replace_target_op)    print('\ntarget_params_replaced\n')

更新网络参数 根据Double DQN的做法,我们需要用两个网络的来计算我们的q-target值,同时通过最小化损失来更新网络参数。这里的做法是,根据eval-net的值来选择动作,然后根据target-net的值来计算Q值。

q_next,q_eval4next = self.sess.run([self.q_next, self.q_eval],
                                           feed_dict={self.s_: batch_memory[:, -self.n_features:],                                                      self.s: batch_memory[:, -self.n_features:]})

q_next是根据经验池中下一时刻状态输入到target-net计算得到的q值,而q_eval4next是根据经验池中下一时刻状态s'输入到eval-net计算得到的q值,这个q值主要用来选择动作。

下面的动作用来得到我们batch中的实际动作和奖励

batch_index = np.arange(self.batch_size, dtype=np.int32)
eval_act_index = batch_memory[:, self.n_features].astype(int)
reward = batch_memory[:, self.n_features + 1]

接下来,我们就要来选择动作并计算该动作的q值了,如果是double dqn的话,我们是根据刚刚计算的q_eval4next来选择动作,然后根据q_next来得到q值的。而原始的dqn直接通过最大的q_next来得到q值:

if self.double_q:
    max_act4next = np.argmax(q_eval4next, axis=1)        # the action that brings the highest value is evaluated by q_eval
    selected_q_next = q_next[batch_index, max_act4next]  # Double DQN, select q_next depending on above actionselse:
    selected_q_next = np.max(q_next, axis=1)    # the natural DQN

那么我们的q-target值就可以计算得到了:

q_target = q_eval.copy()
q_target[batch_index, eval_act_index] = reward + self.gamma * selected_q_next

有了q-target值,我们就可以结合eval-net计算的q-eval值来更新网络参数了:

_, self.cost = self.sess.run([self.train_op, self.loss],
                             feed_dict={self.s: batch_memory[:, :self.n_features],                                        self.q_target: q_target})self.cost_his.append(self.cost)self.epsilon = self.epsilon + self.epsilon_increment if self.epsilon < self.epsilon_max else self.epsilon_maxself.learn_step_counter += 1

3、参考文献

1、Double-DQN原文:https://arxiv.org/pdf/1509.06461v3.pdf 2、解析 DeepMind 采用双 Q 学习 (Double Q-Learning) 深度强化学习技术:https://www.jianshu.com/p/193ca0106aa5

原文发布于微信公众号 - 小小挖掘机(wAIsjwj)

原文发表时间:2018-03-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器之心

专栏 | 阿里IJCAI 2017 Workshop论文:使用深度强化学习方法求解一类新型三维装箱问题

机器之心专栏 阿里菜鸟物流人工智能部 据机器之心了解,阿里巴巴有 11 篇论文入选如今正在墨尔本进行的 IJCAI 2017 大会,其中 6 篇来自阿里巴巴-浙...

1.1K60
来自专栏IT综合技术分享

大数据算法汇总

转载36大数据(36dsj.com):36大数据»大数据等最核心的关键技术:32个算法

71910
来自专栏星回的实验室

特征选择的评估方法

数据挖掘中,特征选择的过程就是计算特征与样本观测结果的相关性。卡方检验和互信息是用得较多的计算方法。

20810
来自专栏大数据挖掘DT机器学习

用TensorFlow实现文本分析模型,做个聊天机器人

不错的学习聊天机器人的资源,不知道小伙伴们有没有去学习呢。 自己动手做聊天机器人教程 http://www.shareditor.com/blogshow...

49050
来自专栏PPV课数据科学社区

【学习】R语言与机器学习(分类算法)logistic回归

由于我们在前面已经讨论过了神经网络的分类问题,如今再从最优化的角度来讨论logistic回归就显得有些不合适了。Logistic回归问题的最优化问题可以表述为:...

29340
来自专栏专知

【干货】理解特征工程Part 1——连续数值数据(附代码)

93410
来自专栏大数据挖掘DT机器学习

用keras对国产剧评论文本的情感进行预测

RNN即循环神经网络,其主要用途是处理和预测序列数据。在CNN中,神经网络层间采用全连接的方式连接,但层内节点之间却无连接。RNN为了处理序列数据,层内节点的输...

46850
来自专栏深度学习入门与实践

机器学习基础与实践(一)----数据清洗

本博客所有内容以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,并且是非商业用途,谢谢!   想写这个系列很久了,最近刚好项目结束了闲下来有点时间,...

58460
来自专栏CDA数据分析师

机器学习基础与实践(一)——数据清洗

想写这个系列很久了,最近刚好项目结束了闲下来有点时间,于是决定把之前学过的东西做个总结。之前看过一些机器学习方面的书,每本书都各有侧重点,机器学习实战和集体智慧...

28870
来自专栏CreateAMind

Unsupervised Learning of Latent Physical Properties Using

https://www.groundai.com/project/unsupervised-learning-of-latent-physical-proper...

8030

扫码关注云+社区

领取腾讯云代金券