DQN三大改进(三)-Dueling Network

简介:

1、Dueling Network

什么是Dueling Deep Q Network呢?看下面的图片

72a83de9faca1b6edb3372850fdc779b8a8c4bd2

上面是我们传统的DQN,下面是我们的Dueling DQN。在原始的DQN中,神经网络直接输出的是每种动作的 Q值, 而 Dueling DQN 每个动作的 Q值 是有下面的公式确定的:

f431e77506119790c8e10ee9466098e0eb43d54c

它分成了这个 state 的值, 加上每个动作在这个 state 上的 advantage。我们通过下面这张图来解释一下:

cd6ca76adc136478a53708a37856d9ccc013ebef

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

但是,利用上面的式子计算Q值会出现一个unidentifiable问题:给定一个Q,是无法得到唯一的V和A的。比如,V和A分别加上和减去一个值能够得到同样的Q,但反过来显然无法由Q得到唯一的V和A。

解决方法
强制令所选择贪婪动作的优势函数为0:

90094ede72febc6e972c2863bf1ead9e8f0e1b75

则我们能得到唯一的值函数:

2a66f0c0382a95bb038bb5105435357a8e067693

解决方法的改进
使用优势函数的平均值代替上述的最优值

b41f60c4e2a991eb3b249d41eb06402dbe147af2

采用这种方法,虽然使得值函数V和优势函数A不再完美的表示值函数和优势函数(在语义上的表示),但是这种操作提高了稳定性。而且,并没有改变值函数V和优势函数A的本质表示。

2、代码实现

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

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

703205bd1f50af5a048e8dd00d8b8cf12a1dce1c

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

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

接下来,我们重点看一下我们Dueling-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_')

定义网络结构
根据Dueling DQN的网络结构,我们首先定义一个隐藏层,针对隐藏层的输出,我们将此输出分别作为两个隐藏层的输入,分别输出state的Value,和每个action的Advantage,最后, 根据Q = V+A得到每个action的Q值:

 

def build_layers(s, c_names, n_l1, w_initializer, b_initializer):    with tf.variable_scope('l1'):        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:        with tf.variable_scope('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'):            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'):            out = self.V + self.A - tf.reduce_mean(self.A,axis=1,keep_dims=True)    else:        with tf.variable_scope('Q'):            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

接下来,我们定义我们的eval-net和target-net

 

# -----------------------------eval net ---------------------with tf.variable_scope('eval_net'):    c_names, n_l1, w_initializer, b_initializer = \        ['eval_net_params', tf.GraphKeys.GLOBAL_VARIABLES], 20, \        tf.random_normal_initializer(0., 0.3), tf.constant_initializer(0.1)  # config of layers    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')

更新网络参数
我们使用DQN的做法来更新网络参数:

 

q_next = self.sess.run(self.q_next, feed_dict={self.s_: batch_memory[:, -self.n_features:]})  # next observationq_eval = self.sess.run(self.q_eval, {self.s: batch_memory[:, :self.n_features]}) q_target = q_eval.copy() 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_target[batch_index, eval_act_index] = reward + self.gamma * np.max(q_next, axis=1) _, 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 +=

原文发布时间为:2018-10-9

本文作者:文文

本文来自云栖社区合作伙伴“Python爱好者社区”,了解相关信息可以关注“Python爱好者社区”。

相关文章
|
Web App开发 前端开发 JavaScript
介绍Chrome DevTools的使用方法,助您更好地掌握这款工具
【6月更文挑战第14天】Chrome DevTools是Chrome内置的网页调试利器,提供Elements(编辑HTML/CSS)、Console(JavaScript调试)、Sources(查看/调试JS/CSS文件)、Network(分析网络请求)和Performance(性能瓶颈分析)等面板,助力开发者优化网页性能和用户体验。通过掌握其使用,可提升开发效率。
444 2
|
机器学习/深度学习 算法 数据挖掘
【机器学习】算法术语、决策函数、概率模型、神经网络的详细讲解(图文解释)
【机器学习】算法术语、决策函数、概率模型、神经网络的详细讲解(图文解释)
890 1
|
消息中间件 运维 Java
【Log日志】logback.xml动态配置属性值(包括接入的第三方配置)
1如何动态配置Logback的存放路径 我们在开发过程中,会使用到logback.xml 配置来管理日志文件; 比如
3399 0
|
前端开发 Ubuntu Linux
【.NET6+Avalonia】开发支持跨平台的仿WPF应用程序以及基于ubuntu系统的演示
随着跨平台越来越流行,.net core支持跨平台至今也有好几年的光景了。但是目前基于.net的跨平台,大多数还是在使用B/S架构的跨平台上;至于C/S架构,大部分人可能会选择QT进行开发,或者很早之前还有一款Mono可以支持.NET开发者进行开发跨平台应用。
1460 0
【.NET6+Avalonia】开发支持跨平台的仿WPF应用程序以及基于ubuntu系统的演示
|
10月前
|
机器学习/深度学习 存储 人工智能
《深度剖析:Q-learning与策略梯度方法的本质区别》
在强化学习领域,Q-learning和策略梯度方法是两种重要的算法。Q-learning通过迭代更新状态-动作值(Q值),评估动作价值,适用于离散动作空间;策略梯度方法则直接优化参数化策略,适合连续动作空间。前者收敛稳定但速度较慢,后者收敛快但稳定性差。两者各有优劣,适用于不同场景。
383 27
|
机器学习/深度学习 存储 算法
强化学习实战:基于 PyTorch 的环境搭建与算法实现
【8月更文第29天】强化学习是机器学习的一个重要分支,它让智能体通过与环境交互来学习策略,以最大化长期奖励。本文将介绍如何使用PyTorch实现两种经典的强化学习算法——Deep Q-Network (DQN) 和 Actor-Critic Algorithm with Asynchronous Advantage (A3C)。我们将从环境搭建开始,逐步实现算法的核心部分,并给出完整的代码示例。
1507 1
|
监控
模拟点击问题之从风险防控的角度看,设备风险如何解决
模拟点击问题之从风险防控的角度看,设备风险如何解决
238 0
|
机器学习/深度学习 资源调度 自然语言处理
长短时记忆网络(LSTM)完整实战:从理论到PyTorch实战演示
长短时记忆网络(LSTM)完整实战:从理论到PyTorch实战演示
18143 0
|
机器学习/深度学习 数据采集
区间预测 | MATLAB实现基于QRCNN-LSTM卷积长短期记忆神经网络多变量时间序列区间预测
区间预测 | MATLAB实现基于QRCNN-LSTM卷积长短期记忆神经网络多变量时间序列区间预测
|
SQL 开发框架 JavaScript
GitHub开源几分钟被下架!神作《Spring Boot实战项目》竟昙花一现
阿嘴又来给大家分享好书了:韩帅(十三)老师的 《Spring Boot实战:从0开始动手搭建企业级项目》,网上没找见开源版本!小编会在文末附电子版免费下载方式。
下一篇
oss云网关配置