1. 程式人生 > >強化學習(八)價值函數的近似表示與Deep Q-Learning

強化學習(八)價值函數的近似表示與Deep Q-Learning

learning 步驟 狀態更新 任務 eva 學習 max wid 表示

    在強化學習系列的前七篇裏,我們主要討論的都是規模比較小的強化學習問題求解算法。今天開始我們步入深度強化學習。這一篇關註於價值函數的近似表示和Deep Q-Learning算法。

    Deep Q-Learning這一篇對應Sutton書的第11章部分和UCL強化學習課程的第六講。

1. 為何需要價值函數的近似表示

    在之前講到了強化學習求解方法,無論是動態規劃DP,蒙特卡羅方法MC,還是時序差分TD,使用的狀態都是離散的有限個狀態集合$\mathbb{S}$。此時問題的規模比較小,比較容易求解。但是假如我們遇到復雜的狀態集合呢?甚至很多時候,狀態是連續的,那麽就算離散化後,集合也很大,此時我們的傳統方法,比如Q-Learning,根本無法在內存中維護這麽大的一張Q表。    

    比如經典的冰球世界(PuckWorld) 強化學習問題,具體的動態demo見這裏。環境由一個正方形區域構成代表著冰球場地,場地內大的圓代表著運動員個體,小圓代表著目標冰球。在這個正方形環境中,小圓會每隔一定的時間隨機改變在場地的位置,而代表個體的大圓的任務就是盡可能快的接近冰球目標。大圓可以操作的行為是在水平和豎直共四個方向上施加一個時間步時長的一個大小固定的力,借此來改變大圓的速度。環境會在每一個時間步內告訴個體當前的水平與垂直坐標、當前的速度在水平和垂直方向上的分量以及目標的水平和垂直坐標共6項數據,獎勵值為個體與目標兩者中心距離的負數,也就是距離越大獎勵值越低且最高獎勵值為0。

    在這個問題中,狀態是一個6維的向量,並且是連續值。沒法直接用之前離散集合的方法來描述狀態。當然,你可以說,我們可以把連續特征離散化。比如把這個冰球場100x100的框按1x1的格子劃分成10000個格子,那麽對於運動員的坐標和冰球的坐標就有$2^5*2^5=2^{10}$次種,如果再加上個體速度的分量就更是天文數字了,此時之前講過的強化學習方法都會因為問題的規模太大而無法使用。怎麽辦呢?必須要對問題的建模做修改了,而價值函數的近似表示就是一個可行的方法。

2. 價值函數的近似表示方法

    由於問題的狀態集合規模大,一個可行的建模方法是價值函數的近似表示。方法是我們引入一個狀態價值函數$\hat{v}$, 這個函數由參數$w$描述,並接受狀態$s$作為輸入,計算後得到狀態$s$的價值,即我們期望:$$\hat{v}(s, w) \approx v_{\pi}(s)$$

    類似的,引入一個動作價值函數$\hat{q}$,這個函數由參數$w$描述,並接受狀態$s$與動作$a$作為輸入,計算後得到動作價值,即我們期望:$$\hat{q}(s,a,w) \approx q_{\pi}(s,a)$$

    價值函數近似的方法很多,比如最簡單的線性表示法,用$\phi(s)$表示狀態s的特征向量,則此時我們的狀態價值函數可以近似表示為:$$\hat{v}(s, w) = \phi(s)^Tw$$

    當然,除了線性表示法,我們還可以用決策樹,最近鄰,傅裏葉變換,神經網絡來表達我們的狀態價值函數。而最常見,應用最廣泛的表示方法是神經網絡。因此後面我們的近似表達方法如果沒有特別提到,都是指的神經網絡的近似表示。

    對於神經網絡,可以使用DNN,CNN或者RNN。沒有特別的限制。如果把我們計算價值函數的神經網絡看做一個黑盒子,那麽整個近似過程可以看做下面這三種情況:

技術分享圖片

    對於狀態價值函數,神經網絡的輸入是狀態s的特征向量,輸出是狀態價值$\hat{v}(s, w)$。對於動作價值函數,有兩種方法,一種是輸入狀態s的特征向量和動作a,輸出對應的動作價值$\hat{q}(s,a,w)$,另一種是只輸入狀態s的特征向量,動作集合有多少個動作就有多少個輸出$\hat{q}(s,a_i,w)$。這裏隱含了我們的動作是有限個的離散動作。

    對於我們前一篇講到的Q-Learning算法,我們現在就價值函數的近似表示來將其改造,采用上面右邊的第三幅圖的動作價值函數建模思路來做,現在我們叫它Deep Q-Learning。

