前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Categorical DQN-一种建模价值分布的深度强化学习方法!

Categorical DQN-一种建模价值分布的深度强化学习方法!

作者头像
石晓文
发布2019-01-02 17:34:22
1.7K0
发布2019-01-02 17:34:22
举报
文章被收录于专栏:小小挖掘机小小挖掘机

之前介绍的DQN及其各种变体,网络输出的都是状态-动作价值Q的期望预估值。而本文将介绍的Categorical DQN,它建模的是状态-动作价值Q的分布。这样的估计方法使得估计结果更加细致可信。

本文的论文名称为《A Distributional Perspective on Reinforcement Learning》,地址为:https://arxiv.org/abs/1707.06887。

不过论文里数学公式非常多,如果想要快速了解这种方法的原理,建议大家阅读《强化学习精要:核心算法与Tensorflow实现》一书。

1、Categorical DQN

1.1 为什么要输出价值分布?

之前介绍的DQN及其各种变体,网络输出的都是状态-动作价值Q的期望预估值。这个期望值其实忽略很多信息。比如同一状态下的两个动作,能够获得的价值期望是相同的,比如都是20,第一个动作在90%的情况下价值是10,在10%的情况下是110,另一个动作在50%的情况下是15,在50%的情况下是25。那么虽然期望一样,但如果我们想要减小风险,我们应该选择后一种动作。而只有期望值的话,我们是无法看到动作背后所蕴含的风险的。

所以从理论上来说,从分布视角(distributional perspective)来建模我们的深度强化学习模型,可以获得更多有用的信息,从而得到更好、更稳定的结果。

1.2 Categorical DQN原理

我们首先需要考虑的一个问题是,选择什么样的分布呢?一种很自然的想法是一个正态分布,我们需要估计的是动作状态价值的期望和方差,但是使用正态分布有很多限制,这就将状态价值限制为了中间概率大,两头概率小的一种分布形式,如果是两头概率大,中间概率小呢?同时,在训练时,我们计算两个分布的差距,选择正态分布从计算的层面也是非常困难的。因此,我们选择的分布至少需要满足两个条件:

  1. 可以表示各种各样的分布形式,不受太多的限制;
  2. 便于损失函数的计算和模型参数的更新。

基于以上两点,我们选择用直方图来表示一个分布。同时我们假设价值的最终值落在[Vmin,Vmax]之间。我们要在这段中均匀找N个价值采样点。要找N个价值采样点,两个点之间的间距计算为△z = (Vmax - Vmin)/(N-1),从而采样点的集合为{zi = Vmin + i △z,i=0,1,...,N-1}。

所以,我们的模型要输出一个N个值的向量,每一个值代表一个价值采样点出现的概率。而输入是当前的状态以及选择的动作。

接下来的关键是,如何进行更新?既然是分布,我们自然地想到使用交叉熵损失函数来刻画两个分布的差距。而根据强化学习的思想,我么会有一个价值的估计分布,以及一个价值的实际分布。估计分布的价值采样点是z,这没问题,而实际分布的价值采样点呢?z' = r + gamma * z。举个简单的例子:

可以看到,预估的价值分布和实际的价值分布,由于它们的采样点变的不一样了,我们不能直接比较两个分布的差距,因此我们需要把实际的价值分布的采样点,变换成跟预估的价值分布的采样点一样,即将[0.8,1.7,2,6,3.5,4.4,5.3] 投影为[0,1,2,3,4,5],当然,相应的概率也会发生变化。为了更方便的解释,我们称原有的价值采样点为z,而经过r+gamma*z得到的价值采样点为z'。

为了进行投影,我们首先要对z'的两头进行裁剪,也就是把小于0的变为0,大于5的变为5,此时概率不变,所以经过第一步,价值采样点变为z'=[0.8,1.7,2,6,3.5,4.4,5]。

接下来,我们就要进行采样点的投影了。N个价值采样点共有N-1个间隔,我们首先需要判断z'中每个采样点属于z中的第几个间隔,然后把概率按照距离分配给该间隔两头的价值采样点上。举例来说,z'中第一个价值采样点0.8在z的第一个间隔,其两头的价值采样点分别是0和1。根据距离,其对应概率的20%(0.2 *0.2 = 0.04)应该分配到0这个采样点上,80% (0.2 * 0.8 = 0.16)应该分配到1这个采样点上。这里你可能没有绕过弯来,0.8距离1较近,分配的概率应该越多。对z'所有采样点进行相同的操作,就可以把对应的概率投影到原有采样点z上。过程的示意图如下:

其中,1这个价值采样点的概率计算如下:

z'中有两个采样点的概率要分配到1这个采样点上来,分别是0.8和1.7。原有的价值采样点的间隔是1,所以0.8距离1的上一个价值采样点0的是0.8个间隔,距离1是0.2个间隔,所以0.8对应概率的80%应该分配到1上面,同理,1.7对应概率的30% 要分配到1上,所以投影后1对应的概率是0.2 * 0.8 + 0.3 * 0.3 = 0.25。

在进行裁剪和投影之后,实际的价值分布和预估的价值分布的价值采样点都统一了,我们就可以计算交叉熵损失,并更新模型的参数了。

2、Categorical DQN的Tensorflow实现

本文代码的实现地址为:https://github.com/princewen/tensorflow_practice/tree/master/RL/Basic-DisRL-Demo

这里我们玩的还是atrai游戏,只介绍一下模型实现的最关键的地方。

