mxnet實現(xiàn)強化學習NoisyNet網(wǎng)絡(luò)

強化學習實現(xiàn)的代碼實現(xiàn)參考主要是增強模型的探索能力-強化學習NoisyNet原理及實現(xiàn)!

論文地址:https://arxiv.org/abs/1706.10295

基礎(chǔ)知識儲備

  • 強化學習流程
  • Q-learning算法
  • DQN算法
  • epsilon貪婪策略

NoisyNet目標

在強化學習算法中,為了增強模型對動作的探索能力,通常使用的策略是epsilon貪婪策略,而NoisyNet論文結(jié)果顯示可以通過在全連接層增加噪聲來代替epsilon貪婪策略

NoisyNet理論

噪聲網(wǎng)絡(luò)是權(quán)重(weight)和偏差(bias)受到噪聲函數(shù)擾動的神經(jīng)網(wǎng)絡(luò)

如果用y表示網(wǎng)絡(luò)的輸出結(jié)果,x表示網(wǎng)絡(luò)的輸入,f表示網(wǎng)絡(luò)的映射,神經(jīng)網(wǎng)絡(luò)可以表示成

其中theta表示網(wǎng)絡(luò)參數(shù),或者表示成

輸入的x的形狀為(batch_size,feature_size),所以參數(shù)w的形狀就是(units,feature_size),參數(shù)b的形狀是(units,),xw.T進行矩陣乘法,得到的形狀為(batch_size,units),就是y的形狀

加噪聲之后,函數(shù)表示為

其中,musigmaepsilon都是和wb形狀相同的,bigodot表示元素乘法

函數(shù)在計算過程中,musigma是網(wǎng)絡(luò)需要更新學習的參數(shù),而epsilon是每次進行前向傳播的時候隨機噪聲

生成噪聲的兩種方式

獨立高斯噪聲

每次進行前向傳播的時候直接隨機生成高斯(正態(tài))噪聲,噪聲的形狀和wb相同,所以一個噪聲的形狀是(units,feature_size),一個噪聲的形狀是(units,),以共需要生成的噪聲數(shù)量就是units * feature_size + units

因子分解高斯噪聲

因子分解高斯噪聲主要可以避免生成一個(units,feature_size)的噪聲,而通過因子分解的方式分別生成一個(units,1)(1,feature_size)的噪聲,經(jīng)過噪聲函數(shù)f映射之后,進行矩陣乘積生成一個(units,feature_size)的噪聲作為權(quán)重的噪聲,而偏差的噪聲形狀仍然不變,所以以共需要生成的噪聲數(shù)量就是2 * units + feature_size,與獨立高斯噪聲相比可以減少很多資源

代碼實現(xiàn)

