DRQN學習——代碼解讀

更新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

 

參考:https://zhuanlan.zhihu.com/p/54898904

           https://www.jianshu.com/p/305aee09ec31

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章