文章目录

  • 1 什么是 DQN
  • 2 DQN 算法更新 (Tensorflow)
  • 3 DQN 神经网络 (Tensorflow)
  • 4 DQN 思维决策 (Tensorflow)

1 什么是 DQN

我们使用表格来存储每一个状态 state, 和在这个 state 每个行为 action 所拥有的 Q 值. 而当今问题是在太复杂, 状态可以多到比天上的星星还多(比如下围棋). 如果全用表格来存储它们, 恐怕我们的计算机有再大的内存都不够, 而且每次在这么大的表格中搜索对应的状态也是一件很耗时的事. 不过, 在机器学习中, 有一种方法对这种事情很在行, 那就是神经网络. 我们可以将状态和动作当成神经网络的输入, 然后经过神经网络分析后得到动作的 Q 值, 这样我们就没必要在表格中记录 Q 值, 而是直接使用神经网络生成 Q 值. 还有一种形式的是这样, 我们也能只输入状态值, 输出所有的动作值, 然后按照 Q learning 的原则, 直接选择拥有最大值的动作当做下一步要做的动作. 我们可以想象, 神经网络接受外部的信息, 相当于眼睛鼻子耳朵收集信息, 然后通过大脑加工输出每种动作的值, 最后通过强化学习的方式选择动作.

接下来我们基于第二种神经网络来分析, 我们知道, 神经网络是要被训练才能预测出准确的值. 那在强化学习中, 神经网络是如何被训练的呢? 首先, 我们需要 a1, a2 正确的Q值, 这个 Q 值我们就用之前在 Q learning 中的 Q 现实来代替. 同样我们还需要一个 Q 估计 来实现神经网络的更新. 所以神经网络的的参数就是老的 NN 参数 加学习率 alpha 乘以 Q 现实 和 Q 估计 的差距. 我们整理一下.

我们通过 NN 预测出Q(s2, a1) 和 Q(s2,a2) 的值, 这就是 Q 估计. 然后我们选取 Q 估计中最大值的动作来换取环境中的奖励 reward. 而 Q 现实中也包含从神经网络分析出来的两个 Q 估计值, 不过这个 Q 估计是针对于下一步在 s’ 的估计. 最后再通过刚刚所说的算法更新神经网络中的参数. 但是这并不是 DQN 会玩电动的根本原因. 还有两大因素支撑着 DQN 使得它变得无比强大. 这两大因素就是 Experience replay 和 Fixed Q-targets.

简单来说, DQN 有一个记忆库用于学习之前的经历. 在之前的简介影片中提到过, Q learning 是一种 off-policy 离线学习法, 它能学习当前经历着的, 也能学习过去经历过的, 甚至是学习别人的经历. 所以每次 DQN 更新的时候, 我们都可以随机抽取一些之前的经历进行学习. 随机抽取这种做法打乱了经历之间的相关性, 也使得神经网络更新更有效率. Fixed Q-targets 也是一种打乱相关性的机理, 如果使用 fixed Q-targets, 我们就会在 DQN 中使用到两个结构相同但参数不同的神经网络, 预测 Q 估计 的神经网络具备最新的参数, 而预测 Q 现实 的神经网络使用的参数则是很久以前的. 有了这两种提升手段, DQN 才能在一些游戏中超越人类.

2 DQN 算法更新 (Tensorflow)


要点
Deep Q Network 的简称叫 DQN, 是将 Q learning 的优势 和 Neural networks 结合了. 如果我们使用 tabular Q learning, 对于每一个 state, action 我们都需要存放在一张 q_table 的表中. 如果像显示生活中, 情况可就比那个迷宫的状况复杂多了, 我们有千千万万个 state, 如果将这千万个 state 的值都放在表中, 受限于我们计算机硬件, 这样从表中获取数据, 更新数据是没有效率的. 这就是 DQN 产生的原因了. 我们可以使用神经网络来 估算 这个 state 的值, 这样就不需要一张表了.

这次的教程我们还是基于熟悉的 迷宫 环境, 重点在实现 DQN 算法, 之后我们再拿着做好的 DQN 算法去跑其他更有意思的环境.

算法
DQN 算法更新 (Tensorflow)

整个算法乍看起来很复杂, 不过我们拆分一下, 就变简单了. 也就是个 Q learning 主框架上加了些装饰.

