【飛槳開發者說】秦浩然,瀋陽人,畢業於東北大學。強化學習技術愛好者。傳統軟件開發領域的前浪,AI領域的後浪。php
提及強化學習的入門,不知道你們是否也是從Sarsa、Q-learning開始,到DQN,再到Policy Gradient,最後到DDPG,一步步走進了強化學習的世界。在學習了這些基礎算法以後,今天咱們就一塊兒來了解一下進階算法SAC,而且看一看如何利用飛槳的PARL強化學習框架方便地把SAC應用到GYM Box2D的月球着陸器環境(LunarLanderContinuous-v2任務)當中去。讓咱們的月球着陸器能夠適應各類狀況,順利着陸。python
本文主要包括如下三部份內容:git
-
SAC算法論文簡介github
-
SAC算法樣例代碼簡介算法
-
介紹如何用SAC算法玩轉月球着陸器網絡
下載安裝命令 ## CPU版本安裝命令 pip install -f https://paddlepaddle.org.cn/pip/oschina/cpu paddlepaddle ## GPU版本安裝命令 pip install -f https://paddlepaddle.org.cn/pip/oschina/gpu paddlepaddle-gpu
SAC算法論文
SAC是Soft Actor-Critic的縮寫,由伯克利人工智能研究實驗室(BAIR)的Tuomas Haarnoja等人,提出於2018年。原文連接:框架
https://arxiv.org/abs/1801.01290函數
不知道讀完了論文的同窗有沒有同感,就是SAC能夠大體當作是DDPG的加強版。那麼論文爲何想要去加強DDPG呢?性能
問題一:爲何須要加強DDPG?學習
論文認爲,有兩大因素使深度強化學習的實際應用變得困難:
-
很是高的採樣複雜度(very high sample complexity)
-
脆弱的收斂性質(brittle convergence properties)
爲了克服這兩個困難,論文提出了SAC,一個基於最大熵強化學習框架的off-policy actor-critic深度強化學習算法。(soft actor-critic, an offpolicy actor-critic deep RL algorithm based on the maximum entropy reinforcement learning framework)
問題二:如何加強DDPG?
在討論這個問題以前,咱們先來講說什麼是最大熵?在DDPG算法中,咱們是以使reward最大化爲目標的。而在SAC中,在使reward最大化的同時,也要最大化策略分佈的熵。這就是最大熵。
有的同窗可能要問了,到底什麼是熵呢?此處咱們不說熱力學熵,也不說信息熵。這裏的熵,我的理解,是用來衡量分佈的隨機程度的。分佈越隨機,熵越大。那麼Actor把最大化熵也做爲優化目標之一,也就意味着,要使策略分佈的隨機性也最大化。策略分佈得更隨機了,算法的穩定性和探索性也就隨之加強了。
問題三:加強了什麼?
那麼,加入熵最大化到底加強了什麼呢。我的理解,就是使得決策的分佈不要像DDPG同樣,趨於集中到一個最優解。而是但願同時存在更多的,一樣優的策略。這樣能夠大幅加強魯棒性,以適應各類各樣的環境。
解決了這三個問題,咱們已經對SAC有了必定的理解。接下來,咱們來看一看論文的實驗數據,來感覺一下SAC的強大。論文給出SAC以及另外幾個主流深度強化學習算法,在六個強化學習任務Benchmark中的訓練曲線,圖中黃色表明SAC。
從圖中的訓練曲線來看,SAC在難度各異的幾個任務中都表現出了良好的穩定性(黃色陰影部分較窄,且集中於實線附近)。在Hopper-v1,HalfCheetah-v1,Ant-v1,Humanoid(rllab)中,SAC最終的return明顯高於其餘算法,尤爲是在最爲複雜的控制空間多達21維的Humanoid (rllab) 中,一騎絕塵,表現出了明顯的優點。
SAC算法樣例
SAC算法這麼好,實現起來會不會很麻煩呢?一塊兒看一看百度飛槳開源深度學習平臺PaddlePaddle的飛槳深度強化學習框架PARL中的SAC樣例。
樣例代碼連接,GitHub中基於PARL的SAC樣例:
https://github.com/PaddlePaddle/PARL/tree/develop/examples/SAC
(GitHub訪問困難的同窗能夠本身搜索一下Gitee,也能夠在example目錄下找到的)
PARL框架的結構大體如上圖所示,採用了層層嵌套的結構,適用於絕大多數強化學習算法。具體到SAC算法的話,最內層Model封裝了Q網絡與策略網絡的網絡結構,經過value()與policy()兩個方法輸出Q值和動做值。向外一層,Algorithm主要封裝了損失函數,經過predict()向外輸出動做值,經過learn()向內更新Model的網絡權重。最外層的Agent主要負責與環境的交互並把從環境獲得的數據餵給Algorithm。
具體實現上,由於使用了PARL框架的結構和封裝好的算法,整個實現顯得很整潔。共分爲三個文件:
-
mujoco_agent.py
-
mujoco_model.py
-
train.py
先看看mujoco_model.py,這裏封裝了兩個Model類,來實現策略網絡和Q網絡,也就是SAC中的A與C(Actor與Critic)。兩個類都繼承了PARL的Model。ActorModel中定義了Actor的網絡結構(策略網絡),實現了policy方法。這個方法根據輸入obs也就是當前外部環境狀態,經過內部的策略網絡,來輸出動做值。
class ActorModel(parl.Model): def __init__(self, act_dim): hid1_size = 400 hid2_size = 300 self.fc1 = layers.fc(size=hid1_size, act='relu') self.fc2 = layers.fc(size=hid2_size, act='relu') self.mean_linear = layers.fc(size=act_dim) self.log_std_linear = layers.fc(size=act_dim) def policy(self, obs): hid1 = self.fc1(obs) hid2 = self.fc2(hid1) means = self.mean_linear(hid2) log_std = self.log_std_linear(hid2) log_std = layers.clip(log_std, min=LOG_SIG_MIN, max=LOG_SIG_MAX) return means, log_std
CriticModel中定義了Critic的網絡結構(Q網絡)而且實現了value方法。這個方法根據輸入obs和策略網絡輸出的動做值,經過內部的Q網絡,來輸出Q值。
class CriticModel(parl.Model): def __init__(self): hid1_size = 400 hid2_size = 300 self.fc1 = layers.fc(size=hid1_size, act='relu') self.fc2 = layers.fc(size=hid2_size, act='relu') self.fc3 = layers.fc(size=1, act=None) self.fc4 = layers.fc(size=hid1_size, act='relu') self.fc5 = layers.fc(size=hid2_size, act='relu') self.fc6 = layers.fc(size=1, act=None) def value(self, obs, act): hid1 = self.fc1(obs) concat1 = layers.concat([hid1, act], axis=1) Q1 = self.fc2(concat1) Q1 = self.fc3(Q1) Q1 = layers.squeeze(Q1, axes=[1]) hid2 = self.fc4(obs) concat2 = layers.concat([hid2, act], axis=1) Q2 = self.fc5(concat2) Q2 = self.fc6(Q2) Q2 = layers.squeeze(Q2, axes=[1]) return Q1, Q2
再看看mujoco_agent.py,這裏封裝了MujocoAgent類,這個類繼承了PARL的Agent。主要實現了predict,sample,learn三大功能。predict用來根據從環境取得的數據也就是環境狀態來輸出一個動做值。sample也一樣根據環境狀態輸出動做值,不一樣的是,這個方法還有必定的機率輸出一個隨機的動做值,用來探索新的動做。learn則實現了根據環境狀態、動做值和回報值等數據來優化model內部網絡參數的功能。
class MujocoAgent(parl.Agent): def predict(self, obs): obs = np.expand_dims(obs, axis=0) act = self.fluid_executor.run( self.pred_program, feed={'obs': obs}, fetch_list=[self.pred_act])[0] return act def sample(self, obs): obs = np.expand_dims(obs, axis=0) act = self.fluid_executor.run( self.sample_program, feed={'obs': obs}, fetch_list=[self.sample_act])[0] return act def learn(self, obs, act, reward, next_obs, terminal): feed = { 'obs': obs, 'act': act, 'reward': reward, 'next_obs': next_obs, 'terminal': terminal } [critic_cost, actor_cost] = self.fluid_executor.run( self.learn_program, feed=feed, fetch_list=[self.critic_cost, self.actor_cost]) self.alg.sync_target() return critic_cost[0], actor_cost[0]
最後看看train.py,這裏就是訓練腳本了。實例化actor和critic後,使用PARL封裝好的SAC算法,層層嵌套最後獲得一個能夠和環境交互的agent實例:
actor = ActorModel(act_dim) critic = CriticModel() algorithm = parl.algorithms.SAC( actor, critic, max_action=max_action, gamma=GAMMA, tau=TAU, actor_lr=ACTOR_LR, critic_lr=CRITIC_LR) agent = MujocoAgent(algorithm, obs_dim, act_dim)
能夠直接用PARL內置的ReplayMemory類實現經驗回放,而且給從PARL導入的ReplayMemory設置參數。
from parl.utils import ReplayMemory rpm = ReplayMemory(MEMORY_SIZE, obs_dim, act_dim)
以上設置OK以後,直接啓動 python train.py 就能夠愉快的開始訓練了。
篇幅有限這裏只說說簡要思路,有興趣的同窗能夠直接去看看源碼。
SAC算法實踐
最後,咱們經過實踐,來看一看SAC算法在GYM Box2D的LunarLanderContinuous-v2任務中的表現。
一樣基於PARL框架,代碼也十分簡潔。與樣例相比,主要在訓練腳本中改動了兩處。
首先引入gym庫
import gym
而後,在main中建立月球着陸器環境
env = gym.make('LunarLanderContinuous-v2')
另外,爲了可視化訓練過程,咱們還加入了reward保存與讀取,以及可視化的代碼
np.save('train_step_list', train_step_list) np.save('train_reward_list', train_reward_list) np.save('evaluate_step_list', evaluate_step_list) np.save('evaluate_reward_list', evaluate_reward_list)
在Notebook文件中加入下面語句,輸出可視化就作好了。
train_step_list = np.load('train_step_list.npy') train_reward_list = np.load('train_reward_list.npy') plt.figure() plt.title('train reward') plt.xlabel('step') plt.ylabel('reward') plt.plot(train_step_list, train_reward_list) plt.grid() plt.show()
在GPU上通過幾個小時的訓練,咱們就能夠獲得可視化輸出以下所示。
從訓練與評估的輸出來看,收斂良好,SAC算法的優點在這個實踐中得以完美體現。
全文回顧
首先,咱們總結了論文的主要內容,分析了SAC算法提出的目的,原理和做用。算法經過引入最大熵使得決策分佈趨於多樣化,從而適應於更爲複雜的實際應用,並取得更好的應用效果。
其次,介紹了PARL框架給出的SAC算法樣例概要。得益於PARL框架的強大,樣例代碼顯得簡潔明晰,易讀易上手。
最後,實踐環節,瞭解了這個框架在LunarLanderContinuous-v2環境中的應用。在SAC算法的幫助下,咱們的登月艙得以平穩準確地着陸於指定位置。而且在訓練過程當中表現出了快速收斂和穩定輸出的良好性能。
實踐代碼連接:
https://aistudio.baidu.com/aistudio/projectdetail/888258
想具體瞭解如何簡潔地實現SAC算法嗎?想親手訓練並玩轉月球着陸器嗎?那就點擊上面的連接查看開源代碼,而後Fork並運行一下吧。
下載安裝命令 ## CPU版本安裝命令 pip install -f https://paddlepaddle.org.cn/pip/oschina/cpu paddlepaddle ## GPU版本安裝命令 pip install -f https://paddlepaddle.org.cn/pip/oschina/gpu paddlepaddle-gpu