您當前的位置:首頁 > 詩詞

理解Actor-Critic的關鍵是什麼?(附程式碼及程式碼分析)

作者:由 張斯俊 發表于 詩詞時間:2020-03-05

現在,我們終於開始學習頂頂大名的Actor-Critic了!

雖然AC演算法是如此有名,又如此重要,我們終於要開始學習了,是不是有點小激動,但又怕學不動呢?

如果前面的基礎已經紮實了,理解和實現Actor-Critic一點也不難。

什麼是AC

關於AC,很多書籍和教程都說AC是DQN和PG的結合。個人覺得道理是怎麼個道理,但其實是不夠清晰,也很容易產生誤讀,甚至錯誤理解AC。至於是在哪裡容易產生誤讀,我會在講解的時候為你說明。

照我的觀點來說,PG利用帶權重的梯度下降方法更新策略,而獲得權重的方法是蒙地卡羅計算G值。

蒙地卡羅需要完成整個遊戲過程,直到最終狀態,才能透過回溯計算G值。這使得PG方法的效率被限制。

那我們可不可以更快呢?相信大家已經想到了,那就是改為TD。

但改為TD還有一個問題需要解決,就是:在PG,我們需要計算G值;那麼在TD中,我們應該怎樣估算每一步的Q值呢?

有沒有發現?

熟悉的問題:其實這個問題我們在學DQN的時候也遇到過。 熟悉的套路:我們用上萬能的神經網路解決。

也就是說,Actor-Critic,其實是用了兩個網路:

兩個網路有一個共同點,輸入狀態S: 一個輸出策略,負責選擇動作,我們把這個網路成為Actor; 一個負責計算每個動作的分數,我們把這個網路成為Critic。

大家可以形象地想象為,Actor是舞臺上的舞者,Critic是臺下的評委。

Actor在臺上跳舞,一開始舞姿並不好看,Critic根據Actor的舞姿打分。Actor透過Critic給出的分數,去學習:如果Critic給的分數高,那麼Actor會調整這個動作的輸出機率;相反,如果Critic給的分數低,那麼就減少這個動作輸出的機率。

所以依我的觀點來看,AC不是PG+DQN,而應該說AC是TD版本的PG。

TD-error

注意:這是AC的重點。很多同學在這裡會和DQN搞亂,也就是容易產生誤解的地方。在DQN預估的是Q值,在AC中的Critic,估算的是V值。

你可能會說,為什麼不是Q值呢?說好是給動作評價呢。

我們可以直接用network估算的Q值作為更新值,但效果會不太好。

原因我們可以看下圖:

理解Actor-Critic的關鍵是什麼?(附程式碼及程式碼分析)

假設我們用Critic網路,預估到S狀態下三個動作A1,A2,A3的Q值分別為1,2,10。

但在開始的時候,我們採用平均策略,於是隨機到A1。於是我們用策略梯度的帶權重方法更新策略,這裡的權重就是Q值。

於是策略會更傾向於選擇A1,意味著更大機率選擇A1。結果A1的機率就持續升高。。。

這就掉進了正數陷阱。我們明明希望A3能夠獲得更多的機會,最後卻是A1獲得最多的機會。

這是為什麼呢?

這是因為Q值用於是一個正數,如果權重是一個正數,那麼我們相當於提高對應動作的選擇的機率。權重越大,我們調整的幅度將會越大。

其實當我們有足夠的迭代次數,這個是不用擔心這個問題的。因為總會有機會抽中到權重更大的動作,因為權重比較大,抽中一次就能提高很高的機率。

但在強化學習中,往往沒有足夠的時間讓我們去和環境互動。這就會出現由於運氣不好,使得一個

很好

的動作沒有被取樣到的情況發生。

要解決這個問題,我們可以透過減去一個baseline,令到權重有正有負。而通常這個baseline,我們選取的是權重的平均值。減去平均值之後,值就變成有正有負了。

而Q值的期望(均值)就是V。

理解Actor-Critic的關鍵是什麼?(附程式碼及程式碼分析)

所以我們可以得到更新的權重:Q(s,a)-V(s)

隨之而來的問題是,這就需要兩個網路來估計Q和V了。但馬爾科夫告訴我們,很多時候,V和Q是可以互相換算的。

Q(s,a)用gamma * V(s‘) + r 來代替,於是整理後就可以得到:

gamma * V(s’) + r - V(s) —— 我們把這個差,叫做TD-error

這個和之前DQN的更新公式非常像,只不過DQN的更新用了Q,而TD-error用的是V。

眼尖的同學可能已經發現,如果Critic是用來預估V值,而不是原來討論的Q值。那麼,這個TD-error是用來更新Critic的loss了!

沒錯,Critic的任務就是讓TD-error儘量小。然後TD-error給Actor做更新。

現在我們再總結一下TD-error的知識:

1。 為了避免正數陷阱,我們希望Actor的更新權重有正有負。因此,我們把Q值減去他們的均值V。有:Q(s,a)-V(s)

