本文將圍繞壹個實際的問題進行介紹:應用強化學習的社區可以如何從對腳本和單個案例的收集更進壹步,實現壹個強化學習 API——壹個用於強化學習的 tf-learn 或 skikit-learn?在討論 TensorForce 框架之前,我們將談壹談啟發了這個項目的觀察和思想。如果妳只想了解這個 API,妳可以跳過這壹部分。我們要強調壹下:這篇文章並不包含對深度強化學習本身的介紹,也沒有提出什麽新模型或談論最新的最佳算法,因此對於純研究者來說,這篇文章可能並不會那麽有趣。
開發動機
假設妳是計算機系統、自然語言處理或其它應用領域的研究者,妳壹定對強化學習有壹些基本的了解,並且有興趣將深度強化學習(deep RL)用來控制妳的系統的某些方面。
對深度強化學習、DQN、vanilla 策略梯度、A3C 等介紹文章已經有很多了,比如 Karpathy 的文章對策略梯度方法背後的直觀思想就進行了很好的描述。另外,妳也能找到很多可以幫助上手的代碼,比如 OpenAI 上手智能體、rllab以及 GitHub 上許多特定的算法。
但是,我們發現在強化學習的研究框架開發和實際應用之間還存在壹個巨大的鴻溝。在實際應用時,我們可能會面臨如下的問題:
強化學習邏輯與模擬句柄的緊密耦合:模擬環境 API 是非常方便的,比如,它們讓我們可以創建壹個環境對象然後將其用於壹個 for 循環中,同時還能管理其內部的更新邏輯(比如:通過收集輸出特征)。如果我們的目標是評估壹個強化學習思想,那麽這就是合理的,但將強化學習代碼和模擬環境分開則要艱難得多。它還涉及到流程控制的問題:當環境就緒後,強化學習代碼可以調用它嗎?或者當環境需要決策時,它會調用強化學習智能體嗎?對於在許多領域中實現的應用強化學習庫,我們往往需要後者。 固定的網絡架構:大多數實現案例都包含了硬編碼的神經網絡架構。這通常並不是壹個大問題,因為我們可以很直接地按照需求加入或移除不同的網絡層。盡管如此,如果有壹個強化學習庫能夠提供聲明式接口的功能,而無需修改庫代碼,那麽情況就會好得多。此外,在有的案例中,修改架構(出人意外地)要難得多,比如當需要管理內部狀態的時候(見下文)。 不兼容狀態/動作接口:很多早期的開源代碼都使用了流行的 OpenAI Gym 環境,具有平坦的狀態輸入的簡單接口和單個離散或連續動作輸出。但 DeepMind Lab 則使用了壹種詞典格式,壹般具有多個狀態和動作。而 OpenAI Universe 則使用的是命名關鍵事件(named key events)。理想情況下,我們想讓強化學習智能體能處理任意數量的狀態和動作,並且具有潛在的不同類型和形狀。比如說,TensorForce 的壹位作者正在 NLP 中使用強化學習並且想要處理多模態輸入,其中壹個狀態在概念上包含兩個輸入——壹張圖像和壹個對應的描述。 不透明的執行設置和性能問題:寫 TensorFlow 代碼的時候,我們很自然地會優先關註邏輯。這會帶來大量重復/不必要的運算或實現不必要的中間值。此外,分布式/異步/並行強化學習的目標也有點不固定,而分布式 TensorFlow 需要對特定的硬件設置進行壹定程度的人工調節。同樣,如果最終有壹種執行配置只需要聲明可用設備或機器,然後就能在內部處理好其它壹切就好了,比如兩臺有不同 IP 的機器可以運行異步 VPG。明確壹下,這些問題並不是要批評研究者寫的代碼,因為這些代碼本來就沒打算被用作 API 或用於其它應用。在這裏我們介紹的是想要將強化學習應用到不同領域中的研究者的觀點。
TensorForce API
TensorForce 提供了壹種聲明式接口,它是可以使用深度強化學習算法的穩健實現。在想要使用深度強化學習的應用中,它可以作為壹個庫使用,讓用戶無需擔心所有底層的設計就能實驗不同的配置和網絡架構。我們完全了解當前的深度強化學習方法往往比較脆弱,而且需要大量的微調,但這並不意味著我們還不能為強化學習解決方案構建通用的軟件基礎設施。
TensorForce 並不是原始實現結果的集合,因為這不是研究模擬,要將原始實現用在實際環境的應用中還需要大量的工作。任何這樣的框架都將不可避免地包含壹些結構決策,這會使得非標準的事情變得更加惱人(抽象泄漏(leaky abstractions))。這就是為什麽核心強化學習研究者可能更傾向於從頭打造他們的模型的原因。使用 TensorForce,我們的目標是獲取當前最佳研究的整體方向,包含其中的新興見解和標準。
接下來,我們將深入到 TensorForce API 的各個基本方面,並討論我們的設計選擇。
創建和配置智能體
這個示例中的狀態和動作是更壹般的狀態/動作的短形式(short-form)。比如由壹張圖像和壹個描述構成多模態輸入按如下方式定義。類似地,也可以定義多輸出動作。註意在整個代碼中,單個狀態/動作的短形式必須被持續不斷地用於與智能體的通信。
配置參數依賴於所用的基本智能體和模型。
TensorForce 目前提供了以下強化學習算法:
隨機智能體基線(RandomAgent) 帶有 generalized advantage estimation 的 vanilla 策略梯度(VPGAgent) 信任區域策略優化(TRPOAgent) 深度 Q 學習/雙深度 Q 學習(DQNAgent) 規範化的優勢函數(NAFAgent) 對專家演示的深度 Q 學習(DQFDAgent) Asynchronous Advantage Actor-Critic(A3C)(可以隱含地通過 distributed 使用)最後壹項的意思是說並沒有 A3CAgent 這樣的東西,因為 A3C 實際上描述的是壹種異步更新的機制,而不是壹種特定的智能體。因此,使用分布式 TensorFlow 的異步更新機制是通用 Model 基類的壹部分,所有智能體都衍生於此。正如論文《Asynchronous Methods for Deep Reinforcement Learning》中描述的那樣,A3C 是通過為 VPGAgent 設置 distributed flag 而隱含地實現的。應該指出,A3C 並不是對每種模型而言都是最優的分布式更新策略(對壹些模型甚至完全沒意義),我們將在本文結尾處討論實現其它方法(比如 PAAC)。重要的壹點是要在概念上將智能體和更新語義的問題與執行語義區分開。
我們還想談談模型(model)和智能體(agent)之間的區別。Agent 類定義了將強化學習作為 API 使用的接口,可以管理傳入觀察數據、預處理、探索等各種工作。其中兩個關鍵方法是 agent.act(state) 和 agent.observe(reward, terminal)。agent.act(state) 返回壹個動作,而 agent.observe(reward, terminal) 會根據智能體的機制更新模型,比如離策略記憶回放(MemoryAgent)或在策略批處理(BatchAgent)。註意,要讓智能體的內在機制正確工作,必須交替調用這些函數。Model 類實現了核心強化學習算法,並通過 get_action 和 update 方法提供了必要的接口,智能體可以在相關點處內在地調用。比如說,DQNAgent 是壹個帶有 DQNModel 和額外壹行(用於目標網絡更新)的 MemoryAgent 智能體。
神經網絡配置
強化學習的壹個關鍵問題是設計有效的價值函數。在概念上講,我們將模型看作是對更新機制的描述,這有別於實際更新的東西——在深度強化學習的例子中是指壹個(或多個)神經網絡。因此,模型中並沒有硬編碼的網絡,而是根據配置不同的實例化。
在上面的例子中,我們通過編程創造了壹個網絡配置作為描述每壹層的詞典列表。這樣的配置也可以通過 JSON 給出,然後使用壹個效用函數將其變成壹個網絡構建器(network constructor)。
默認的激活層是 relu,但也還有其它激活函數可用(目前有 elu、selu、softmax、tanh 和 sigmoid)。此外也可以修改層的其它性質.
我們選擇不使用已有的層實現(比如來自 tf.layers),從而能對內部運算施加明確的控制,並確保它們能與 TensorForce 的其余部分正確地整合在壹起。我們想要避免對動態 wrapper 庫的依賴,因此僅依賴於更低層的 TensorFlow 運算。
我們的 layer 庫目前僅提供了非常少的基本層類型,但未來還會擴展。
到目前為止,我們已經給出了 TensorForce 創建分層網絡的功能,即壹個采用單壹輸入狀態張量的網絡,具有壹個層的序列,可以得出壹個輸出張量。但是在某些案例中,可能需要或更適合偏離這樣的層堆疊結構。最顯著的情況是當要處理多個輸入狀態時,這是必需的,使用單個處理層序列無法自然地完成這壹任務。
我們目前還沒有為自動創建對應的網絡構建器提供更高層的配置接口。因此,對於這樣的案例,妳必須通過編程來定義其網絡構建器函數,並像之前壹樣將其加入到智能體配置中。
內部狀態和 Episode 管理
和經典的監督學習設置(其中的實例和神經網絡調用被認為是獨立的)不同,強化學習壹個 episode 中的時間步取決於之前的動作,並且還會影響後續的狀態。因此除了其每個時間步的狀態輸入和動作輸出,可以想象神經網絡可能有內部狀態在 episode 內的對應於每個時間步的輸入/輸出。下圖展示了這種網絡隨時間的工作方式:
這些內部狀態的管理(即在時間步之間前向傳播它們和在開始新 episode 時重置它們)可以完全由 TensorForce 的 agent 和 model 類處理。註意這可以處理所有的相關用例(在 batch 之內壹個 episode,在 batch 之內多個 episode,在 batch 之內沒有終端的 episode)。
在這個示例架構中,稠密層的輸出被送入壹個 LSTM cell,然後其得出該時間步的最終輸出。當向前推進該 LSTM 壹步時,其內部狀態會獲得更新並給出此處的內部狀態輸出。對於下壹個時間步,網絡會獲得新狀態輸入及這個內部狀態,然後將該 LSTM 又推進壹步並輸出實際輸出和新的內部 LSTM 狀態,如此繼續……
對於帶有內部狀態的層的自定義實現,該函數不僅必須要返回該層的輸出,而且還要返回壹個內部狀態輸入占位符的列表、對應的內部狀態輸出張量和壹個內部狀態初始化張量列表(這些都長度相同,並且按這個順序)。
預處理狀態
我們可以定義被應用於這些狀態(如果指定為列表的詞典,則可能是多個狀態)的預處理步驟.
這個 stack 中的每壹個預處理器都有壹個類型,以及可選的 args 列表和/或 kwargs 詞典。比如 sequence 預處理器會取最近的四個狀態(即:幀)然後將它們堆疊起來以模擬馬爾可夫屬性。隨便壹提:在使用比如之前提及的 LSTM 層時,這顯然不是必需的,因為 LSTM 層可以通過內部狀態建模和交流時間依賴。
探索
探索可以在 configuration 對象中定義,其可被智能體應用到其模型決定所在的動作上(以處理多個動作,同樣,會給出壹個規範詞典)。比如,為了使用 Ornstein-Uhlenbeck 探索以得到連續的動作輸出,下面的規範會被添加到配置中。
用 Runner 效用函數使用智能體
讓我們使用壹個智能體,這個代碼是在我們測試環境上運行的壹個智能體,我們將其用於連續積分——壹個為給定智能體/模型的工作方式驗證行動、觀察和更新機制的最小環境。註意我們所有的環境實現(OpenAI Gym、OpenAI Universe、DeepMind Lab)都使用了同壹個接口,因此可以很直接地使用另壹個環境運行測試。
Runner 效用函數可以促進壹個智能體在壹個環境上的運行過程。給定任意壹個智能體和環境實例,它可以管理 episode 的數量,每個 episode 的最大長度、終止條件等。Runner 也可以接受 cluster_spec 參數,如果有這個參數,它可以管理分布式執行(TensorFlow supervisors/sessions/等等)。通過可選的 episode_finished 參數,妳還可以周期性地報告結果,還能給出在最大 episode 數之前停止執行的指標。
正如在引言中說的壹樣,在壹個給定應用場景中使用 runner 類取決於流程控制。如果使用強化學習可以讓我們合理地在 TensorForce 中查詢狀態信息(比如通過壹個隊列或網絡服務)並返回動作(到另壹個隊列或服務),那麽它可被用於實現環境接口,並因此可以使用(或擴展)runner 效用函數。
更常見的情況可能是將 TensorForce 用作驅動控制的外部應用庫,因此無法提供壹個環境句柄。對研究者來說,這可能無足輕重,但在計算機系統等領域,這是壹個典型的部署問題,這也是大多數研究腳本只能用於模擬,而無法實際應用的根本原因。
另外值得提及的壹點是聲明式的中心配置對象使得我們可以直接用超參數優化為強化學習模型的所有組件配置接口,尤其還有網絡架構。
進壹步思考
我們希望妳能發現 TensorForce 很有用。到目前為止,我們的重點還是讓架構先就位,我們認為這能讓我們更持續壹致地實現不同的強化學習概念和新的方法,並且避免探索新領域中的深度強化學習用例的不便。
在這樣壹個快速發展的領域,要決定在實際的庫中包含哪些功能是很困難的。現在的算法和概念是非常多的,而且看起來在 Arcade Learning Environment (ALE) 環境的壹個子集上,每周都有新想法得到更好的結果。但也有壹個問題存在:許多想法都只在易於並行化或有特定 episode 結構的環境中才有效——對於環境屬性以及它們與不同方法的關系,我們還沒有壹個準確的概念。但是,我們能看到壹些明顯的趨勢:
策略梯度和 Q 學習方法混合以提升樣本效率(PGQ、Q-Prop 等):這是壹種合乎邏輯的事情,盡管我們還不清楚哪種混合策略將占上風,但是我們認為這將成為下壹個「標準方法」。我們非常有興趣理解這些方法在不同應用領域(數據豐富/數據稀疏)的實用性。我們壹個非常主觀的看法是大多數應用研究者都傾向於使用 vanilla 策略梯度的變體,因為它們易於理解、實現,而且更重要的是比新算法更穩健,而新算法可能需要大量的微調才能處理潛在的數值不穩定性(numerical instabilities)。壹種不同的看法是非強化學習研究者可能只是不知道相關的新方法,或者不願意費力去實現它們。而這就激勵了 TensorForce 的開發。最後,值得考慮的是,應用領域的更新機制往往沒有建模狀態、動作和回報以及網絡架構重要。 更好地利用 GPU 和其他可用於並行/壹步/分布式方法的設備(PAAC、GA3C 等):這壹領域的方法的壹個問題是關於收集數據與更新所用時間的隱含假設。在非模擬的領域,這些假設可能並不成立,而理解環境屬性會如何影響設備執行語義還需要更多的研究。我們仍然在使用 feed_dicts,但也在考慮提升輸入處理的性能。 探索模式(比如,基於計數的探索、參數空間噪聲……) 大型離散動作空間、分層模型和子目標(subgoal)的分解。比如 Dulac-Arnold 等人的論文《Deep Reinforcement Learning in Large Discrete Action Spaces》。復雜離散空間(比如許多依賴於狀態的子選項)在應用領域是高度相關的,但目前還難以通過 API 使用。我們預計未來幾年會有大量成果。 用於狀態預測的內部模塊和基於全新模型的方法:比如論文《The Predictron: End-To-End Learning and Planning》。 貝葉斯深度強化學習和關於不確定性的推理總的來說,我們正在跟蹤這些發展,並且將會將此前錯過的已有技術(應該有很多)納入進來;而壹旦我們相信壹種新想法有變成穩健的標準方法的潛力,我們也會將其納入進來。在這個意義上,我們並沒有與研究框架構成明確的競爭,而是更高程度的覆蓋。