这些装饰包括:

记忆库 (用于重复学习)
神经网络计算 Q 值
暂时冻结 q_target 参数 (切断相关性)

算法的代码形式
接下来我们对应上面的算法, 来实现主循环. 首先 import 所需模块.

from maze_env import Maze
from RL_brain import DeepQNetwork
下面的代码, 就是 DQN 于环境交互最重要的部分.

def run_maze():
step = 0 # 用来控制什么时候学习
for episode in range(300):
# 初始化环境
observation = env.reset()

    while True:# 刷新环境env.render()# DQN 根据观测值选择行为action = RL.choose_action(observation)# 环境根据行为给出下一个 state, reward, 是否终止observation_, reward, done = env.step(action)# DQN 存储记忆RL.store_transition(observation, action, reward, observation_)# 控制学习起始时间和频率 (先累积一些记忆再开始学习)if (step > 200) and (step % 5 == 0):RL.learn()# 将下一个 state_ 变为 下次循环的 stateobservation = observation_# 如果终止, 就跳出循环if done:breakstep += 1   # 总步数# end of game
print('game over')
env.destroy()

if name == “main”:
env = Maze()
RL = DeepQNetwork(env.n_actions, env.n_features,
learning_rate=0.01,
reward_decay=0.9,
e_greedy=0.9,
replace_target_iter=200, # 每 200 步替换一次 target_net 的参数
memory_size=2000, # 记忆上限
# output_graph=True # 是否输出 tensorboard 文件
)
env.after(100, run_maze)
env.mainloop()
RL.plot_cost() # 观看神经网络的误差曲线

3 DQN 神经网络 (Tensorflow)

两个神经网络
为了使用 Tensorflow 来实现 DQN, 比较推荐的方式是搭建两个神经网络, target_net 用于预测 q_target 值, 他不会及时更新参数. eval_net 用于预测 q_eval, 这个神经网络拥有最新的神经网络参数. 不过这两个神经网络结构是完全一样的, 只是里面的参数不一样.
神经网络结构
因为 DQN 的结构相比之前所讲的内容都不一样, 所以我们不使用继承来实现这次的功能. 这次我们创建一个 DeepQNetwork 的 class, 以及他神经网络部分的功能. 下次再说强化学习的更新部分.

class DeepQNetwork:
# 建立神经网络
def _build_net(self):

创建两个网络
两个神经网络是为了固定住一个神经网络 (target_net) 的参数, target_net 是 eval_net 的一个历史版本, 拥有 eval_net 很久之前的一组参数, 而且这组参数被固定一段时间, 然后再被 eval_net 的新参数所替换. 而 eval_net 是不断在被提升的, 所以是一个可以被训练的网络 trainable=True. 而 target_net 的 trainable=False.

class DeepQNetwork:
def _build_net(self):
# -------------- 创建 eval 神经网络, 及时提升参数 --------------
self.s = tf.placeholder(tf.float32, [None, self.n_features], name=‘s’) # 用来接收 observation
self.q_target = tf.placeholder(tf.float32, [None, self.n_actions], name=‘Q_target’) # 用来接收 q_target 的值, 这个之后会通过计算得到
with tf.variable_scope(‘eval_net’):
# c_names(collections_names) 是在更新 target_net 参数时会用到
c_names, n_l1, w_initializer, b_initializer =
[‘eval_net_params’, tf.GraphKeys.GLOBAL_VARIABLES], 10,
tf.random_normal_initializer(0., 0.3), tf.constant_initializer(0.1) # config of layers

        # eval_net 的第一层. collections 是在更新 target_net 参数时会用到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(self.s, w1) + b1)# eval_net 的第二层. collections 是在更新 target_net 参数时会用到with tf.variable_scope('l2'):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.q_eval = tf.matmul(l1, w2) + b2with 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)# ---------------- 创建 target 神经网络, 提供 target Q ---------------------self.s_ = tf.placeholder(tf.float32, [None, self.n_features], name='s_')    # 接收下个 observationwith tf.variable_scope('target_net'):# c_names(collections_names) 是在更新 target_net 参数时会用到c_names = ['target_net_params', tf.GraphKeys.GLOBAL_VARIABLES]# target_net 的第一层. collections 是在更新 target_net 参数时会用到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(self.s_, w1) + b1)# target_net 的第二层. collections 是在更新 target_net 参数时会用到with tf.variable_scope('l2'):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.q_next = tf.matmul(l1, w2) + b2