2。 為了避免需要預估V值和Q值,我們希望把Q和V統一;由於Q(s,a) = gamma * V(s‘) + r - V(s)。所以我們得到TD-error公式: TD-error = gamma * V(s’) + r - V(s)

3。 TD-error就是Actor更新策略時候,帶權重更新中的權重值;

4。 現在Critic不再需要預估Q,而是預估V。而根據馬可洛夫鏈所學,我們知道TD-error就是Critic網路需要的loss,也就是說,Critic函式需要最小化TD-error。

大家看,其實AC沒有很難,關鍵是對TD-error的理解。理解了TD-error,就能串起Actor和Critic兩者。

演算法

定義兩個network:Actor 和 Critic

j進行N次更新。

從狀態s開始,執行動作a,得到獎勵r,進入狀態s‘

記錄的資料。

把輸入到Critic,根據公式: TD-error = gamma * V(s’) + r - V(s) 求 TD-error,並縮小TD-error

把輸入到Actor,計算策略分佈 。

理解Actor-Critic的關鍵是什麼?(附程式碼及程式碼分析)

程式碼解釋

以下,我們就用tensorflow的AC程式碼作為示例,一起看看DQN應該如何實現。

tensorflow示例程式碼:

如果一時間看程式碼有困難,可以看我的帶註釋版本。希望能幫助到你。

更新流程

我們可以把更新流程和PG做對比: 在PG,智慧體需要從頭一直跑到尾,直到最終狀態才開始進行學習。 在AC,智慧體採用是每步更新的方式。

但要注意,我們需要先更新Critic,並計算出TD-error。再用TD-error更新Actor。

在示例程式碼中,Actor 和 Critic兩個網路是完全分離的。但在實做得時候,很多時候我們會把Actor和Critic公用網路前面的一些層。例如state是一張圖片,我們可以先通過幾層的CNN進行特徵的提取,再分別輸出Actor的動作機率分佈和Critic的V值。

理解Actor-Critic的關鍵是什麼?(附程式碼及程式碼分析)

修改reward

if done: r = -20

在更新流程中,有這麼一行程式碼。意思是:如果已經到達最終狀態,那麼獎勵直接扣20點。這是為什麼呢?

首先我們要明確,這個CartPole遊戲最終目的,是希望堅持越久越好。所以大家可以想象這麼一個過程:在某個瀕死狀態s下,選擇動作a,進入結束狀態s,收穫r,在CartPole中,這個reward為 1。0。

但我們並不希望遊戲結束,我們希望智慧體能在瀕死狀態下“力挽狂瀾”!

於是我們把reward減去20,相當於是對瀕死狀態下,選擇動作a的強烈不認同。透過-20大幅減少動作a出現的機率。

再進一步,reward會向前傳播,讓智慧體瀕死狀態之前狀態時,不選擇會進入瀕死狀態的動作,努力避免進入瀕死狀態。

所以我們說,reward是一個主觀因素很強的數值。當環境返回的reward不能滿足的我們的時候,我們完全可以進行reward的修改,讓智慧體更快學習。

Critic 的學習

def learn(self, s, r, s_):

v_ = self。model(np。array([s_]))

with tf。GradientTape() as tape:

v = self。model(np。array([s]))

## TD_error = r + lambd * V(newS) - V(S)

td_error = r + LAMBDA * v_ - v

loss = tf。square(td_error)

grad = tape。gradient(loss, self。model。trainable_weights)

self。optimizer。apply_gradients(zip(grad, self。model。trainable_weights))

這裡我們重點關注td-error的計算。

Actor 的學習

Actor 的學習本質上是PG的更新,也就是加權的學習。程式碼中用了cross_entropy_reward_loss函式。

_exp_v = tl。rein。cross_entropy_reward_loss(logits=_logits, actions=[a], rewards=td[0])

其實這個函式就是我們在PG種的帶引數交叉熵損失函式。

def cross_entropy_reward_loss(logits, actions, rewards, name=None):

cross_entropy = tf。nn。sparse_softmax_cross_entropy_with_logits(labels=actions, logits=logits, name=name)

return tf。reduce_sum(tf。multiply(cross_entropy, rewards))

其功能是完全一樣的,只不過tensorlayer把這個函式提出出來而已。

總結

要掌握AC演算法,關鍵是理解TD-error的來歷。不然Critic要最小化TD-error,Actor要把TD-error帶引數更新,聽上去就會很懵。

理解TD-error本質是Q(s,a)-V(s)來的,而Q(s,a)轉為由V(s‘)+r的形式表示,整個思路就會非常清晰。

AC很強,強在它就又無限的潛力。AC出來之後,就有很多厲害的演算法基於AC框架,這就包括了我們下篇要說的,OpenAI都拿它做基準的PPO演算法。

===========你的支援,就是在下努力的原動力===========

如果專欄對你有用,請點贊並關注在下喔。如果發現有問題,也可以在文章下留言。

你的每一點關注,都是在下的繼續努力的動力來源!感激!

標簽: td  Error  critic  actor  AC