在強化學習系列的前七篇裏,咱們主要討論的都是規模比較小的強化學習問題求解算法。今天開始咱們步入深度強化學習。這一篇關注於價值函數的近似表示和Deep Q-Learning算法。html
Deep Q-Learning這一篇對應Sutton書的第11章部分和UCL強化學習課程的第六講。git
在以前講到了強化學習求解方法,不管是動態規劃DP,蒙特卡羅方法MC,仍是時序差分TD,使用的狀態都是離散的有限個狀態集合$\mathbb{S}$。此時問題的規模比較小,比較容易求解。可是假如咱們遇到複雜的狀態集合呢?甚至不少時候,狀態是連續的,那麼就算離散化後,集合也很大,此時咱們的傳統方法,好比Q-Learning,根本沒法在內存中維護這麼大的一張Q表。 github
好比經典的冰球世界(PuckWorld) 強化學習問題,具體的動態demo見這裏。環境由一個正方形區域構成表明着冰球場地,場地內大的圓表明着運動員個體,小圓表明着目標冰球。在這個正方形環境中,小圓會每隔必定的時間隨機改變在場地的位置,而表明個體的大圓的任務就是儘量快的接近冰球目標。大圓能夠操做的行爲是在水平和豎直共四個方向上施加一個時間步時長的一個大小固定的力,藉此來改變大圓的速度。環境會在每個時間步內告訴個體當前的水平與垂直座標、當前的速度在水平和垂直方向上的份量以及目標的水平和垂直座標共6項數據,獎勵值爲個體與目標二者中心距離的負數,也就是距離越大獎勵值越低且最高獎勵值爲0。算法
在這個問題中,狀態是一個6維的向量,而且是連續值。無法直接用以前離散集合的方法來描述狀態。固然,你能夠說,咱們能夠把連續特徵離散化。好比把這個冰球場100x100的框按1x1的格子劃分紅10000個格子,那麼對於運動員的座標和冰球的座標就有$10^4*10^4=10^{8}$次種,若是再加上個體速度的份量就更是天文數字了,此時以前講過的強化學習方法都會由於問題的規模太大而沒法使用。怎麼辦呢?必需要對問題的建模作修改了,而價值函數的近似表示就是一個可行的方法。網絡
因爲問題的狀態集合規模大,一個可行的建模方法是價值函數的近似表示。方法是咱們引入一個狀態價值函數$\hat{v}$, 這個函數由參數$w$描述,並接受狀態$s$做爲輸入,計算後獲得狀態$s$的價值,即咱們指望:$$\hat{v}(s, w) \approx v_{\pi}(s)$$app
相似的,引入一個動做價值函數$\hat{q}$,這個函數由參數$w$描述,並接受狀態$s$與動做$a$做爲輸入,計算後獲得動做價值,即咱們指望:$$\hat{q}(s,a,w) \approx q_{\pi}(s,a)$$dom
價值函數近似的方法不少,好比最簡單的線性表示法,用$\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。
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的算法流程,基於NIPS 2013 DQN。
算法輸入:迭代輪數$T$,狀態特徵維度$n$, 動做集$A$, 步長$\alpha$,衰減因子$\gamma$, 探索率$\epsilon$, Q網絡結構, 批量梯度降低的樣本數$m$。
輸出:Q網絡參數
1. 隨機初始化Q網絡的全部參數$w$,基於$w$初始化全部的狀態和動做對應的價值$Q$。清空經驗回放的集合$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$須要隨着迭代的進行而變小。
下面咱們用一個具體的例子來演示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的分數。固然因爲DQN不保證絕對的收斂,因此可能到了200分後還會有抖動。
DQN因爲對價值函數作了近似表示,所以有了解決大規模強化學習問題的能力。可是DQN有個問題,就是它並不必定能保證Q網絡的收斂,也就是說,咱們不必定能夠獲得收斂後的Q網絡參數。這會致使咱們訓練出的模型效果不好。
針對這個問題,衍生出了DQN的不少變種,好比Nature DQN(NIPS 2015), Double DQN,Dueling DQN等。這些咱們在下一篇討論。
(歡迎轉載,轉載請註明出處。歡迎溝通交流: liujianping-ok@163.com)