dqn-tensorflow笔记

打算学习下DQN,在github上找了一份代码,代码质量挺高。下面是阅读笔记,期望通过对该代码的学习加深对DQN的理解。

代码结构如下:

├── README.md
├── config.py
├── dqn
│   ├── __init__.py
│   ├── agent.py
│   ├── base.py
│   ├── environment.py
│   ├── history.py
│   ├── ops.py
│   ├── replay_memory.py
│   ├── utils.py
└── main.py

程序的入口为main.py

强化学习中涉及了两个实体,一个是智能体,一个是环境,分别对应agent.py和environment.py。

智能体通过与环境交互来实现累积回报最大化,交互的动作即action,获得的奖励即reward。同时,智能体也从一个状态(state)到达了另外一个state。

根据config.py中的配置,history_length=4,即一个样本中包含四帧图像。 样本中包含如下信息:

当前时刻的四帧图像、最后一帧图像对应的action、reward、是否终止态,四帧图像对应的下一帧组成的四帧图像

图像格式:[batch_size,history_length,screen_width,screen_height]

根据取值,实际格式为[batch_size,4,84,84]

在build_dqn中为网络结构。

对于q网络,包含3层卷积层和2层全连接层。

目标网络也是包含3层卷积层和2层全连接层

q网络: 三层卷积的配置如下:

  1. 第一层:32个feature_map,每个feature_map大小[8,8],步长[4,4]
  2. 第二层:64个feature_map,每个feature_map大小[4,4],步长[2,2]
  3. 第三层:64个feature_map,每个feature_map大小[3,3],步长[1,1]

当不使用GPU 时,cnn_format为NHWC,dqn网络过程如下:

  1. 输入:[N,84,84,4]

  2. 第一层CNN stride:[1,4,4,1],kernel:[8,8,4,32] 输出: 根据 (x-kernel)/stride+1,得到输出维度:[N,20,20,32]
  3. 第二层CNN stride:[1,2,2,1],kernel:[4,4,32,64] 输出:[N,9,9,64]
  4. 第三层CNN stride:[1,1,1,1],kernel:[3,3,64,64] 输出:[N,7,7,64]
  5. 两层MLP reshape成[N,6477]维度后接两层MLP
  6. 决策 最终输出[N,action_size],表示每个样本上每个action的未来累积期望的分布, 选择最大的动作记录到q_action中。

target 网络结构都一样

计算出每个样本的目标q值 target_q

  self.target_q_idx = tf.placeholder('int32', [None, None], 'outputs_idx')
  self.target_q_with_idx = tf.gather_nd(self.target_q, self.target_q_idx)

这个是啥?

看代码在double dqn中用到

将预测q网络的参数复制到目标q网络

    with tf.variable_scope('pred_to_target'):
      self.t_w_input = {}
      self.t_w_assign_op = {}
      #将q网络的参数复制到目标q网络
      for name in self.w.keys():
        self.t_w_input[name] = tf.placeholder('float32', self.t_w[name].get_shape().as_list(), name=name)
        self.t_w_assign_op[name] = self.t_w[name].assign(self.t_w_input[name])

loss

def clipped_error(x):
  # Huber loss
  try:
    return tf.select(tf.abs(x) < 1.0, 0.5 * tf.square(x), tf.abs(x) - 0.5)
  except:
    return tf.where(tf.abs(x) < 1.0, 0.5 * tf.square(x), tf.abs(x) - 0.5)

训练:

先使用同一个screen 组成四帧 放到history中

在每一个step中:

1.基于当前history(只存储了最新的四个screen),选择action 基于epsilon greedy选择,如果随机数小于epsilon,则随机选择action,否则,使用预测q网络预测当前状态(四帧图像)下 q值最大的action,并采用该action。 epsilon的值需要注意一下: 在测试阶段 ep值是固定的,即随机的概率是一定的 在训练阶段,如果当前step 小于learn_start,那么就全部进行随机选择 如果 step 大于learn_start,那么此时探索的概率就线性下降。

2.采用该action得到reward、screen和terminal(表示该轮游戏是否终止) 采用该action时重复了action_repeat次,不清楚是为啥。reward是重复这么多次的累积的reward。 screen 为重复数次后达到的状态。

3.将screen 放到history中,将screen、action、reward、terminal放到memory中 表示在采取该action后,即时回报为reward,到达的状态为screen,是否终止存储在terminal中。 4.如果当前step正好到了训练频次(step>learn_start且step % train_frequency == 0)那么从memory中采样一个batch进行学习 5.如果到了更新目标网络的频次(step%target_q_update_step==target_q_update_step-1),那么用预测q网络的参数更新target q网络。 6.如果一局游戏结束,则重新开始下一轮游戏,同时,重置该轮(episode)游戏的reward为0;否则更新该轮游戏reward。

从memory中采样batch:

先从[self.history_length, self.count - 1](闭区间)中随机生成待采样的样本的index,

index需要满足如下条件:

1.index >=current & index -history_length < current

2.[index-history_length, index-1](闭区间)之间不能包含终止state

第一个条件是说选择的样本不能包含current,因为memory中的样本都是顺序添加的,这样的话选出来的样本包含的帧就不是连续的了 第二个条件比较好理解,是指样本中不能包含终止状态的帧。但是下标为index的screen(即后文的poststates) 可以是终止状态的。

如果index 没有命中上面两个条件,那么 以index-1 为最后一个元素,继续向前取history_length-1 个元素,共history_length个元素 作为prestates。 以index 为最后一个元素,继续向前取history_length-1个元素,共history_length个元素,作为poststates。

同时,将index加入到列表中,该列表中记录了历史上所有选中的screend的下标。

最后,将这些下标对应的action、reward、terminals,连同prestate和poststate都返回,作为一个batch。

这些信息是说当状态是prestate时,采取动作为action,立即回报为reward,是否到达终止状态为terminals,转移到的state为poststate。即poststate的前一个state为poststate。

采样完batch后:

如果是double dqn:

1.使用预测q网络预估poststate的未来累积回报最大的action,并将最大的action的下标作为该样本采取的目标action。

2.然后使用目标q网络预估poststate的未来累积回报的分布即target_q;

最后,使用1预估的最优action从2中的target_q中选择对应的q值,记为q_t_plus_1_with_pred_action

label如下:

      target_q_t = (1. - terminal) * self.discount * q_t_plus_1_with_pred_action + reward

如果只是一般的dqn: 则使用目标q网络预测poststates的目标q值 target_q,维度为(batch_size,action_size) 这个target_q 值是在采取动作action后观察到状态poststate的q值分布。 选择每个样本在poststate下target_q中最大的q值,表示该状态下的最优action。然后计算该状态下最优的未来累积回报。

    target_q_t = (1. - terminal) * self.discount * max_q_t_plus_1 + reward

这个值就是label。

同时,网络以prestate为输入,得到该状态下的q值分布,然后乘以action,表示在该状态下采取action 得到的未来累积回报(q值),这个就是预估值了,记做q_acted。

self.delta = self.target_q_t - q_acted

二者之差就是预估偏差。然后就是根据这个计算loss,并根据loss更新q网络。

可以看到,target网络是用来算目标值的,预测网络是用来算预估值的。

double dqn的思想:

宁雨 /
Published under (CC) BY-NC-SA in categories MachineLearning  tagged with
comments powered by Disqus