Policy Gradient 類的算法是深度強化學習中很重要的一類算法,也是目前最有成效的算法之一。但我在學習的過程當中一直以爲這部分的知識點比較散亂,由於策略梯度類的算法包括不少種變體,通過了各類改進,初學的時候感受就是一團亂麻,迷失在各類各樣的損失函數表達式裏。當看到還有 Actor-Critic 這種結構的時候更是一臉懵逼。python
所以這裏把這類算法作個簡單的梳理,也是爲了幫助本身理清思路,總結的算法包括:Reinforce,受限策略梯度,PPO1 和 PPO2。爲了搞清楚具體的流程,我把三種經常使用算法的流程畫了簡圖,對照流程圖寫程序思路更清晰。ios
核心設計:Policy Gradient 的損失函數
Policy Gradient 類的算法是經過梯度計算去更新策略網絡的參數,所以目標函數就直接設計成指望累積獎勵。這個指望值有多種表達方式,也就對應着不一樣的具體算法對損失函數的不一樣計算方法。算法
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
但由於累積獎勵的指望值沒法直接計算,須要採用蒙特卡洛方法,屢次採樣取近似的平均值。每次採樣都會生成一個 Trajectory,在不斷迭代運行,獲取了大量的 Trajectory 後,使用必定的變換和近似去計算累積獎勵,做爲用於梯度更新的損失函數。網絡
在進行梯度計算時,每每採用 log probablity 的形式,這更易於計算(在Pytorch等框架中也很容易實現)。相關近似計算的推到過程以下:app
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
算法種類和演化:從Reinforce 到 PPO
原始形式 — Reinforce
最先的 Policy Gradient 算法就是蒙特卡洛策略梯度(Reinforce),基本思想就是前面說的隨機採樣,而後用近似的平均累積獎勵去代替指望值。框架
把每一個trajectory的累積獎勵做爲一個總體 函數
損失函數能夠寫成: (這裏的負號是爲了作梯度降低,取了目標函數的負值)性能
Credit Assigment
原始形式的損失函數計算了從 0-T 的全部累積獎勵總和,但對於每一個 time step , 當前採起的 action 只會對 >t 的時刻之後的狀態產生影響,所以能夠只計算將來的累積獎勵,忽略過去的獎勵。 學習
對每一個時間步 t,對應的將來累積獎勵爲: 優化
則損失函數爲 :
注意這裏跟前面的不一樣,Reinforce 的損失函數是直接計算整個 Trajectory 的累積獎勵做爲一個常數,而加入了 Credit Assigment 後,是要分別對每一個 time step 計算一下將來的累積獎勵,而後再乘以 log probability 一塊兒求和的。
這種在 Reinforce 的基礎上作了簡單改進的算法,有時也叫作受限策略梯度。蒙特卡洛策略梯度方法的特色是每次採樣完一整個 Trajectory 後就更新一次策略網絡的參數,若是玩 gym 等環境中的一些典型階段性任務(CartPole,MountCar 等)的話,就是跑完一個 episode 就更新。因此這裏損失函數只須要計算一個 Trajectory就能夠了。
Importance Sampling
前面兩種計算方法都至關因而均勻採樣,並且每次跑完一個 Trajectory 就更新,是典型的隨用隨棄,沒辦法利用以往的經驗。帶來的潛在問題包括梯度更新的震盪過大,訓練完後 agent 表現很不穩定,或者致使收斂困難等。
因而孜孜不倦的計算機大佬們就想到了另外一個辦法,能夠相似於 DDPG 中的 Memory Buffer,對舊的經驗進行循環再利用。即用之前的 old_policy 下采樣獲得的 trajectory ,更新當前的新 policy 參數。既然是屢次採樣,就須要考慮到每一個 Trajectory 的採樣機率不一樣。
當引入採樣機率時,目標函數就能夠寫成:
其中 爲單個 Trajactory 的目標函數,
這裏有一個很管用的 Trick,就是在目標函數中引入 reweighting factor:
(這個公式初看起來是廢話,但把上面的聯合機率展開之後,就會發生神奇的事情。。。)
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
上面的公式稱爲 Surrogate Function,基本思想是利用新舊策略的比例來計算目標函數。跟直接計算 log 機率的方式相比(好比 Reinforce),可讓更新的步子更平緩,避免梯度震盪等問題。
Surrogate Function 的計算能夠用兩種方式,
- 一種是用上面的(將來)累積獎勵,即:
- 另外一種是用優點函數 Advantage Function :
(注意 分別表明新策略和舊策略的參數)
這也是更高級的算法,好比 PPO 計算損失函數的基礎。但 Surrogate Function 存在一個問題,從前面的推到過程也能看出來,一個關鍵的近似計算是在新舊策略相差不大這個基礎上進行的。但若是新舊策略差異較大,就會帶來問題,爲了考慮到這種狀況,須要採起一些辦法對策略更新的幅度進行限制,保證比例近似爲1。這就引出了後面的 PPO。
PPO
- KL 散度
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
KL 散度衡量了兩個分佈之間的差別程度,用新舊策略各自對應分佈之間的 KL 散度做爲懲罰項,能夠對 Surrogate Function 進行約束,限制策略更新的幅度。
但 KL 散度在實踐中較難計算,因此又衍生出了第二種 PPO 的版本。
- Clipped Surrogate function
當 Policy 函數在更新過程當中出現突變,Surrogate Function 對於 reward 的估計就會不許確
The big problem is that at some point we hit a cliff, where the policy changes by a large amount. From the perspective of the surrogate function, the average reward is really great. But the actually average reward is really bad!
用簡單的裁剪法代替 KL 散度,也能夠起到頗有效的限制更新幅度的做用。
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
加入了 CLIP 裁剪後,Surrogate function 就能夠寫爲:
這也就是第二種 PPO 算法的損失函數計算式,也是更經常使用的一種。
三種主要策略梯度類算法的流程對比
這裏對三種經常使用的 Policy Gradient 類算法的流程進行了總結和對比。
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
Reinforce 比較簡單,受限策略梯度是在 Reinforce 的基礎上對損失函數進行了改進。這兩種都是每次採樣完一整個 Trajectory 的數據後,再進行策略更新,屬於 on policy 的更新方式。
而 PPO 則不一樣於前面兩種,採用的是 off policy 離線策略更新。每次重複進行 N 個 Trajectory 的連續採樣,而後把這些數據存入 memory buffer,更新的時候把多個 Trajectory 的樣本打亂,按照 mini-batch 取用,而後進行屢次迭代更新。而每次更新,策略參數就是改變,計算最新策略和舊策略(採樣時的行動策略)的機率之比。而這時用於更新的策略和用於行動的策略就不是同一個了,對於一個固定的採樣批次,行動策略是保持不變的,而更新的策略則在不停變化。所以是典型的離線策略更新。
若是用 Advantage 去計算損失函數,那麼還須要一個 Critic 網絡生成 value function , 這時 PPO 的結構就相似於 Actor-Critic,可是 Critic 輸出的是 V 而不是 Q,更新相對簡單。
近端策略優化算法 PPO
PPO 僞代碼
![](http://static.javashuo.com/static/loading.gif)
![](http://static.javashuo.com/static/loading.gif)
算法關鍵點
- 優點函數:Actor-Critic結構 ,Advantage 的計算
- 直接用 A=G-V
- 使用 GAE 方法計算 ✳
- Clipped Surrogate function ,off policy 離線策略更新
- 加入 entropy 項做爲對 new policy 的限制 ✳
- policy 更新的問題
- 更新的時機:跑完一個episode再更新,仍是收集固定數量的 exp 就能夠更新
- 更新的頻率,更新先後兩個
的差別程度,對梯度計算的影響
- 是否引入梯度剪裁 clip_grad_norm_
- Policy Loss 的最終形式 ✳
- Critic 網絡更新的問題
- 損失函數計算:
- Critic 與 Actor 同步更新,對自身損失函數的影響,是否會致使梯度不穩定
- Actor 和 Critic 網絡結構和超參數配置對學習的影響
PPO 在連續控制場景中的應用關鍵點(contiounus action space)
- 因爲 PPO 屬於隨機策略 stochastic policy,網絡不能直接輸出連續動做的取值(與DDPG不一樣),而是須要輸出action 的機率分佈
- policy 網絡包含兩個輸出層,分別對應着動做機率分佈的均值 和方差 ,每一個輸出層的維度與 action 的維度一致
- action 的生成須要從動做機率分佈中採樣,既先把當前 state 輸入 policy 網絡,獲得動做機率分佈,再從中採樣獲得隨機動做值
def act(self,state):
state = torch.from_numpy(state).float().unsqueeze(0).to(device)
with torch.no_grad():
(mu, sigma) = self.policy(state) # 2d tensors
dist = Normal(mu, sigma)
action = dist.sample()
action_log_prob = dist.log_prob(action)
return action.numpy()[0], action_log_prob.numpy()[0]
4. Surrogate function 中 ratio 的計算 ✳
(mus, sigmas) = self.policy(states)
dists = Normal(mus, sigmas)
new_probs=dists.log_prob(actions)
ratios = torch.exp(new_probs - old_probs)
5. 引入 entropy 項
這篇就寫到這裏,後面還會對繼續深刻討論PPO損失函數的更多改進方式,以及對各類算法在 gym 項目裏進行實戰的過程作個記錄,對比它們的性能差別。(未完待續啦~~)