基於飛槳復現強化學習進階算法SAC,讓月球着陸器順利着陸

飛槳開發者說】秦浩然,瀋陽人,畢業於東北大學。強化學習技術愛好者。傳統軟件開發領域的前浪,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
相關文章
相關標籤/搜索