強化學習(十五) A3C

    在強化學習(十四) Actor-Critic中,我們討論了Actor-Critic的算法流程,但是由於普通的Actor-Critic算法難以收斂,需要一些其他的優化。而Asynchronous Advantage Actor-critic(以下簡稱A3C)就是其中比較好的優化算法。本文我們討論A3C的算法原理和算法流程。

    本文主要參考了A3C的論文,以及ICML 2016的deep RL tutorial

1. A3C的引入

    上一篇Actor-Critic算法的代碼,其實很難收斂,無論怎麼調參,最後的CartPole都很難穩定在200分,這是Actor-Critic算法的問題。但是我們還是有辦法去有優化這個難以收斂的問題的。

    回憶下之前的DQN算法,爲了方便收斂使用了經驗回放的技巧。那麼我們的Actor-Critic是不是也可以使用經驗回放的技巧呢?當然可以!不過A3C更進一步,還克服了一些經驗回放的問題。經驗回放有什麼問題呢? 回放池經驗數據相關性太強,用於訓練的時候效果很可能不佳。舉個例子,我們學習下棋,總是和同一個人下,期望能提高棋藝。這當然沒有問題,但是到一定程度就再難提高了,此時最好的方法是另尋高手切磋。

    A3C的思路也是如此,它利用多線程的方法,同時在多個線程裏面分別和環境進行交互學習,每個線程都把學習的成果彙總起來,整理保存在一個公共的地方。並且,定期從公共的地方把大家的齊心學習的成果拿回來,指導自己和環境後面的學習交互。

    通過這種方法,A3C避免了經驗回放相關性過強的問題,同時做到了異步併發的學習模型。