4 DQN 思维决策 (Tensorflow)

义完上次的神经网络部分以后, 这次我们来定义其他部分. 包括:

class DeepQNetwork:
# 上次的内容
def _build_net(self):

# 这次的内容:
# 初始值
def __init__(self):# 存储记忆
def store_transition(self, s, a, r, s_):# 选行为
def choose_action(self, observation):# 学习
def learn(self):# 看看学习效果 (可选)
def plot_cost(self):

初始值
class DeepQNetwork:
def init(
self,
n_actions,
n_features,
learning_rate=0.01,
reward_decay=0.9,
e_greedy=0.9,
replace_target_iter=300,
memory_size=500,
batch_size=32,
e_greedy_increment=None,
output_graph=False,
):
self.n_actions = n_actions
self.n_features = n_features
self.lr = learning_rate
self.gamma = reward_decay
self.epsilon_max = e_greedy # epsilon 的最大值
self.replace_target_iter = replace_target_iter # 更换 target_net 的步数
self.memory_size = memory_size # 记忆上限
self.batch_size = batch_size # 每次更新时从 memory 里面取多少记忆出来
self.epsilon_increment = e_greedy_increment # epsilon 的增量
self.epsilon = 0 if e_greedy_increment is not None else self.epsilon_max # 是否开启探索模式, 并逐步减少探索次数

    # 记录学习次数 (用于判断是否更换 target_net 参数)self.learn_step_counter = 0# 初始化全 0 记忆 [s, a, r, s_]self.memory = np.zeros((self.memory_size, n_features*2+2)) # 和视频中不同, 因为 pandas 运算比较慢, 这里改为直接用 numpy# 创建 [target_net, evaluate_net]self._build_net()# 替换 target net 的参数t_params = tf.get_collection('target_net_params')  # 提取 target_net 的参数e_params = tf.get_collection('eval_net_params')   # 提取  eval_net 的参数self.replace_target_op = [tf.assign(t, e) for t, e in zip(t_params, e_params)] # 更新 target_net 参数self.sess = tf.Session()# 输出 tensorboard 文件if output_graph:# $ tensorboard --logdir=logstf.summary.FileWriter("logs/", self.sess.graph)self.sess.run(tf.global_variables_initializer())self.cost_his = []  # 记录所有 cost 变化, 用于最后 plot 出来观看

存储记忆
DQN 的精髓部分之一: 记录下所有经历过的步, 这些步可以进行反复的学习, 所以是一种 off-policy 方法, 你甚至可以自己玩, 然后记录下自己玩的经历, 让这个 DQN 学习你是如何通关的.

class DeepQNetwork:
def init(self):

def store_transition(self, s, a, r, s_):
if not hasattr(self, ‘memory_counter’):
self.memory_counter = 0

    # 记录一条 [s, a, r, s_] 记录transition = np.hstack((s, [a, r], s_))# 总 memory 大小是固定的, 如果超出总大小, 旧 memory 就被新 memory 替换index = self.memory_counter % self.memory_sizeself.memory[index, :] = transition # 替换过程self.memory_counter += 1

选行为
和之前的 QLearningTable, SarsaTable 等一样, 都需要一个选行为的功能.

class DeepQNetwork:
def init(self):

def store_transition(self, s, a, r, s_):

def choose_action(self, observation):
# 统一 observation 的 shape (1, size_of_observation)
observation = observation[np.newaxis, :]

    if np.random.uniform() < self.epsilon:# 让 eval_net 神经网络生成所有 action 的值, 并选择值最大的 actionactions_value = self.sess.run(self.q_eval, feed_dict={self.s: observation})action = np.argmax(actions_value)else:action = np.random.randint(0, self.n_actions)   # 随机选择return action

学习
最重要的一步来了, 就是在 DeepQNetwork 中, 是如何学习, 更新参数的. 这里涉及了 target_net 和 eval_net 的交互使用.

class DeepQNetwork:
def init(self):

def store_transition(self, s, a, r, s_):

def choose_action(self, observation):

def _replace_target_params(self):