首先看模型的输入,我们这里不用batch的形式了,一次只输入一个状态动作进行更新,m_input是经过投影后实际的价值分布:

代码语言:javascript
复制
target_state_shape = [1]
target_state_shape.extend(self.state_shape)
self.state_input = tf.placeholder(tf.float32,target_state_shape)
self.action_input = tf.placeholder(tf.int32,[1,1])
self.m_input = tf.placeholder(tf.float32,[self.atoms])

随后是我们的价值采样点:

代码语言:javascript
复制
self.delta_z = (self.v_max - self.v_min) / (self.atoms - 1)
self.z = [self.v_min + i * self.delta_z for i in range(self.atoms)]

接下来是构建我们的dqn网络结构,一个是eval-net,一个是target-net,网络的输入是当前的state以及采取的动作action,只不过action是在中间过程中拼接上去的,而不是最开始就输入进去的:

代码语言:javascript
复制
def build_layers(self, state, action, c_names, units_1, units_2, w_i, b_i, reg=None):
    with tf.variable_scope('conv1'):
        conv1 = conv(state, [5, 5, 3, 6], [6], [1, 2, 2, 1], w_i, b_i)
    with tf.variable_scope('conv2'):
        conv2 = conv(conv1, [3, 3, 6, 12], [12], [1, 2, 2, 1], w_i, b_i)
    with tf.variable_scope('flatten'):
        flatten = tf.contrib.layers.flatten(conv2)

    with tf.variable_scope('dense1'):
        dense1 = dense(flatten, units_1, [units_1], w_i, b_i)
    with tf.variable_scope('dense2'):
        dense2 = dense(dense1, units_2, [units_2], w_i, b_i)
    with tf.variable_scope('concat'):
        concatenated = tf.concat([dense2, tf.cast(action, tf.float32)], 1)
    with tf.variable_scope('dense3'):
        dense3 = dense(concatenated, self.atoms, [self.atoms], w_i, b_i) # 返回
    return tf.nn.softmax(dense3)

def build_cate_dqn_net(self):
    with tf.variable_scope('target_net'):
        c_names = ['target_net_arams',tf.GraphKeys.GLOBAL_VARIABLES]
        w_i = tf.random_uniform_initializer(-0.1,0.1)
        b_i = tf.constant_initializer(0.1)
        self.z_target = self.build_layers(self.state_input,self.action_input,c_names,24,24,w_i,b_i)

    with tf.variable_scope('eval_net'):
        c_names = ['eval_net_params',tf.GraphKeys.GLOBAL_VARIABLES]
        w_i = tf.random_uniform_initializer(-0.1,0.1)
        b_i = tf.constant_initializer(0.1)
        self.z_eval = self.build_layers(self.state_input,self.action_input,c_names,24,24,w_i,b_i)

可以看到,我们这里使用的是两层卷积和三层全连接操作,动作只在最后一层全连接时拼接上去。最后的输出经过softmax变为每个价值采样点的概率。

因此我们可以根据分布求出q值:

代码语言:javascript
复制
self.q_eval = tf.reduce_sum(self.z_eval * self.z)
self.q_target = tf.reduce_sum(self.z_target * self.z)

构建好了两个网络,我们怎么进行训练呢?我们的经验池中还是存放了(state,action,reward,next_state),我们首先根据把next_state放入到target-net中,遍历每个可行的动作,找到q值最大的动作,作为next_action:

代码语言:javascript
复制
list_q_ = [self.sess.run(self.q_target,feed_dict={self.state_input:[s_],self.action_input:[[a]]}) for a in range(self.action_dim)]
a_ = tf.argmax(list_q_).eval()

接下来,我们使用target-net计算(next_state,next_action)在原始价值采样点下的概率分布:

代码语言:javascript
复制
p = self.sess.run(self.z_target,feed_dict = {self.state_input:[s_],self.action_input:[[a_]]})[0]

随后就是进行投影操作了,过程我们刚才已经介绍过了,这里不在赘述:

代码语言:javascript
复制
m = np.zeros(self.atoms)
for j in range(self.atoms):
    Tz = min(self.v_max,max(self.v_min,r+gamma * self.z[j]))
    bj = (Tz - self.v_min) / self.delta_z # 分在第几个块里
    l,u = math.floor(bj),math.ceil(bj) # 上下界
    pj = p[j]
    m[int(l)] += pj * (u - bj)
    m[int(u)] += pj * (bj - l)

这样,我们就得到了当前状态动作的实际价值分布。然后我们可以将当前的state和action输入到eval-net中,并通过交叉熵损失来对模型参数进行更新:

代码语言:javascript
复制
self.sess.run(self.optimizer,feed_dict={self.state_input:[s] , self.action_input:[action], self.m_input: m })

其中,优化器的定义如下:

代码语言:javascript
复制
self.cross_entropy_loss = -tf.reduce_sum(self.m_input * tf.log(self.z_eval))
self.optimizer = tf.train.AdamOptimizer(self.config.LEARNING_RATE).minimize(self.cross_entropy_loss)

好了,代码部分就介绍到这里,关于Categorical DQN的更多的知识,大家可以结合论文和代码进行更深入的理解!

参考文献

https://baijiahao.baidu.com/s?id=1573880107529940&wfr=spider&for=pc

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-12-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小小挖掘机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、Categorical DQN
    • 1.1 为什么要输出价值分布?
      • 1.2 Categorical DQN原理
      • 2、Categorical DQN的Tensorflow实现
      • 参考文献
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档