增强模型的探索能力-强化学习NoisyNet原理及实现!

增加Agent的探索能力是强化学习中经常遇到的问题,一种常用的方法是采用e-greedy的策略,即以e的概率采取随机的动作,以1-e的概率采取当前获得价值最大的动作。本文我们将介绍另一种方法:NoisyNet,该方法通过对参数增加噪声来增加模型的探索能力。

1、NoisyNet的原理

我们的噪声通常添加在全连接层,考虑我们全连接层的前向计算公式:

假设两层的神经元个数分别为p个和q个,那么w是q*p的,x是p维的,y和b都是q维的。

此时我们在参数上增加噪声,文章中假设每一个参数b和w分别服从于均值为μ,方差为σ的正态分布,同时存在一定的随机噪声ε,我们可以假设噪声是服从标准正态分布N(0,1)的。那么前向计算公式变为:

这样,我们模型的变量从原来的p*q + q个,变为了2 * p * q + q个,你可能会问,变量不是3 * p * q + q个么?因为这里,我们的噪声ε在每一轮中,都是随机产生的常量,试想如果噪声ε也是变量的话,就跟原有的wx+b没有什么区别了。

接下来就是这个噪声如何产生的问题了。文中提到了两种方法:

Independent Gaussian noise:这也是我们最容易想到的方法,就是直接从标准正态分布中,随机产生p*q+q个常量。这也就是说,对于每一个全连接层来说,为每一个w和b都产生独立的噪声,这无疑对于模型的计算带来了不小的负担。

Factorised Gaussian noise:该方法有效地减少了噪声的个数,我们只需要p + q个噪声即可,w和b的噪声计算方式如下:

而上式中的f所代表的函数如下:

了解了如何给参数增加噪声,我们就可以把这种方法应用于DQN或者AC等方法中。

2、NoisyNet的TF实现

代码的地址为:https://github.com/princewen/tensorflow_practice/tree/master/RL/Basic-NoisyNet-Demo

这里实现的是使用DQN来玩Atrai游戏的Demo。关于DQN的整体实现思路,我们就不在细讲了,这里重点介绍一下eval-net的构建以及其中最重点的带噪声的全连接层的实现。

我们这里的网络结构是两层卷积层 + 三层全连接层,输入是当前的状态,即三帧的游戏画面,输出是上下左右共四个动作的预估Q值:

def build_layers(self, state, 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)
        # 两种reshape写法
        # flatten = tf.reshape(relu5, [-1, np.prod(relu5.get_shape().as_list()[1:])])
        # flatten = tf.reshape(relu5, [-1, np.prod(relu5.shape.as_list()[1:])])
        # print flatten.get_shape()
    with tf.variable_scope('dense1'):
        dense1 = noisy_dense(flatten, units_1, [units_1], c_names, w_i, b_i, noisy_distribution=self.config.noisy_distribution)
    with tf.variable_scope('dense2'):
        dense2 = noisy_dense(dense1, units_2, [units_2], c_names, w_i, b_i, noisy_distribution=self.config.noisy_distribution)
    with tf.variable_scope('dense3'):
        dense3 = noisy_dense(dense2, self.action_dim, [self.action_dim], c_names, w_i, b_i, noisy_distribution=self.config.noisy_distribution)
    return dense3

接下来,我们重点介绍一下之中noisy_dense的实现。首先,我们得到每个w的均值和方差的估计:

weights = tf.get_variable('weights', shape=[flatten_shape, units], initializer=w_i)
w_sigma = tf.get_variable('w_sigma', [flatten_shape, units], initializer=w_i, collections=c_names)

如果噪声采用Independent Gaussian noise的方式,我们接下来需要得到p*q个的随机噪声,并得到最终的w:

if noisy_distribution == 'independent':
    weights += tf.multiply(tf.random_normal(shape=w_sigma.shape), w_sigma)

如果噪声采用Factorised Gaussian noise的方式,我们需要得到p + q个噪声,并对这些噪声进行变换,最终得到w:

elif noisy_distribution == 'factorised':
    noise_1 = f(tf.random_normal(tf.TensorShape([flatten_shape, 1]), dtype=tf.float32))  # 注意是列向量形式,方便矩阵乘法
    noise_2 = f(tf.random_normal(tf.TensorShape([1, units]), dtype=tf.float32))
    weights += tf.multiply(noise_1 * noise_2, w_sigma)