def learn(self):
# 检查是否替换 target_net 参数
if self.learn_step_counter % self.replace_target_iter == 0:
self.sess.run(self.replace_target_op)
print(’\ntarget_params_replaced\n’)

    # 从 memory 中随机抽取 batch_size 这么多记忆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, :]# 获取 q_next (target_net 产生了 q) 和 q_eval(eval_net 产生的 q)q_next, q_eval = 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, q_eval 包含所有 action 的值,# 而我们需要的只是已经选择好的 action 的值, 其他的并不需要.# 所以我们将其他的 action 值全变成 0, 将用到的 action 误差值 反向传递回去, 作为更新凭据.# 这是我们最终要达到的样子, 比如 q_target - q_eval = [1, 0, 0] - [-1, 0, 0] = [2, 0, 0]# q_eval = [-1, 0, 0] 表示这一个记忆中有我选用过 action 0, 而 action 0 带来的 Q(s, a0) = -1, 所以其他的 Q(s, a1) = Q(s, a2) = 0.# q_target = [1, 0, 0] 表示这个记忆中的 r+gamma*maxQ(s_) = 1, 而且不管在 s_ 上我们取了哪个 action,# 我们都需要对应上 q_eval 中的 action 位置, 所以就将 1 放在了 action 0 的位置.# 下面也是为了达到上面说的目的, 不过为了更方面让程序运算, 达到目的的过程有点不同.# 是将 q_eval 全部赋值给 q_target, 这时 q_target-q_eval 全为 0,# 不过 我们再根据 batch_memory 当中的 action 这个 column 来给 q_target 中的对应的 memory-action 位置来修改赋值.# 使新的赋值为 reward + gamma * maxQ(s_), 这样 q_target-q_eval 就可以变成我们所需的样子.# 具体在下面还有一个举例说明.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)"""假如在这个 batch 中, 我们有2个提取的记忆, 根据每个记忆可以生产3个 action 的值:q_eval =[[1, 2, 3],[4, 5, 6]]q_target = q_eval =[[1, 2, 3],[4, 5, 6]]然后根据 memory 当中的具体 action 位置来修改 q_target 对应 action 上的值:比如在:记忆 0 的 q_target 计算值是 -1, 而且我用了 action 0;记忆 1 的 q_target 计算值是 -2, 而且我用了 action 2:q_target =[[-1, 2, 3],[4, 5, -2]]所以 (q_target - q_eval) 就变成了:[[(-1)-(1), 0, 0],[0, 0, (-2)-(6)]]最后我们将这个 (q_target - q_eval) 当成误差, 反向传递会神经网络.所有为 0 的 action 值是当时没有选择的 action, 之前有选择的 action 才有不为0的值.我们只反向传递之前选择的 action 的值,"""# 训练 eval_net_, 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) # 记录 cost 误差# 逐渐增加 epsilon, 降低行为的随机性self.epsilon = self.epsilon + self.epsilon_increment if self.epsilon < self.epsilon_max else self.epsilon_maxself.learn_step_counter += 1

看学习效果
为了看看学习效果, 我们在最后输出学习过程中的 cost 变化曲线.

class DeepQNetwork:
def init(self):

def store_transition(self, s, a, r, s_):

def choose_action(self, observation):

def _replace_target_params(self):

def learn(self):

def plot_cost(self):
import matplotlib.pyplot as plt
plt.plot(np.arange(len(self.cost_his)), self.cost_his)
plt.ylabel(‘Cost’)
plt.xlabel(‘training steps’)
plt.show()
DQN 思维决策 (Tensorflow)

可以看出曲线并不是平滑下降的, 这是因为 DQN 中的 input 数据是一步步改变的, 而且会根据学习情况, 获取到不同的数据. 所以这并不像一般的监督学习, DQN 的 cost 曲线就有所不同了.

