專案背景
20世紀90年代以來,隨著計算機技術和資料量的爆發式增長,算力和神經網路得到了極大的發展,但是距離人們想象中的人工智慧還需要不斷的實踐和創新。
而在神經網路的基礎上出現的強化學習分支,將機器的智慧進行了大幅提升,這種提升來源於強化學習可以讓機器(演算法)自己和環境互動,不斷試錯,從而提升自己的能力。是不是感覺和人類成長的方式很像了?
在強化學習發展的過程中,PPO可以算是最經典的演算法之一,這主要得益於其非常明顯的 3 個優勢:演算法簡潔易理解、適應性強、效果優異。
本次專案中的 Mujoco Half-Cheetah 環境,是在模擬環境中讓一個(事實上是半個)虛擬獵豹學會奔跑。而奔跑這件事,難點在於:動作是連續的,環境的反饋也是連續的。所以要應對這些難點,本次選取了“萬能演算法”——PPO作為嘗試。
我們可以先來看看 PPO 演算法是如何發展而來的。
最早出現的策略最佳化演算法是PG演算法(Policy Gradient)。該演算法由於取樣和最佳化用的是同一套策略(on-policy),所以取樣的效率比較低。PG演算法訓練過程不穩定,容易崩潰(可能由於樣本的關聯度太高造成),比如隨機採集到了差的動作,和環境互動後更差,得到的也是一堆差的反饋資料,惡性迴圈,導致很難從錯誤中恢復,最終演算法崩潰。
如何使PG演算法訓練更穩定呢?
- 引入 Trust region 機制,使產生的 gradient 總是在一個安全的範圍裡(平緩更新),或者用二階的 natural policy gradient 的方法(增加了Fisher Information Matrix的逆矩陣)取代此前用的一階的 SGD,這樣更準確穩定,但是計算複雜度很高。
- 對 TRPO 進一步改進,得到ACKTR演算法。把 Fisher Information Matrix 求逆的過程,用 Kronecker-factored approximation curvature (K-FAC) 替代,訓練速度大幅加快,從而提升 TRPO 的計算效率
- PPO對複雜的 TRPO 演算法進行了簡化。把 Natural gradient 和 TRPO 的 loss function 結合起來,不需要使用複雜的二階最佳化方法,可以在保證效果的前提下,速度更快。
- 最後得到PPO with clipping演算法,有如下兩點優勢:
- 使用 clip 的方式,限制 ratio 的更新,讓 policy output 不會有太激進的變化,讓更新變得穩定,所以大部分的 PPO 演算法都採用了這種形式。
- 僅幾行程式碼即可把經典 PG 演算法改寫成 PPO 的形式,所以很容易實現。
PPO訓練效果展示
(Mujoco HalfCheetah-v2)
- 執行 300,000 步達到 1000 分
- 執行 600,000 步達到 1500 分
- 執行 2,000,000 步達到 2500 分以上
- 最終可以達到 4000-5000 分
PPO 演算法論文閱讀
PPO是ArXiv 2017的一篇論文,Proximal Policy Optimization Algorithms,論文網址:https://arxiv.org/abs/1707.06347
下面我們對論文的關鍵內容進行解讀。
1. 回顧各種強化學習演算法
論文開篇先是回顧了各種強化學習演算法的瓶頸,以及PPO的改進和創新點。
- Q-learning:對於很多簡單的問題無法解決,而且演算法難以理解。
- Vanilla Policy Gradient:資料利用率差;魯棒性(普適性)差;不穩定的問題來源於使用了同一條軌跡進行多步最佳化。
- TRPO:演算法太過於太複雜;引入噪聲以後,和引數共享的結構不相容。
- PPO:資料利用率高;模型可靠度高(適用範圍廣),類似 TRPO;模型簡潔易懂;創新地引入了 clipped probability ratio。
2. 截斷方式
解釋了 PPO 如何透過截斷 probability ratio 達到 TRPO 同樣的效果,同時保持了演算法的簡易性和可理解性。
另外一種方法是用 KL 懲罰因子來代替截斷的方法:
3. 演算法
演算法非常簡潔。同時這裡需要注意:PPO是一個 on-policy 演算法!
4. 效果展示
4.1 對比兩種更新方法
原論文對比了 no clipping or penalty ,Clipping,KL penalty 三種更新方式之間的區別。結果是後面兩個表現更好,另外 Clipping 比 KL 表現更好。
4.2 在連續控制環境中與其他演算法對比
- 可以看得出,在前1000000步過程中,PPO 明顯大多數其他演算法收斂更快;
- 而且適應範圍更廣,在多種任務中皆表現出色。
4.3 複雜的連續控制環境:模擬人類奔跑
可以看到,在複雜的連續控制環境下,PPO取得的效果也非常不錯。
4.4 Atari環境中與其他演算法對比
基於飛槳PARL 的
PPO 演算法程式實現
1 . 相關依賴庫
- python3.5+
- paddlepaddle>=1.6.1
- PARL
- gym
- tqdm
- mujoco-py>=1.50.1.0
1.1 Gym 和 PARL 的介紹
Gym 是個模擬平臺,python 的開源庫,RL 的測試平臺:
- 離散控制場景(動作為確定值):一般使用 atari 環境評估
- 連續控制場景(動作為浮動連續值):一般使用 mujoco 環境遊戲評估
Gym 的核心介面是 enviroment,核心方法:
- observation (object):對環境的一次觀察
- reward (float):獎勵
- done (boolean):代表是否要重置環境(是否達成最終結果/遊戲結束)
- info (dict):用於除錯的診斷資訊
- reset():重置環境的狀態,回到初始環境,以便開始下一次訓練。
- step(action):推進一個時間步長,返回 4 個值:
render():渲染,重新整理環境新一幀的圖形
PARL 是對 Agent 的框架抽象,適用範圍:
- 入門:快速學習和對比不同常用演算法
- 科研:快速復現論文結果,遷移演算法到不同環境調研
- 工業:大規模分散式能力,單機到多機僅需2行程式碼,快速迭代上線
PARL 的實現,基於 3 個類:
- Model(模版+使用者定製):構建前向網路,使用者可以自由的定製自己的網路結構。
- Algorithm:定義了具體的演算法來更新前向網路(Model),也就是透過定義損失函式來更新Model,和演算法相關的計算都放在algorithm中。
- Agent(模版+使用者定製): 負責演算法與環境的互動,在互動過程中把生成的資料提供給Algorithm來更新模型(Model),資料的預處理流程也一般定義在這裡。
1.2 Mujoco 及 mujoco-py 的安裝
MuJoCo 是目前機器人強化學習中最流行的模擬器之一
- 安裝 MuJoCo:
獲取許可證:
https://www.roboti.us/license.html,可以使用30 天試用版本。
- 例如:Mac電腦,點選 OSX 下載檔案 getid_osx;
- 獲取電腦 id;
- 在網站上輸入,立刻就收到了郵件,裡面有兩個附件:LICENSE.txt和mjkey.txt。
您也可以選擇安裝個人版。
官網上有一段提示,意思就是慎重選擇要安裝的電腦,它是繫結電腦的。尤其是學生黨要注意,雖然有 1 年的免費使用,但是千萬別裝錯電腦!
下載原始檔:
- Linux 請選擇:
https://www.roboti.us/download/mujoco200_linux.zip - OS 系統請選擇:
https://www.roboti.us/download/mujoco200_macos.zip
注意:這裡的目錄設定很重要!如果搞不清楚的話,請選擇預設安裝路徑,以免出錯!
- 在終端中mkdir ~/.mujoco 建立安裝目錄;
- 然後把下載的程式檔案解壓,移動到該目錄下
mv mujoco200_macos ~/.mujoco/mujoco200; - 將郵件裡面得到的 key 放到目錄下
mv mjkey.txt ~/.mujoco/mjkey.txt。
安裝 mujoco-py:
官方文件介紹直接使用 pip3 install -U 'mujoco-py<2.1,>=2.0' 即可
安裝過程如果出現各種報錯,可以參考:
強化學習環境:MuJoCo 安裝踩坑記錄(2020年7月18日)
2 . 程式實現
主程式包含 4 個主要的函式:
- run_train_episode:用於訓練的互動程式;
- run_evaluate_episode:評估和測試的互動程式;
- collect_trajectories:透過互動程式,收集軌跡資料,包含了環境狀態,採取的動作,對應的獎勵等;
- build_train_data:把軌跡資料轉化為可以用作訓練的資料。
mujoco_agent:演算法核心:
- 這裡一共建立了 5 個網路:policy_sample_program,policy_predict_program,policy_learn_program,value_predict_program。
- policy_learn:用於同步新的策略模型的引數到舊的策略模型,固定策略模型,然後學習多次後再同步。
- value_learn:資料訓練,更新引數。
def policy_learn(self, obs, actions, advantages): """ Learn policy: 1. Sync parameters of policy model to old policy model 2. Fix old policy model, and learn policy model multi times 3. if use KLPEN loss, Adjust kl loss coefficient: beta """ self.alg.sync_old_policy() all_loss, all_kl = [], [] for _ in range(self.policy_learn_times): loss, kl = self._batch_policy_learn(obs, actions, advantages) all_loss.append(loss) all_kl.append(kl) if self.loss_type == 'KLPEN': # Adative KL penalty coefficient if kl > self.kl_targ * 2: # 如果 KL 過大 self.beta = 1.5 * self.beta # 則將懲罰係數增大 elif kl < self.kl_targ / 2: # 如果 KL 過小 self.beta = self.beta / 1.5 # 則懲罰係數也對應減小 return np.mean(all_loss), np.mean(all_kl) def value_learn(self, obs, value): """ Fit model to current data batch + previous data batch """ data_size = obs.shape[0] if self.value_learn_buffer is None: # 快取為空的時候 obs_train, value_train = obs, value # 讀取狀態值和價值到快取 else:# 如果快取中有資料,則把新資料和舊的資料進行拼接 obs_train = np.concatenate([obs, self.value_learn_buffer[0]]) value_train = np.concatenate([value, self.value_learn_buffer[1]]) self.value_learn_buffer = (obs, value) # 更新快取為新資料 all_loss = [] for _ in range(self.value_learn_times): random_ids = np.arange(obs_train.shape[0]) # 獲取 id 列表 np.random.shuffle(random_ids) # 隨機打亂訓練資料 shuffle_obs_train = obs_train[random_ids] shuffle_value_train = value_train[random_ids] start = 0 while start < data_size: end = start + self.value_batch_size # 更新價值網路(新資料+舊資料) value_loss = self._batch_value_learn( shuffle_obs_train[start:end, :], shuffle_value_train[start:end]) all_loss.append(value_loss) start += self.value_batch_size return np.mean(all_loss)
搭建網路結構:
搭建策略網路 policy model 和價值網路 value model,都用了 4 個全連線層。基於飛槳的架構,搭建的神經網路模型非常簡單清晰。
class PolicyModel(PARL.Model): def __init__(self, obs_dim, act_dim, init_logvar): self.obs_dim = obs_dim self.act_dim = act_dim hid1_size = obs_dim * 10 hid3_size = act_dim * 10 hid2_size = int(np.sqrt(hid1_size * hid3_size)) self.lr = 9e-4 / np.sqrt(hid2_size) self.fc1 = layers.fc(size=hid1_size, act='tanh') self.fc2 = layers.fc(size=hid2_size, act='tanh') self.fc3 = layers.fc(size=hid3_size, act='tanh') self.fc4 = layers.fc(size=act_dim, act='tanh') self.logvars = layers.create_parameter( shape=[act_dim], dtype='float32', default_initializer=fluid.initializer.ConstantInitializer( init_logvar)) def policy(self, obs): hid1 = self.fc1(obs) hid2 = self.fc2(hid1) hid3 = self.fc3(hid2) means = self.fc4(hid3) logvars = self.logvars() return means, logvars def sample(self, obs): means, logvars = self.policy(obs) sampled_act = means + ( layers.exp(logvars / 2.0) * # stddev layers.gaussian_random(shape=(self.act_dim, ), dtype='float32')) return sampled_act
完整程式碼可見 AI Studio 公開專案:
https://aistudio.baidu.com/aistudio/projectdetail/644829
文章小結
1. 關於模型
- PPO模型確實非常強大,穩定性很好,最重要的是魯棒性強,適用性廣。所以該演算法被 OpenAI 推出後,受到了業界的極大關注,後面強化學習的進一步研究工作,也通常都會把 PPO 作為參照物件和對比基準。
- PPO提出的創新點,比如截斷等操作,其實衍生性很好,不僅限於強化學習領域,其他深度學習方向也可以借鑑受益。
- 最後不得不提的是百度的 PaddlePaddle 和 PARL,這是一套完整的深度學習和強化學習框架。從初期學習,到中期實踐,到後期落地,這套框架給開發者和研究者帶來了極大的便利。PARL 作為專精於強化學習的框架,免除了我們重複造輪子的痛苦,可以專注於不同演算法的核心優劣對比和研究,確實節約了我們大量的時間。就我自己而言,以後做相關的研究和論文撰寫,PaddlePaddle 和 PARL 一定會是我的首選!
2.關於學習過程
- 入門強化學習時,發現最難搞的是理解核心的理念,比如關於獎勵的概念,如何讓獎勵可以不斷促使演算法自我學習和進化,需要反覆去琢磨和思考。
- 學習過程中,相信最經典的理解障礙就是 On-policy 和 Off-policy 的理念對比了,初次接觸的人可不容易搞明白,所幸當時科老師的課講得細緻清晰,生動形象,讓這些常理來說很困難的部分可以迎刃而解。
- 有了基礎的強化學習理念,往後就是對比和學習主流的強化學習演算法,這個時候網上的教程其實就稍顯匱乏了,對於想要深入研究的同學,可以考慮從論文研讀和程式碼實踐兩個板塊同時入手,由此可對各種演算法有深入的理解。
3.關於PPO訓練過程
個人認為,強化學習的程式碼實踐,包含以下幾個重要的組成部分,每個部分都有其難點。
- 實驗環境搭建
初期我們會直接用 Gym 庫裡面提供的一些環境,幾乎不費力氣去搭建和呼叫,只要把 Gym 安裝完成即可輕鬆實現。可越往後,當你想嘗試各種高難度的環境時,比如一些機器人模擬環境,飛行器模擬環境,交通控制環境,甚至還有一些股票交易環境的時候,光是安裝一個環境達到成功執行都是非常費時費力的。
這裡實踐 PPO,沒有選用簡單的 Gym 環境,挑戰了一下安裝 Mujoco,真的是一把辛酸淚,弄了整整一天才勉強跑通,期間無數次在 github 上查詢各種 issue,然後進行各種嘗試,各種重灌。我只想說,那些可以一次安裝成功的朋友真的是人品爆發才行!而且大家要有心理準備,以後安裝別的環境只會更難~~。
- 演算法程式碼構建
如果是從 0 開始手動搭建程式碼,在現代社會幾乎是無法想象的事情,所以站在巨人的肩膀上才是最重要的。而有了 PARL 這樣的成熟框架,可以讓我們有了搭建的基礎,讓演算法程式碼構建稍稍輕鬆一點點!
把環境的互動,演算法部分,學習部分,都分別做了基礎框架,這不但讓呼叫更加簡單,其實也是讓我們學習過程中對於整個深度學習的過程有了分塊的清晰認知。
- 訓練除錯
由於 PPO 演算法的特性,決定了其穩定性比較好,所以不像 PG 這樣的強化學習演算法,常常遇到無法收斂的情況,很多時候又需要拼人品~~。PPO 就好多了,在訓練前,設定好定期儲存模型,然後就開始走起。另外要記得儲存訓練資料,用於後面繪圖以看清楚整個訓練過程。
開始訓練 100,000 步左右,看看之前有沒有收斂的趨勢,沒有的話就停掉重新除錯程式,如果基本的演算法和架構沒有問題,可以嘗試降低學習率來除錯。
如果收斂趨勢明顯,那就可以放著讓電腦訓練,睡一覺以後看最後訓練效果。可以看到最終的收斂曲線,也就是獲得獎勵上漲的趨勢如本文開頭的圖示。調取訓練過程中儲存的中間模型,放到測試程式碼中開啟渲染執行,就可以看到我們的小獵豹成長的過程喜人,從待著不動,到亂七八糟地晃動,再到逐漸可以移動,最後可以比較順暢地完成奔跑,整個過程還是讓人很有成就感的!
飛槳PaddlePaddle課程連結:
百度架構師手把手教深度學習:
https://aistudio.baidu.com/aistudio/course/introduce/888
強化學習7日打卡營:
https://aistudio.baidu.com/aistudio/course/introduce/1335
如果您加入官方 QQ 群,您將遇上大批志同道合的深度學習同學。官方 QQ 群:
1108045677。
如果您想詳細瞭解更多飛槳的相關內容,請參閱以下文件。
·官網地址·
https://www.paddlepaddle.org.cn/
·飛槳PARL開源專案地址·
GitHub:
https://github.com/PaddlePaddle/PARL
Gitee: