在強化學習(十三) 策略梯度(Policy Gradient)中,我們講到了基於策略(Policy Based)的強化學習方法的基本思路,並討論了蒙特卡羅策略梯度reinforce演算法。但是由於該演算法需要完整的狀態序列,同時單獨對策略函式進行迭代更新,不太容易收斂。
在本篇我們討論策略(Policy Based)和價值(Value Based)相結合的方法:Actor-Critic演算法。
本文主要參考了Sutton的強化學習書第13章和UCL強化學習講義的第7講。
1. Actor-Critic演算法簡介
Actor-Critic從名字上看包括兩部分,演員(Actor)和評價者(Critic)。其中Actor使用我們上一節講到的策略函式,負責生成動作(Action)並和環境互動。而Critic使用我們之前講到了的價值函式,負責評估Actor的表現,並指導Actor下一階段的動作。
回想我們上一篇的策略梯度,策略函式就是我們的Actor,但是那裡是沒有Critic的,我們當時使用了蒙特卡羅法來計算每一步的價值部分替代了Critic的功能,但是場景比較受限。因此現在我們使用類似DQN中用的價值函式來替代蒙特卡羅法,作為一個比較通用的Critic。
也就是說在Actor-Critic演算法中,我們需要做兩組近似,第一組是策略函式的近似:$$\pi_{\theta}(s,a) = P(a|s,\theta)\approx \pi(a|s)$$
第二組是價值函式的近似,對於狀態價值和動作價值函式分別是:$$\hat{v}(s, w) \approx v_{\pi}(s)$$$$\hat{q}(s,a,w) \approx q_{\pi}(s,a)$$
對於我們上一節講到的蒙特卡羅策略梯度reinforce演算法,我們需要進行改造才能變成Actor-Critic演算法。
首先,在蒙特卡羅策略梯度reinforce演算法中,我們的策略的引數更新公式是:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(s_t,a_t) v_t$$
梯度更新部分中,$\nabla_{\theta}log \pi_{\theta}(s_t,a_t) $是我們的分值函式,不用動,要變成Actor的話改動的是$v_t$,這塊不能再使用蒙特卡羅法來得到,而應該從Critic得到。
而對於Critic來說,這塊是新的,不過我們完全可以參考之前DQN的做法,即用一個Q網路來做為Critic, 這個Q網路的輸入可以是狀態,而輸出是每個動作的價值或者最優動作的價值。
現在我們彙總來說,就是Critic通過Q網路計算狀態的最優價值$v_t$, 而Actor利用$v_t$這個最優價值迭代更新策略函式的引數$\theta$,進而選擇動作,並得到反饋和新的狀態,Critic使用反饋和新的狀態更新Q網路引數$w$, 在後面Critic會使用新的網路引數$w$來幫Actor計算狀態的最優價值$v_t$。
2. Actor-Critic演算法可選形式
在上一節我們已經對Actor-Critic演算法的流程做了一個初步的總結,不過有一個可以注意的點就是,我們對於Critic評估的點選擇是和上一篇策略梯度一樣的狀態價值$v_t$,實際上,我們還可以選擇很多其他的指標來做為Critic的評估點。而目前可以使用的Actor-Critic評估點主要有:
a) 基於狀態價值:這是我們上一節使用的評估點,這樣Actor的策略函式引數更新的法公式是:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(s_t,a_t) V(s,w)$$
b) 基於動作價值:在DQN中,我們一般使用的都是動作價值函式Q來做價值評估,這樣Actor的策略函式引數更新的法公式是:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(s_t,a_t) Q(s,a,w)$$
c) 基於TD誤差:在強化學習(五)用時序差分法(TD)求解中,我們講到了TD誤差,它的表示式是$\delta(t) = R_{t+1} + \gamma V(S_{t+1}) -V(S_t)$或者$\delta(t) = R_{t+1} + \gamma Q(S_{t+1},A_{t+1} ) -Q(S_t,A_t)$, 這樣Actor的策略函式引數更新的法公式是:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(s_t,a_t)\delta(t)$$
d) 基於優勢函式:在強化學習(十二) Dueling DQN中,我們講到過優勢函式A的定義:$A(S,A,w,\beta) = Q(S,A, w, \alpha, \beta) - V(S,w,\alpha) $,即動作價值函式和狀態價值函式的差值。這樣Actor的策略函式引數更新的法公式是:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(s_t,a_t)A(S,A,w,\beta)$$
e) 基於TD($\lambda$)誤差:一般都是基於後向TD($\lambda$)誤差, 在強化學習(五)用時序差分法(TD)求解中也有講到,是TD誤差和效用跡E的乘積。這樣Actor的策略函式引數更新的法公式是:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(s_t,a_t)\delta(t)E_(t)$$
對於Critic本身的模型引數$w$,一般都是使用均方誤差損失函式來做做迭代更新,類似之前DQN系列中所講的迭代方法. 如果我們使用的是最簡單的線性Q函式,比如$Q(s,a ,w) = \phi(s,a)^Tw$,則Critic本身的模型引數$w$的更新公式可以表示為:$$\delta = R_{t+1} + \gamma Q(S_{t+1},A_{t+1} ) -Q(S_t,A_t)$$$$w = w+ \beta\delta\phi(s,a)$$
通過對均方誤差損失函式求導可以很容易的得到上式。當然實際應用中,我們一般不使用線性Q函式,而使用神經網路表示狀態和Q值的關係。
3. Actor-Critic演算法流程
這裡給一個Actor-Critic演算法的流程總結,評估點基於TD誤差,Critic使用神經網路來計算TD誤差並更新網路引數,Actor也使用神經網路來更新網路引數
演算法輸入:迭代輪數$T$,狀態特徵維度$n$, 動作集$A$, 步長$\alpha,\beta$,衰減因子$\gamma$, 探索率$\epsilon$, Critic網路結構和Actor網路結構。
輸出:Actor 網路引數$\theta$, Critic網路引數$w$
1. 隨機初始化所有的狀態和動作對應的價值$Q$. 隨機初始化Critic網路的所有引數$w$。隨機初始化Actor網路的所有引數$\theta$。
2. for i from 1 to T,進行迭代。
a) 初始化S為當前狀態序列的第一個狀態, 拿到其特徵向量$\phi(S)$
b) 在Actor網路中使用$\phi(S)$作為輸入,輸出動作$A$,基於動作$A$得到新的狀態$S'$,反饋$R$。
c) 在Critic網路中分別使用$\phi(S), \phi(S‘’)$作為輸入,得到Q值輸出$V(S), V(S’)$
d) 計算TD誤差$\delta = R +\gamma V(S’) -V(S) $
e) 使用均方差損失函式$\sum\limits(R +\gamma V(S’) -V(S,w))^2$作Critic網路引數$w$的梯度更新
f) 更新Actor網路引數$\theta$:$$\theta = \theta + \alpha \nabla_{\theta}log \pi_{\theta}(S_t,A)\delta$$
對於Actor的分值函式$\nabla_{\theta}log \pi_{\theta}(S_t,A)$,可以選擇softmax或者高斯分值函式。
上述Actor-Critic演算法已經是一個很好的演算法框架,但是離實際應用還比較遠。主要原因是這裡有兩個神經網路,都需要梯度更新,而且互相依賴。但是瞭解這個演算法過程後,其他基於Actor-Critic的演算法就好理解了。
4. Actor-Critic演算法例項
下面我們用一個具體的例子來演示上面的Actor-Critic演算法。仍然使用了OpenAI Gym中的CartPole-v0遊戲來作為我們演算法應用。CartPole-v0遊戲的介紹參見這裡。它比較簡單,基本要求就是控制下面的cart移動使連線在上面的pole保持垂直不倒。這個任務只有兩個離散動作,要麼向左用力,要麼向右用力。而state狀態就是這個cart的位置和速度, pole的角度和角速度,4維的特徵。堅持到200分的獎勵則為過關。
演算法流程可以參考上面的第三節,這裡的分值函式我們使用的是softmax函式,和上一片的類似。完整的程式碼參見我的Github:https://github.com/ljpzzz/machinelearning/blob/master/reinforcement-learning/actor_critic.py
程式碼主要分為兩部分,第一部分是Actor,第二部分是Critic。對於Actor部分,大家可以和上一篇策略梯度的程式碼對比,改動並不大,主要區別在於梯度更新部分,策略梯度使用是蒙特卡羅法計算出的價值$v(t)$,則我們的actor使用的是TD誤差。
在策略梯度部分,對應的位置如下:
self.loss = tf.reduce_mean(self.neg_log_prob * self.tf_vt) # reward guided loss
而我們的Actor對應的位置的程式碼是:
self.exp = tf.reduce_mean(self.neg_log_prob * self.td_error)
此處要注意的是,由於使用的是TD誤差,而不是價值$v(t)$,此處需要最大化self.exp,而不是最小化它,這點和策略梯度不同。對應的Actor程式碼為:
#這裡需要最大化當前策略的價值,因此需要最大化self.exp,即最小化-self.exp self.train_op = tf.train.AdamOptimizer(LEARNING_RATE).minimize(-self.exp)
除此之外,Actor部分的程式碼和策略梯度的程式碼區別並不大。
對於Critic部分,我們使用了類似於DQN的三層神經網路。不過我們簡化了這個網路的輸出,只有一維輸出值,而不是之前DQN使用的有多少個可選動作,就有多少維輸出值。網路結構如下:
def create_Q_network(self): # network weights W1q = self.weight_variable([self.state_dim, 20]) b1q = self.bias_variable([20]) W2q = self.weight_variable([20, 1]) b2q = self.bias_variable([1]) self.state_input = tf.placeholder(tf.float32, [1, self.state_dim], "state") # hidden layers h_layerq = tf.nn.relu(tf.matmul(self.state_input, W1q) + b1q) # Q Value layer self.Q_value = tf.matmul(h_layerq, W2q) + b2q
和之前的DQN相比,這裡還有一個區別就是我們的critic沒有使用DQN的經驗回放,只是使用了反饋和當前網路在下一個狀態的輸出來擬合當前狀態。
對於演算法中Actor和Critic互動的邏輯,在main函式中:
for step in range(STEP): action = actor.choose_action(state) # e-greedy action for train next_state,reward,done,_ = env.step(action) td_error = critic.train_Q_network(state, reward, next_state) # gradient = grad[r + gamma * V(s_) - V(s)] actor.learn(state, action, td_error) # true_gradient = grad[logPi(s,a) * td_error] state = next_state if done: break
大家對照第三節的演算法流程和程式碼應該可以比較容易理清這個過程。但是這個程式很難收斂。因此大家跑了後發現分數總是很低的話是可以理解的。我們需要優化這個問題。
5. Actor-Critic演算法小結
基本版的Actor-Critic演算法雖然思路很好,但是由於難收斂的原因,還需要做改進。
目前改進的比較好的有兩個經典演算法,一個是DDPG演算法,使用了雙Actor神經網路和雙Critic神經網路的方法來改善收斂性。這個方法我們在從DQN到Nature DQN的過程中已經用過一次了。另一個是A3C演算法,使用了多執行緒的方式,一個主執行緒負責更新Actor和Critic的引數,多個輔執行緒負責分別和環境互動,得到梯度更新值,彙總更新主執行緒的引數。而所有的輔執行緒會定期從主執行緒更新網路引數。這些輔執行緒起到了類似DQN中經驗回放的作用,但是效果更好。
在後面的文章中,我們會繼續討論DDPG和A3C。
(歡迎轉載,轉載請註明出處。歡迎溝通交流: liujianping-ok@163.com)