強化學習實現(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,),x與w.T進行矩陣乘法,得到的形狀為(batch_size,units),就是y的形狀
加噪聲之后,函數(shù)表示為

其中,mu、sigma和epsilon都是和w和b形狀相同的,bigodot表示元素乘法
函數(shù)在計算過程中,mu和sigma是網(wǎng)絡(luò)需要更新學習的參數(shù),而epsilon是每次進行前向傳播的時候隨機噪聲
生成噪聲的兩種方式
獨立高斯噪聲
每次進行前向傳播的時候直接隨機生成高斯(正態(tài))噪聲,噪聲的形狀和w和b相同,所以一個噪聲的形狀是(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)于HybridBlock和Block類之間的實現(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ù)