2. A3C的算法優化

    現在我們來看看相比Actor-Critic,A3C到底做了哪些具體的優化。

    相比Actor-Critic,A3C的優化主要有3點,分別是異步訓練框架,網絡結構優化,Critic評估點的優化。其中異步訓練框架是最大的優化。

    我們首先來看這個異步訓練框架,如下圖所示:

    圖中上面的Global Network就是上一節說的共享的公共部分,主要是一個公共的神經網絡模型,這個神經網絡包括Actor網絡和Critic網絡兩部分的功能。下面有n個worker線程,每個線程裏有和公共的神經網絡一樣的網絡結構,每個線程會獨立的和環境進行交互得到經驗數據,這些線程之間互不干擾,獨立運行。

    每個線程和環境交互到一定量的數據後,就計算在自己線程裏的神經網絡損失函數的梯度,但是這些梯度卻並不更新自己線程裏的神經網絡,而是去更新公共的神經網絡。也就是n個線程會獨立的使用累積的梯度分別更新公共部分的神經網絡模型參數。每隔一段時間,線程會將自己的神經網絡的參數更新爲公共神經網絡的參數,進而指導後面的環境交互。

    可見,公共部分的網絡模型就是我們要學習的模型,而線程裏的網絡模型主要是用於和環境交互使用的,這些線程裏的模型可以幫助線程更好的和環境交互,拿到高質量的數據幫助模型更快收斂。

 

    現在我們來看看第二個優化,網絡結構的優化。之前在強化學習(十四) Actor-Critic中,我們使用了兩個不同的網絡Actor和Critic。在A3C這裏,我們把兩個網絡放到了一起,即輸入狀態$S$,可以輸入狀態價值$V$,和對應的策略$\pi$, 當然,我們仍然可以把Actor和Critic看做獨立的兩塊,分別處理,如下圖所示:

    第三個優化點是Critic評估點的優化,在強化學習(十四) Actor-Critic第2節中,我們討論了不同的Critic評估點的選擇,其中d部分講到了使用優勢函數$A$來做Critic評估點,優勢函數$A$在時刻t不考慮參數的默認表達式爲:$$A(S,A,t) = Q(S,A) - V(S)$$

    $Q(S,A)$的值一般可以通過單步採樣近似估計,即:$$Q(S,A) = R + \gamma V(S')$$

    這樣優勢函數去掉動作可以表達爲:$$A(S,t) = R + \gamma V(S') - V(S)$$

    其中$V(S)$的值需要通過Critic網絡來學習得到。

    在A3C中,採樣更進一步,使用了N步採樣,以加速收斂。這樣A3C中使用的優勢函數表達爲:$$A(S,t) = R_t + +  \gamma R_{t+1} +...\gamma^{n-1} R_{t+n-1} + \gamma^n V(S') - V(S)$$

    對於Actor和Critic的損失函數部分,和Actor-Critic基本相同。有一個小的優化點就是在Actor-Critic策略函數的損失函數中,加入了策略$\pi$的熵項,係數爲c, 即策略參數的梯度更新和Actor-Critic相比變成了這樣:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(s_t,a_t)A(S,t) + c\nabla_{\theta}H(\pi(S_t, \theta))$$

    以上就是A3C和Actor-Critic相比有優化的部分。下面我們來總價下A3C的算法流程。

3. A3C算法流程

    這裏我們對A3C算法流程做一個總結,由於A3C是異步多線程的,我們這裏給出任意一個線程的算法流程。

    輸入:公共部分的A3C神經網絡結構,對應參數位$\theta, w$,本線程的A3C神經網絡結構,對應參數$\theta ', w'$, 全局共享的迭代輪數$T$,全局最大迭代次數$T_{max}$, 線程內單次迭代時間序列最大長度$T_{local}$,狀態特徵維度$n$, 動作集$A$, 步長$\alpha,\beta$,熵係數c, 衰減因子$\gamma$, 探索率$\epsilon$

    輸入:公共部分的A3C神經網絡參數$\theta, w$

    1. 更新時間序列$t=1$

    2. 重置Actor和Critic的梯度更新量:$d\theta  \gets 0, dw\gets 0$

    3. 從公共部分的A3C神經網絡同步參數到本線程的神經網絡:$\theta ' =\theta,\;\; w'=w$

    4. $t_{start} = t$,初始化狀態$s_t$

    5. 基於策略$\pi(a_t|s_t;\theta)$選擇出動作$a_t$

    6. 執行動作$a_t$得到獎勵$r_t$和新狀態$s_{t+1}$

    7. $t \gets t+1, T \gets T+1$

    8. 如果$s_t$是終止狀態,或$t-t_{start} == t_{local} $,則進入步驟9,否則回到步驟5

    9. 計算最後一個時間序列位置$s_t$的$Q(s,t)$:$$Q(s,t)= \begin{cases} 0& {terminal\;state}\\ V(s_t,w')& {none\;terminal\;state,bootstrapping} \end{cases}$$

    10. for $i \in (t-1,t-2,...t_{start})$:

      1) 計算每個時刻的$Q(s,i)$:$Q(s,i) = r_i + \gamma Q(s,i+1)$

      2) 累計Actor的本地梯度更新:$$d\theta \gets d\theta + \nabla_{\theta '}log \pi_{\theta'}(s_i,a_i)(Q(s,i)-V(S_i, w')) + c\nabla_{\theta '}H(\pi(s_i, \theta '))$$

      3) 累計Critic的本地梯度更新:$$dw  \gets dw + \frac{\partial (Q(s,i)-V(S_i, w')^2}{\partial w'}$$

    11. 更新全局神經網絡的模型參數:$$\theta = \theta -\alpha d\theta,\;w = w -\beta dw$$

    12. 如果$T > T_{max}$,則算法結束,輸出公共部分的A3C神經網絡參數$\theta, w$,否則進入步驟3

    以上就是A3C算法單個線程的算法流程。

4. A3C算法實例

    下面我們基於上述算法流程給出A3C算法實例。仍然使用了OpenAI Gym中的CartPole-v0遊戲來作爲我們算法應用。CartPole-v0遊戲的介紹參見這裏。它比較簡單,基本要求就是控制下面的cart移動使連接在上面的pole保持垂直不倒。這個任務只有兩個離散動作,要麼向左用力,要麼向右用力。而state狀態就是這個cart的位置和速度, pole的角度和角速度,4維的特徵。堅持到200分的獎勵則爲過關。

    算法代碼大部分參考了莫言的A3C代碼,增加了模型測試部分的代碼並調整了部分模型參數。完整的代碼參見我的Github:https://github.com/ljpzzz/machinelearning/blob/master/reinforcement-learning/a3c.py

    整個算法的Actor和Critic的網絡結構都定義在這裏, 所有的線程中的網絡結構,公共部分的網絡結構都在這裏定義。

    def _build_net(self, scope):
        w_init = tf.random_normal_initializer(0., .1)
        with tf.variable_scope('actor'):
            l_a = tf.layers.dense(self.s, 200, tf.nn.relu6, kernel_initializer=w_init, name='la')
            a_prob = tf.layers.dense(l_a, N_A, tf.nn.softmax, kernel_initializer=w_init, name='ap')
        with tf.variable_scope('critic'):
            l_c = tf.layers.dense(self.s, 100, tf.nn.relu6, kernel_initializer=w_init, name='lc')
            v = tf.layers.dense(l_c, 1, kernel_initializer=w_init, name='v')  # state value
        a_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope=scope + '/actor')
        c_params = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope=scope + '/critic')
        return a_prob, v, a_params, c_params

    所有線程初始化部分,以及本線程和公共的網絡結構初始化部分如下:

    with tf.device("/cpu:0"):
        OPT_A = tf.train.RMSPropOptimizer(LR_A, name='RMSPropA')
        OPT_C = tf.train.RMSPropOptimizer(LR_C, name='RMSPropC')
        GLOBAL_AC = ACNet(GLOBAL_NET_SCOPE)  # we only need its params
        workers = []
        # Create worker
        for i in range(N_WORKERS):
            i_name = 'W_%i' % i   # worker name
            workers.append(Worker(i_name, GLOBAL_AC))

    本線程神經網絡將本地的梯度更新量用於更新公共網絡參數的邏輯在update_global函數中,而從公共網絡把參數拉回到本線程神經網絡的邏輯在pull_global中。

    def update_global(self, feed_dict):  # run by a local
        SESS.run([self.update_a_op, self.update_c_op], feed_dict)  # local grads applies to global net

    def pull_global(self):  # run by a local
        SESS.run([self.pull_a_params_op, self.pull_c_params_op])

    詳細的內容大家可以對照代碼和算法流程一起看。在主函數裏我新加了一個測試模型效果的過程,大家可以試試看看最後的模型效果如何。

5. A3C小結

    A3C解決了Actor-Critic難以收斂的問題,同時更重要的是,提供了一種通用的異步的併發的強化學習框架,也就是說,這個併發框架不光可以用於A3C,還可以用於其他的強化學習算法。這是A3C最大的貢獻。目前,已經有基於GPU的A3C框架,這樣A3C的框架訓練速度就更快了。

    除了A3C, DDPG算法也可以改善Actor-Critic難收斂的問題。它使用了Nature DQN,DDQN類似的思想,用兩個Actor網絡,兩個Critic網絡,一共4個神經網絡來迭代更新模型參數。在下一篇我們討論DDPG算法。

 

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

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