更新2019.12.5
找到一個可以跑的DRQN,不用安裝新的遊戲環境,更新需要的庫即可。做了一些修改。代碼:https://github.com/Elly0723/DRQN-tensorflow
主要信息————————————
1、輸入是[1,84,84,3]大小的圖片batchsize:4,轉化成[?,21168]大小的向量,再reshape成[?,84,84,3]進入四層卷積,經過單元數爲512的lstm得到當前隱狀態ht,以2:1比例分爲advantage 和 value值,計算最終的Qout。
2、targetQ和MainQ用的是同一個Q網絡。每5steps用主網絡更新目標網絡,更新lstm隱藏狀態。
3、損失函數爲target和Qout的平方誤差,計算損失的時,用mask去掉了一半的值(現在還不知道原因)
主要修改——————————
1、去掉了gif的存儲,因爲存了一堆看不明白的圖
2、輸出每10次迭代的loss圖
後期計劃(目標是用RNN和RL做路徑規劃)———————
1、修改輸入爲一張黑白的障礙物地圖,輸出離散動作以到達目的地
2、修改網絡框架,輸出連續動作值,參考DDPG
3、DDPG、AC和Lstm的嘗試結合
4、多智能體的路徑規劃
每次sample 4 個記錄,關於lstm歸零隱藏狀態如下:
if total_steps % (update_freq) == 0:
updateTarget(targetOps, sess)
#####################################在這裏,每update_freq=5steps,更新一次lstm隱狀態。
##################################################################################
# Reset the recurrent layer's hidden state
state_train = (np.zeros([batch_size, h_size]), np.zeros([batch_size, h_size]))
trainBatch = myBuffer.sample(batch_size, trace_length) #[[s, a, r, s1, d],[...]...]batch_size*trace_length個
#Get a random batch of experiences.
# Below we perform the Double-DQN update to the target Q-values
Q1 = sess.run(mainQN.predict,feed_dict={ \
mainQN.scalarInput: np.vstack(trainBatch[:, 3] / 255.0), \
mainQN.trainLength: trace_length, mainQN.state_in: state_train, mainQN.batch_size: batch_size})
Q2 = sess.run(targetQN.Qout, feed_dict={ \
targetQN.scalarInput: np.vstack(trainBatch[:, 3] / 255.0), \
targetQN.trainLength: trace_length, targetQN.state_in: state_train,
targetQN.batch_size: batch_size})
end_multiplier = -(trainBatch[:, 4] - 1)
doubleQ = Q2[range(batch_size * trace_length), Q1]
targetQ = trainBatch[:, 2] + (y * doubleQ * end_multiplier)
——————————————————————-以上更新——————————————————————————
基於DRQN玩vizdoom,代碼(09章):https://github.com/sudharsan13296/Hands-On-Reinforcement-Learning-With-Python/
DQN神經網絡是用於像Atari遊戲這樣的遊戲AI程序,它通過觀察遊戲屏幕內容,由卷積網絡(CNN)捕獲屏幕中每一幀的特徵來使AI能夠“理解”玩遊戲的過程,通過強化學習的獎罰機制,讓AI成爲遊戲大師。然而DQN存在一個重要問題:那就是對屏幕捕獲的記憶量有限。
DRQN:在DQN中,輸入s、a,用神經網絡逼近Q(s,a)值,而在某些情況下,智能體不能只根據當前一個觀測值做決策,而需要之前的幾個觀測狀態。比如打乒乓球,不但要知道球現在在哪裏,也要知道球的速度和方向。因此加入RNN(或lstm),讓後續狀態也能感知以前的信息。
首先,複習一下使用卷積層獲得狀態信息的DQN: 使用連續的四幀圖像作爲輸入。
DRQN的結構如下圖所示,DRQN中將DQN中的一個全鏈接層替換爲了LSTM結構,每一次的輸入由四幀畫面變成了僅僅一張畫面。LSTM的輸出經過一個全鏈接層之後變爲每個動作的Q值。
在原始DQN中增加RNN:h(t)=tanh(W*x(t)+U*h(t-1)+b),x(t)爲inputs經過幾個卷積層得到的結果;o(t)=softmax(V*h(t)+b),o(t)爲輸出。 最後用一層全連接y=W*o(t)+b,得到最終的Q值。
#搭建DRQN網絡的代碼
# now let us build the network
# first convolutional layer
self.conv1 = tf.nn.conv2d(
input=tf.reshape(self.input, shape=(1, self.input_shape[0], self.input_shape[1], self.input_shape[2])),
filter=self.features1, strides=[1, self.stride, self.stride, 1], padding="VALID")
self.relu1 = tf.nn.relu(self.conv1)
self.pool1 = tf.nn.max_pool(self.relu1, ksize=[1, self.poolsize, self.poolsize, 1],
strides=[1, self.stride, self.stride, 1], padding="SAME")
# second convolutional layer
self.conv2 = tf.nn.conv2d(input=self.pool1, filter=self.features2, strides=[1, self.stride, self.stride, 1],
padding="VALID")
self.relu2 = tf.nn.relu(self.conv2)
self.pool2 = tf.nn.max_pool(self.relu2, ksize=[1, self.poolsize, self.poolsize, 1],
strides=[1, self.stride, self.stride, 1], padding="SAME")
# third convolutional layer
self.conv3 = tf.nn.conv2d(input=self.pool2, filter=self.features3, strides=[1, self.stride, self.stride, 1],
padding="VALID")
self.relu3 = tf.nn.relu(self.conv3)
self.pool3 = tf.nn.max_pool(self.relu3, ksize=[1, self.poolsize, self.poolsize, 1],
strides=[1, self.stride, self.stride, 1], padding="SAME")
# add dropout and reshape the input
self.drop1 = tf.nn.dropout(self.pool3, self.dropout_probability[0])
self.reshaped_input = tf.reshape(self.drop1, shape=[1, -1])
# 現在我們建立的遞歸神經網絡,輸入爲 卷積網絡的最後一層
self.h = tf.tanh(tf.matmul(self.reshaped_input, self.rW) + tf.matmul(self.h, self.rU) + self.rb)
self.o = tf.nn.softmax(tf.matmul(self.h, self.rV) + self.rc)
# add drop out to RNN
self.drop2 = tf.nn.dropout(self.o, self.dropout_probability[1])
# we feed the result of RNN to the feed forward layer
self.output = tf.reshape(tf.matmul(self.drop2, self.fW) + self.fb, shape=[-1, 1])
self.prediction = tf.argmax(self.output)
# compute loss
self.loss = tf.reduce_mean(tf.square(self.target_vector - self.output))
# we use Adam optimizer for minimizing the error
self.optimizer = tf.train.AdamOptimizer(self.learning_rate)
# compute gradients of the loss and update the gradients
self.gradients = self.optimizer.compute_gradients(self.loss)
self.update = self.optimizer.apply_gradients(self.gradients)
LSTM更新方式:參考:https://zhuanlan.zhihu.com/p/54898904
每次更新循環網絡,需要包含一段時間連續的若干觀測值 與獎勵值 。此外,在每次訓練時,LSTM隱含層的初始狀態可以是0,也可以從上一次的值繼承過來。因此具有兩種更新學習方式:
a. Bootstrapped 序列更新
從經驗回放內存中隨機選取一次遊戲過程(episode),從這次遊戲過程的開始一直學習到遊戲結束。在每一個時刻t,其目標狀態值還是通過目標網絡來獲取。在一次遊戲過程中,每一時刻LSTM隱含層的狀態值從上一時刻繼承而來。
b. Bootstrapped 隨機更新
從經驗回放內存中隨機選取一次遊戲過程(episode),再在這個遊戲過程中隨機選擇一個時刻點,再選擇若干步進行學習(可以是一步)。在每一個時刻t,其目標狀態值還是通過目標網絡 來獲取。在每一次訓練前需要將LSTM隱含層的狀態置爲0。
序列更新能夠更好的讓LSTM學習一次遊戲過程的所有時間序列記憶,更有利於時序推理。但是由於序列化採樣學習,違背了DQN隨機採樣的策略(因爲神經網絡要求學習數據獨立同分布,由於時序數據之間有馬爾科夫性,則會損害神經網絡的學習效果)。
隨機更新更符合DQN的隨機採樣策略,但是需要每次更新前將LSTM隱含層狀態置爲0,這將損害LSTM的記憶能力。實驗證明這兩種更新方法都能得到收斂的策略以及相似的效果。原文中主要採用隨機更新的方法。
我的疑問:代碼中貌似採用隨機更新的方法,但是不知道如何體現:選擇連續的幾步學習。難道是每隔5幀,只隨機選擇一步來學習?而且在代碼裏面,沒有看到把RNN隱層狀態置0.
for episode in range(num_episodes):
# start the new episode
game.new_episode()
# play the episode till it reaches the episode length
for frame in range(episode_length):
# get the game state
state = game.get_state()
s = state.screen_buffer
# select the action
a = actionDRQN.prediction.eval(feed_dict={actionDRQN.input: s})[0]
action = actions[a]
# perform the action and store the reward
reward = game.make_action(action)
# update total rewad
total_reward += reward
# if the episode is over then break
if game.is_episode_finished():
break
# store transistion to our experience buffer
if (frame % store) == 0:
experiences.appendToBuffer((s, action, reward))
# sample experience form the experience buffer
if (frame % sample) == 0:
###############################################################################
###在這裏,難道每次只選取一步來學習?!???????????????????????
###############################################################################3
memory = experiences.sample(1)
mem_frame = memory[0][0]
mem_reward = memory[0][2]
# now, train the network
Q1 = actionDRQN.output.eval(feed_dict={actionDRQN.input: mem_frame})
Q2 = targetDRQN.output.eval(feed_dict={targetDRQN.input: mem_frame})
# set learning rate
learning_rate = actionDRQN.learning_rate.eval()
# calculate Q value
Qtarget = old_q_value + learning_rate * (mem_reward + discount_factor * Q2 - old_q_value)
# update old Q value
old_q_value = Qtarget
# compute Loss
loss = actionDRQN.loss.eval(
feed_dict={actionDRQN.target_vector: Qtarget, actionDRQN.input: mem_frame})
# update total loss
total_loss += loss
# update both networks
actionDRQN.update.run(feed_dict={actionDRQN.target_vector: Qtarget, actionDRQN.input: mem_frame})
targetDRQN.update.run(feed_dict={targetDRQN.target_vector: Qtarget, targetDRQN.input: mem_frame})
rewards.append((episode, total_reward))
losses.append((episode, total_loss))
print("Episode %d - Reward = %.3f, Loss = %.3f." % (episode, total_reward, total_loss))
total_reward = 0
total_loss = 0