3. Deep Q-Learning算法思路

    Deep Q-Learning算法的基本思路來源於Q-Learning。但是和Q-Learning不同的地方在於,它的Q值的計算不是直接通過狀態值s和動作來計算,而是通過上面講到的Q網絡來計算的。這個Q網絡是一個神經網絡,我們一般簡稱Deep Q-Learning為DQN。

    DQN的輸入是我們的狀態s對應的狀態向量$\phi(s)$, 輸出是所有動作在該狀態下的動作價值函數Q。Q網絡可以是DNN,CNN或者RNN,沒有具體的網絡結構要求。

    DQN主要使用的技巧是經驗回放(experience replay),即將每次和環境交互得到的獎勵與狀態更新情況都保存起來,用於後面目標Q值的更新。為什麽需要經驗回放呢?我們回憶一下Q-Learning,它是有一張Q表來保存所有的Q值的當前結果的,但是DQN是沒有的,那麽在做動作價值函數更新的時候,就需要其他的方法,這個方法就是經驗回放。

    通過經驗回放得到的目標Q值和通過Q網絡計算的Q值肯定是有誤差的,那麽我們可以通過梯度的反向傳播來更新神經網絡的參數$w$,當$w$收斂後,我們的就得到的近似的Q值計算方法,進而貪婪策略也就求出來了。

    下面我們總結下DQN的算法流程。    

    算法輸入:叠代輪數$T$,狀態特征維度$n$, 動作集$A$, 步長$\alpha$,衰減因子$\gamma$, 探索率$\epsilon$, Q網絡結構, 批量梯度下降的樣本數$m$。

    輸出:所有的狀態和動作對應的價值$Q$

    1. 隨機初始化所有的狀態和動作對應的價值$Q$. 隨機初始化Q網絡的所有參數$w$。清空經驗回放的集合$D$。

    2. for i from 1 to T,進行叠代。

      a) 初始化S為當前狀態序列的第一個狀態, 拿到其特征向量$\phi(S)$

      b) 在Q網絡中使用$\phi(S)$作為輸入,得到Q網絡的所有動作對應的Q值輸出。用$\epsilon-$貪婪法在當前Q值輸出中選擇對應的動作$A$

      c) 在狀態$S$執行當前動作$A$,得到新狀態$S‘$對應的特征向量$\phi(S‘)和獎勵$R$,是否終止狀態is_end

      d) 將$\{\phi(S),A,R,\phi(S‘),is\_end\}$這個五元組存入經驗回放集合$D$

      e) $S=S‘$

      f) 從經驗回放集合$D$中采樣$m$個樣本$\{\phi(S_j),A_j,R_j,\phi(S‘_j),is\_end_j\}, j=1,2.,,,m$,計算當前目標Q值$y_j$:$$y_j= \begin{cases} R_j& {is\_end_j\; is \;true}\\ R_j + \gamma\max_{a‘}Q(\phi(S‘_j),A‘_j,w) & {is\_end_j \;is\; false} \end{cases}$$

      g) 使用均方差損失函數$\frac{1}{m}\sum\limits_{j=1}^m(y_j-Q(\phi(S_j),A_j,w))^2$,通過神經網絡的梯度反向傳播來更新Q網絡的所有參數$w$

      h) 如果$S‘$是終止狀態,當前輪叠代完畢,否則轉到步驟b)

      註意,上述第二步的f步和g步的Q值計算也都需要通過Q網絡計算得到。另外,實際應用中,為了算法較好的收斂,探索率$\epsilon$需要隨著叠代的進行而變小。