【深度学习入门到精通系列】Deep Q Network相关推荐

  1. 【深度学习入门到精通系列】阿里云人工智能平台的使用方法

    文章目录 1 概述 2 获取密钥方法 3 Python SDK 4 demo 1 概述 接口易用 标准化接口封装,大大降低研发人力投入 稳定可靠 服务可靠性99.99%:全球多机房部署,服务全球化:单 ...

  2. 【深度学习入门到精通系列】 深入浅出强化学习 Sarsa

    文章目录 1 什么是 Sarsa 2 Sarsa 算法更新 3 Sarsa 思维决策 4 什么是 Sarsa(lambda) 5 Sarsa-lambda 1 什么是 Sarsa 同样, 我们会经历正 ...

  3. 【深度学习入门到精通系列】对抗样本和对抗网络

    文章目录 1 概述 2 对抗样本 3 对抗网络 1 概述 所谓对抗 样本是指将实际样本略加扰动而构造出的合成样本,对该样本,分类器非常容易将其类别判错,这意味着光滑性假设(相似的样本应该以很高的概率被 ...

  4. 【深度学习入门到精通系列】进化策略 (Evolution Strategy)

    文章目录 1 什么是进化策略 (Evolution Strategy) 2 简单实现 2.1 和遗传算法的异同 2.2 代码 3 (1+1)-ES 4 Natural Evolution Strate ...

  5. 【深度学习入门到精通系列】神经进化 (NeuroEvolution)

    文章目录 1 什么是神经网络进化 (Neuro-Evolution) 2 神经进化 3 NEAT 监督学习 4 NEAT 强化学习 5 Evolution Strategy 强化学习 1 什么是神经网 ...

  6. 【深度学习入门到精通系列】开始恢复更新通知~!

    因为放假在家基本不学习哈哈,但是快要开学了,准备开始更新了,我会尽量保持一周3到4篇高质量文章的频率,欢迎大家订阅呀-!! 附上我的微博: ID:LiXiangDL 有问题欢迎打扰!

  7. 【深度学习入门到精通系列】Python批量实现图像镜像翻转

    ''' Python批量实现图像镜像翻转 函数:DataAugment() 函数功能:扩大数据量 输入参数:dir_path----图片库路径 '''import cv2 import os impo ...

  8. 【深度学习入门到精通系列】神经网络中动量的概念

    其中动量系数一般取(0,1),直观上理解就是要是当前梯度方向与前一步的梯度方向一样,那么就增加这一步的权值更新,要是不一样就减少更新.

  9. 【深度学习入门到精通系列】nnU-Net论文解析

    文章目录 1 摘要 2 介绍 3 方法 3.1 预处理 3.2 训练过程 3.3 推理(Inference) 3.4 后处理 4 总结 nnUnet虽然不是新的论文,但是这个框架效果很好.它并没有提出 ...

最新文章

  1. PHPcms框架的Webshell
  2. Node/01/案例流程
  3. JavaScript快速入门(三)——JavaScript语句
  4. C++_程序内存模型_内存四区_代码区_全局区_每种区域都存放什么样的变量---C++语言工作笔记028
  5. python的networkx 算法_python图算法库Networkx笔记 - Node and Centrality
  6. 64位电脑 装32位oracle,64位Windows可以安装32位的Oracle吗
  7. html基础之 input:type
  8. 中国人工智能学会通讯——人工智能在各医学亚专科的发展现状及趋势 1.6 结束语...
  9. JS中height、clientHeight、scrollHeight、offsetHeight区别
  10. 电气工程及自动化 (独立本科) 自考
  11. 计算机经常断开网络,当笔记本电脑经常自动断开网络连接时如何解决问题
  12. Java.MapReduce处理全国各省市疫情数据,上传Hadoop平台处理后的数据
  13. 这么写参数校验(validator)就不会被劝退了~
  14. cpuz测试分数天梯图_PC电脑桌面CPU天梯图2020 单路CPU性能排名
  15. 锂电池充电的同时也能放电吗?
  16. mysql的partition_MySQL分区(Partition)
  17. c1科目三灯光全语言播报,驾照科目三灯光模拟考试语音提示
  18. Maven打包跳过单元测试
  19. pod install报错 CDN: trunk Repo update failed...couldnt connect to server
  20. sa结构组网方式_关于 SA/NSA 两种组网模式,你了解多少?-通信/网络-与非网

热门文章

  1. mxnet arg_params, aux_params
  2. ffmpeg openh264
  3. pyqt setStyleSheet用法
  4. C++使用thread类多线程编程
  5. python ‘float‘object is not iterable
  6. Traincascade Error: Bad argument (Can not get new positive sample. The most possible reason is insuf
  7. 十六、Redis三种特殊类型之三Bitmap
  8. linux信号处理编程实验报告,Linux编程之信号处理
  9. 氮化镓充电器哪家好_双十二推荐入手的热门氮化镓充电器
  10. python 中文查找_使用python和regex查找字符串中的所有中文文本