在強化學習(十)Double DQN (DDQN)中,咱們講到了DDQN使用兩個Q網絡,用當前Q網絡計算最大Q值對應的動做,用目標Q網絡計算這個最大動做對應的目標Q值,進而消除貪婪法帶來的誤差。今天咱們在DDQN的基礎上,對經驗回放部分的邏輯作優化。對應的算法是Prioritized Replay DQN。html
本章內容主要參考了ICML 2016的deep RL tutorial和Prioritized Replay DQN的論文<Prioritized Experience Replay>(ICLR 2016)。node
在Prioritized Replay DQN以前,咱們已經討論了不少種DQN,好比Nature DQN, DDQN等,他們都是經過經驗回放來採樣,進而作目標Q值的計算的。在採樣的時候,咱們是一視同仁,在經驗回放池裏面的全部的樣本都有相同的被採樣到的機率。git
可是注意到在經驗回放池裏面的不一樣的樣本因爲TD偏差的不一樣,對咱們反向傳播的做用是不同的。TD偏差越大,那麼對咱們反向傳播的做用越大。而TD偏差小的樣本,因爲TD偏差小,對反向梯度的計算影響不大。在Q網絡中,TD偏差就是目標Q網絡計算的目標Q值和當前Q網絡計算的Q值之間的差距。github
這樣若是TD偏差的絕對值$|\delta(t)|$較大的樣本更容易被採樣,則咱們的算法會比較容易收斂。下面咱們看看Prioritized Replay DQN的算法思路。算法
Prioritized Replay DQN根據每一個樣本的TD偏差絕對值$|\delta(t)|$,給定該樣本的優先級正比於$|\delta(t)|$,將這個優先級的值存入經驗回放池。回憶下以前的DQN算法,咱們僅僅只保存和環境交互獲得的樣本狀態,動做,獎勵等數據,沒有優先級這個說法。網絡
因爲引入了經驗回放的優先級,那麼Prioritized Replay DQN的經驗回放池和以前的其餘DQN算法的經驗回放池就不同了。由於這個優先級大小會影響它被採樣的機率。在實際使用中,咱們一般使用SumTree這樣的二叉樹結構來作咱們的帶優先級的經驗回放池樣本的存儲。數據結構
具體的SumTree樹結構以下圖:dom
全部的經驗回放樣本只保存在最下面的葉子節點上面,一個節點一個樣本。內部節點不保存樣本數據。而葉子節點除了保存數據之外,還要保存該樣本的優先級,就是圖中的顯示的數字。對於內部節點每一個節點只保存本身的兒子節點的優先級值之和,如圖中內部節點上顯示的數字。函數
這樣保存有什麼好處呢?主要是方便採樣。以上面的樹結構爲例,根節點是42,若是要採樣一個樣本,那麼咱們能夠在[0,42]之間作均勻採樣,採樣到哪一個區間,就是哪一個樣本。好比咱們採樣到了26, 在(25-29)這個區間,那麼就是第四個葉子節點被採樣到。而注意到第三個葉子節點優先級最高,是12,它的區間13-25也是最長的,會比其餘節點更容易被採樣到。oop
若是要採樣兩個樣本,咱們能夠在[0,21],[21,42]兩個區間作均勻採樣,方法和上面採樣一個樣本相似。
相似的採樣算法思想咱們在word2vec原理(三) 基於Negative Sampling的模型第四節中也有講到。
除了經驗回放池,如今咱們的Q網絡的算法損失函數也有優化,以前咱們的損失函數是:$$\frac{1}{m}\sum\limits_{j=1}^m(y_j-Q(\phi(S_j),A_j,w))^2$$
如今咱們新的考慮了樣本優先級的損失函數是$$\frac{1}{m}\sum\limits_{j=1}^mw_j(y_j-Q(\phi(S_j),A_j,w))^2$$
其中$w_j$是第j個樣本的優先級權重,由TD偏差$|\delta(t)|$歸一化獲得。
第三個要注意的點就是當咱們對Q網絡參數進行了梯度更新後,須要從新計算TD偏差,並將TD偏差更新到SunTree上面。
除了以上三個部分,Prioritized Replay DQN和DDQN的算法流程相同。
下面咱們總結下Prioritized Replay DQN的算法流程,基於上一節的DDQN,所以這個算法咱們應該叫作Prioritized Replay DDQN。主流程參考論文<Prioritized Experience Replay>(ICLR 2016)。
算法輸入:迭代輪數$T$,狀態特徵維度$n$, 動做集$A$, 步長$\alpha$,採樣權重係數$\beta$,衰減因子$\gamma$, 探索率$\epsilon$, 當前Q網絡$Q$,目標Q網絡$Q'$, 批量梯度降低的樣本數$m$,目標Q網絡參數更新頻率$C$, SumTree的葉子節點數$S$。
輸出:Q網絡參數。
1. 隨機初始化全部的狀態和動做對應的價值$Q$. 隨機初始化當前Q網絡的全部參數$w$,初始化目標Q網絡$Q'$的參數$w' = w$。初始化經驗回放SumTree的默認數據結構,全部SumTree的S個葉子節點的優先級$p_j$爲1。
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\}$這個五元組存入SumTree
e) $S=S'$
f) 從SumTree中採樣$m$個樣本$\{\phi(S_j),A_j,R_j,\phi(S'_j),is\_end_j\}, j=1,2.,,,m$,每一個樣本被採樣的機率基於$P(j) = \frac{p_j}{\sum\limits_i(p_i)}$,損失函數權重$w_j = (N*P(j))^{-\beta}/\max_i(w_i)$,計算當前目標Q值$y_j$:$$y_j= \begin{cases} R_j& {is\_end_j\; is \;true}\\ R_j + \gamma Q'(\phi(S'_j),\arg\max_{a'}Q(\phi(S'_j),a,w),w')& {is\_end_j\; is \;false} \end{cases}$$
g) 使用均方差損失函數$\frac{1}{m}\sum\limits_{j=1}^mw_j(y_j-Q(\phi(S_j),A_j,w))^2$,經過神經網絡的梯度反向傳播來更新Q網絡的全部參數$w$
h) 從新計算全部樣本的TD偏差$\delta_j = y_j- Q(\phi(S_j),A_j,w)$,更新SumTree中全部節點的優先級$p_j = |\delta_j|$
i) 若是T%C=1,則更新目標Q網絡參數$w'=w$
j) 若是$S'$是終止狀態,當前輪迭代完畢,不然轉到步驟b)
注意,上述第二步的f步和g步的Q值計算也都須要經過Q網絡計算獲得。另外,實際應用中,爲了算法較好的收斂,探索率$\epsilon$須要隨着迭代的進行而變小。
下面咱們給出Prioritized Replay DDQN算法的實例代碼。仍然使用了OpenAI Gym中的CartPole-v0遊戲來做爲咱們算法應用。CartPole-v0遊戲的介紹參見這裏。它比較簡單,基本要求就是控制下面的cart移動使鏈接在上面的pole保持垂直不倒。這個任務只有兩個離散動做,要麼向左用力,要麼向右用力。而state狀態就是這個cart的位置和速度, pole的角度和角速度,4維的特徵。堅持到200分的獎勵則爲過關。
完整的代碼參見個人github: https://github.com/ljpzzz/machinelearning/blob/master/reinforcement-learning/ddqn_prioritised_replay.py, 代碼中的SumTree的結構和經驗回放池的結構參考了morvanzhou的github代碼。
這裏重點講下和第三節中算法描述不一樣的地方,主要是$w_j$的計算。注意到:$$w_j = \frac{ (N*P(j))^{-\beta}}{\max_i(w_i)} = \frac{ (N*P(j))^{-\beta}}{\max_i((N*P(i))^{-\beta})} = \frac{ (P(j))^{-\beta}}{\max_i((P(i))^{-\beta})} =( \frac{p_j}{\min_iP(i)})^{-\beta}$$
所以代碼裏面$w_j$,即ISWeights的計算代碼是這樣的:
def sample(self, n): b_idx, b_memory, ISWeights = np.empty((n,), dtype=np.int32), np.empty((n, self.tree.data[0].size)), np.empty((n, 1)) pri_seg = self.tree.total_p / n # priority segment self.beta = np.min([1., self.beta + self.beta_increment_per_sampling]) # max = 1 min_prob = np.min(self.tree.tree[-self.tree.capacity:]) / self.tree.total_p # for later calculate ISweight if min_prob == 0: min_prob = 0.00001 for i in range(n): a, b = pri_seg * i, pri_seg * (i + 1) v = np.random.uniform(a, b) idx, p, data = self.tree.get_leaf(v) prob = p / self.tree.total_p ISWeights[i, 0] = np.power(prob/min_prob, -self.beta) b_idx[i], b_memory[i, :] = idx, data return b_idx, b_memory, ISWeights
上述代碼的採樣在第二節已經講到。根據樹的優先級的和total_p和採樣數n,將要採樣的區間劃分爲n段,每段來進行均勻採樣,根據採樣到的值落到的區間,決定被採樣到的葉子節點。當咱們拿到第i段的均勻採樣值v之後,就能夠去SumTree中找對應的葉子節點拿樣本數據,樣本葉子節點序號以及樣本優先級了。代碼以下:
def get_leaf(self, v): """ Tree structure and array storage: Tree index: 0 -> storing priority sum / \ 1 2 / \ / \ 3 4 5 6 -> storing priority for transitions Array type for storing: [0,1,2,3,4,5,6] """ parent_idx = 0 while True: # the while loop is faster than the method in the reference code cl_idx = 2 * parent_idx + 1 # this leaf's left and right kids cr_idx = cl_idx + 1 if cl_idx >= len(self.tree): # reach bottom, end search leaf_idx = parent_idx break else: # downward search, always search for a higher priority node if v <= self.tree[cl_idx]: parent_idx = cl_idx else: v -= self.tree[cl_idx] parent_idx = cr_idx data_idx = leaf_idx - self.capacity + 1 return leaf_idx, self.tree[leaf_idx], self.data[data_idx]
除了採樣部分,要注意的就是當梯度更新完畢後,咱們要去更新SumTree的權重,代碼以下,注意葉子節點的權重更新後,要向上回溯,更新全部祖先節點的權重。
self.memory.batch_update(tree_idx, abs_errors) # update priority
def batch_update(self, tree_idx, abs_errors): abs_errors += self.epsilon # convert to abs and avoid 0 clipped_errors = np.minimum(abs_errors, self.abs_err_upper) ps = np.power(clipped_errors, self.alpha) for ti, p in zip(tree_idx, ps): self.tree.update(ti, p)
def update(self, tree_idx, p): change = p - self.tree[tree_idx] self.tree[tree_idx] = p # then propagate the change through tree while tree_idx != 0: # this method is faster than the recursive loop in the reference code tree_idx = (tree_idx - 1) // 2 self.tree[tree_idx] += change
除了上面這部分的區別,和DDQN比,TensorFlow的網絡結構流程中多了一個TD偏差的計算節點,以及損失函數多了一個ISWeights係數。此外,區別不大。
Prioritized Replay DQN和DDQN相比,收斂速度有了很大的提升,避免了一些沒有價值的迭代,所以是一個不錯的優化點。同時它也能夠直接集成DDQN算法,因此是一個比較經常使用的DQN算法。
下一篇咱們討論DQN家族的另外一個優化算法Duel DQN,它將價值Q分解爲兩部分,第一部分是僅僅受狀態但不受動做影響的部分,第二部分纔是同時受狀態和動做影響的部分,算法的效果也很好。
(歡迎轉載,轉載請註明出處。歡迎溝通交流: liujianping-ok@163.com)