class Noisy(nn.HybridBlock):
    def __init__(self, units, activation=None, use_bias=True, flatten=True, dtype='float32', weight_initializer=None, bias_initializer='zeros', in_units=0, noisy_distribution='independent', **kwargs):
        super(Noisy, self).__init__(**kwargs)
        self._flatten = flatten  # 是否需要拍平,作為FullyConnected的輸入?yún)?shù)
        self.dtype = dtype
        self.ctx = try_gpu(GPU_INDEX)  # 指定計算環(huán)境GPU還是CPU
        with self.name_scope():
            self._units = units  # 全連接層的網(wǎng)絡(luò)輸出節(jié)點數(shù)
            self._in_units = in_units  # 全連接層的網(wǎng)絡(luò)輸入節(jié)點數(shù)
            self.weights = self.params.get('weights', shape=(units, in_units), init=weight_initializer, dtype=dtype,
                                           allow_deferred_init=False)  # 允許參數(shù)延遲初始化,權(quán)重參數(shù)
            self.weight_sigma = self.params.get('weight_sigma', shape=(units, in_units),
                                                init=weight_initializer, dtype=dtype,
                                                allow_deferred_init=False)  # 權(quán)重方差

            if use_bias:
                self.bias = self.params.get('bias', shape=(units, ), init=bias_initializer, dtype=dtype,
                                                allow_deferred_init=False)  # 偏差參數(shù)
                self.bias_noise = self.params.get('bias_noise', shape=(1, units), init=bias_initializer, dtype=dtype,
                                                  allow_deferred_init=False)  # 偏差方差
            else:
                self.bias = None
            if activation is not None:
                self.act = nn.Activation(activation, prefix=activation+'_')  # 是否使用激活函數(shù)
            else:
                self.act = None
        self.noisy_distribution = noisy_distribution  # 生成噪聲的方式,獨立高斯還是因子分解

    def hybrid_forward(self, F, x, weights, weight_sigma, bias=None, bias_noise=None):
        # 因子分解中的噪聲映射函數(shù)
        def real_valued_f(e_list):
            return F.multiply(F.sign(e_list), F.power(F.abs(e_list), 0.5))
        global noise_1, b
        if self.noisy_distribution == 'independent':
            w = weights + F.multiply(F.random_normal(scale=0.1, shape=weight_sigma.shape, ctx=self.ctx, dtype=self.dtype),
                                     weight_sigma)  # 元素乘法
        else:
            noise_1 = real_valued_f(F.random_normal(scale=0.1, shape=(self._units, 1), ctx=self.ctx, dtype=self.dtype))
            noise_2 = real_valued_f(F.random_normal(scale=0.1, shape=(1, self._in_units), ctx=self.ctx, dtype=self.dtype))
            w = weights + F.multiply(F.dot(noise_1, noise_2), weight_sigma)
        if self.bias is not None:
            if self.noisy_distribution == 'independent':
                b = bias + F.multiply(F.random_normal(scale=0.1, shape=bias.shape, ctx=self.ctx, dtype=self.dtype),
                                      bias_noise.reshape(bias.shape))
            else:
                b = bias + F.multiply(noise_1.reshape(bias.shape), bias_noise.reshape(bias.shape))
        act = F.FullyConnected(x, w, b, no_bias=bias is None, num_hidden=self._units,
                               flatten=self._flatten, name='fwd')  # 處理之后的權(quán)重和偏差輸入到全連接層
        if self.act is not None:
            act = self.act(act)  # 指定是否加激活層
        return act

類使用方法

if __name__ == '__main__':
    net = nn.Sequential()
    a = Noisy(5, in_units=10
                  , weight_initializer=init.Uniform(0.1),
                  bias_initializer=init.Constant(0.1)
                  )

    net.add(Noisy(5, in_units=10
                  , weight_initializer=init.Uniform(0.1),
                  bias_initializer=init.Constant(0.1)
                  ))
    # net.add(nn.Dense(5, weight_initializer=init.Uniform(0.1)))
    net.initialize(init=init.Xavier(), ctx=mx.cpu(1))
    x = nd.arange(20 * 10).reshape((20, 10))
    y = net(x)
    print(y)

總結(jié)

  • 使用噪聲網(wǎng)絡(luò)會增加網(wǎng)絡(luò)參數(shù),在計算資源有限的情況下,可能需要修改原來的網(wǎng)絡(luò)參數(shù),以減少網(wǎng)絡(luò)參數(shù)所占用的資源
  • 該代碼的實現(xiàn)方式主要參考nn.Dense源代碼的實現(xiàn)方式,關(guān)于HybridBlockBlock類之間的實現(xiàn)方式可以參考上一篇文章mxnet采坑記
  • Dense層的源代碼中allow_deferred_init的參數(shù)設(shè)置為True,表示支持延遲初始化,即等到第一個數(shù)據(jù)傳入的時候才會根據(jù)數(shù)據(jù)推斷網(wǎng)絡(luò)的輸入節(jié)點數(shù),而噪聲網(wǎng)絡(luò)中由于前向網(wǎng)絡(luò)的計算依賴于初始化的權(quán)重和偏差參數(shù),所以不支持延遲初始化,需要在初始化該類的時候指定in_units參數(shù)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容