其中变换的代码如下:

  def f(e_list):
    return tf.multiply(tf.sign(e_list), tf.pow(tf.abs(e_list), 0.5))

接下来,进行全连接的操作:

dense = tf.matmul(inputs, weights)

最后,如果有偏置项的话,我们同样通过不同的方式为偏置项增加噪声:

if bias_shape is not None:
    assert bias_shape[0] == units
    biases = tf.get_variable('biases', shape=bias_shape, initializer=b_i)
    b_noise = tf.get_variable('b_noise', [1, units], initializer=b_i, collections=c_names)
    if noisy_distribution == 'independent':
        biases += tf.multiply(tf.random_normal(shape=b_noise.shape), b_noise)
    elif noisy_distribution == 'factorised':
        biases += tf.multiply(noise_2, b_noise)

最后,经过激活函数得到最后的输出:

return activation(dense) if activation is not None else dense

完整noise_dense的函数如下:

def noisy_dense(inputs, units, bias_shape, c_names, w_i, b_i=None, activation=tf.nn.relu, noisy_distribution='factorised'):
    def f(e_list):
        return tf.multiply(tf.sign(e_list), tf.pow(tf.abs(e_list), 0.5))

    if not isinstance(inputs, ops.Tensor):
        inputs = ops.convert_to_tensor(inputs, dtype='float')

    if len(inputs.shape) > 2:
        inputs = tf.contrib.layers.flatten(inputs)
    flatten_shape = inputs.shape[1]
    weights = tf.get_variable('weights', shape=[flatten_shape, units], initializer=w_i)
    w_sigma = tf.get_variable('w_sigma', [flatten_shape, units], initializer=w_i, collections=c_names)
    if noisy_distribution == 'independent':
        weights += tf.multiply(tf.random_normal(shape=w_sigma.shape), w_sigma)
    elif noisy_distribution == 'factorised':
        noise_1 = f(tf.random_normal(tf.TensorShape([flatten_shape, 1]), dtype=tf.float32))  # 注意是列向量形式,方便矩阵乘法
        noise_2 = f(tf.random_normal(tf.TensorShape([1, units]), dtype=tf.float32))
        weights += tf.multiply(noise_1 * noise_2, w_sigma)
    dense = tf.matmul(inputs, weights)
    if bias_shape is not None:
        assert bias_shape[0] == units
        biases = tf.get_variable('biases', shape=bias_shape, initializer=b_i)
        b_noise = tf.get_variable('b_noise', [1, units], initializer=b_i, collections=c_names)
        if noisy_distribution == 'independent':
            biases += tf.multiply(tf.random_normal(shape=b_noise.shape), b_noise)
        elif noisy_distribution == 'factorised':
            biases += tf.multiply(noise_2, b_noise)
        return activation(dense + biases) if activation is not None else dense + biases
    return activation(dense) if activation is not None else dense

说点题外话,这个方法我是通过《强化学习精要:核心算法与Tensorflow》这本书看到的,书中对该方法的描述好像与论文和github上代码中所描述的有所出入,书中好像是把变量的方差当作噪声了。如果大家看过这本书同时也看过原作,如果觉得书中写的没有问题而本文写的有错误的话,欢迎大家在下方留言指正!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • 1乳腺增生 很多女性都有乳房胀痛、乳腺增生的问题,其中不少和爱生气有关;产后心情不好,容易发生乳腺炎;中老年人更可...
    好医道阅读 162评论 0 0
  • 2017.4.5白话版 1、我们先回顾一下熊老师上周末曾留下的一个问题:如果我爷爷的爷爷欺负了你爷爷的爷爷,你该不...
    祥和鸿泰阅读 191评论 1 1
  • 大学,应该是恋爱最美好的时候,因为不再像高中那样,因为再不会有老师因为觉得早恋影响学习而各种想方设拆散我们一对对可...
    Mercy_8fa8阅读 156评论 0 0
  • 20180612星期二 天气晴 时间过的真快啊,第二学期已进入复习阶段了,还是慢调斯文地学习的话,很快就...
    璇戎爸爸阅读 194评论 0 0
  • 随笔 写作是需要契机的,正如现在的我一样,听着莫名的歌,抒发着莫名的情绪!其实我本身是一个简单的纠结体,我时常想写...
    柳阳love阅读 190评论 0 0