4. Deep Q-Learning實例

    下面我們用一個具體的例子來演示DQN的應用。這裏使用了OpenAI Gym中的CartPole-v0遊戲來作為我們算法應用。CartPole-v0遊戲的介紹參見這裏。它比較簡單,基本要求就是控制下面的cart移動使連接在上面的pole保持垂直不倒。這個任務只有兩個離散動作,要麽向左用力,要麽向右用力。而state狀態就是這個cart的位置和速度, pole的角度和角速度,4維的特征。堅持到200分的獎勵則為過關。

    完整的代碼參見我的github: https://github.com/ljpzzz/machinelearning/blob/master/reinforcement-learning/dqn.py

    代碼參考了知乎上的一個DQN實例,修改了代碼中的一些錯誤,並用最新的Python3.6+Tensorflow1.8.0運行。要跑代碼需要安裝OpenAI的Gym庫,使用"pip install gym"即可。

    代碼使用了一個三層的神經網絡,輸入層,一個隱藏層和一個輸出層。下面我們看看關鍵部分的代碼。

    算法第2步的步驟b通過$\epsilon-$貪婪法選擇動作的代碼如下,註意每次我們$\epsilon-$貪婪法後都會減小$\epsilon$值。

  def egreedy_action(self,state):
    Q_value = self.Q_value.eval(feed_dict = {
      self.state_input:[state]
      })[0]
    if random.random() <= self.epsilon:
        self.epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / 10000
        return random.randint(0,self.action_dim - 1)
    else:
        self.epsilon -= (INITIAL_EPSILON - FINAL_EPSILON) / 10000
        return np.argmax(Q_value)

    算法第2步的步驟c在狀態$S$執行當前動作$A$的代碼如下,這個交互是由Gym完成的。

      next_state,reward,done,_ = env.step(action)
      # Define reward for agent
      reward = -1 if done else 0.1

    算法第2步的步驟d保存經驗回放數據的代碼如下:

  def perceive(self,state,action,reward,next_state,done):
    one_hot_action = np.zeros(self.action_dim)
    one_hot_action[action] = 1
    self.replay_buffer.append((state,one_hot_action,reward,next_state,done))
    if len(self.replay_buffer) > REPLAY_SIZE:
      self.replay_buffer.popleft()

    if len(self.replay_buffer) > BATCH_SIZE:
      self.train_Q_network()

    算法第2步的步驟f,g計算目標Q值,並更新Q網絡的代碼如下:

  def train_Q_network(self):
    self.time_step += 1
    # Step 1: obtain random minibatch from replay memory
    minibatch = random.sample(self.replay_buffer,BATCH_SIZE)
    state_batch = [data[0] for data in minibatch]
    action_batch = [data[1] for data in minibatch]
    reward_batch = [data[2] for data in minibatch]
    next_state_batch = [data[3] for data in minibatch]

    # Step 2: calculate y
    y_batch = []
    Q_value_batch = self.Q_value.eval(feed_dict={self.state_input:next_state_batch})
    for i in range(0,BATCH_SIZE):
      done = minibatch[i][4]
      if done:
        y_batch.append(reward_batch[i])
      else :
        y_batch.append(reward_batch[i] + GAMMA * np.max(Q_value_batch[i]))

    self.optimizer.run(feed_dict={
      self.y_input:y_batch,
      self.action_input:action_batch,
      self.state_input:state_batch
      })

    我們在每100輪叠代完後會去玩10次交互測試,計算10次的平均獎勵。運行了代碼後,我的3000輪叠代的輸出如下:

episode: 0 Evaluation Average Reward: 12.2
episode: 100 Evaluation Average Reward: 9.4
episode: 200 Evaluation Average Reward: 10.4
episode: 300 Evaluation Average Reward: 10.5
episode: 400 Evaluation Average Reward: 11.6
episode: 500 Evaluation Average Reward: 12.4
episode: 600 Evaluation Average Reward: 29.6
episode: 700 Evaluation Average Reward: 48.1
episode: 800 Evaluation Average Reward: 85.0
episode: 900 Evaluation Average Reward: 169.4
episode: 1000 Evaluation Average Reward: 200.0
episode: 1100 Evaluation Average Reward: 200.0
episode: 1200 Evaluation Average Reward: 200.0
episode: 1300 Evaluation Average Reward: 200.0
episode: 1400 Evaluation Average Reward: 200.0
episode: 1500 Evaluation Average Reward: 200.0
episode: 1600 Evaluation Average Reward: 200.0
episode: 1700 Evaluation Average Reward: 200.0
episode: 1800 Evaluation Average Reward: 200.0
episode: 1900 Evaluation Average Reward: 200.0
episode: 2000 Evaluation Average Reward: 200.0
episode: 2100 Evaluation Average Reward: 200.0
episode: 2200 Evaluation Average Reward: 200.0
episode: 2300 Evaluation Average Reward: 200.0
episode: 2400 Evaluation Average Reward: 200.0
episode: 2500 Evaluation Average Reward: 200.0
episode: 2600 Evaluation Average Reward: 200.0
episode: 2700 Evaluation Average Reward: 200.0
episode: 2800 Evaluation Average Reward: 200.0
episode: 2900 Evaluation Average Reward: 200.0

    大概到第1000次叠代後,算法已經收斂,達到最高的200分。當然由於是$\epsilon-$探索,每次前面的輸出可能不同,但最後應該都可以收斂到200的分數。

5. Deep Q-Learning小結    

    DQN由於對價值函數做了近似表示,因此有了解決大規模強化學習問題的能力。但是DQN有個問題,就是它並不一定能保證Q網絡的收斂,也就是說,我們不一定可以得到收斂後的Q網絡參數。這會導致我們訓練出的模型效果很差。

     針對這個問題,衍生出了DQN的很多變種,比如Double DQN,Dueling DQN等。這些我們在下一篇討論。

(歡迎轉載,轉載請註明出處。歡迎溝通交流: [email protected]

強化學習(八)價值函數的近似表示與Deep Q-Learning