一、強化學習問題需要描述那些內容
強化學習中最主要的兩類對象是“個體”和“環境”,其次還有一些像“即時獎勵”、“收獲”、“狀態”、“行為”、“價值”、“策略”、“學習”、“控制”等概念。這些概念把個體和環境聯系起來。通過理論學習,我們知道:
1. 環境響應個體的行為。當個體執行一個行為時,它需要根據環境本身的動力學來更新環境,也包括更新個體狀態,同時給以個體一個反饋信息:即時獎勵。
2. 對於個體來說,它並不掌握整個環境信息,它只能通過觀測來獲得其可以獲得的信息,它能觀測到哪些信息取決於問題的難度;同樣個體需要一定的行為與環境進行交互,哪些行為是被允許的,也要由個體和環境協商好。因此環境要確定個體的觀測空間和行為空間。
3. 個體還應該有一個決策功能,該功能根據當前觀測來判斷下一時刻該采取什么行為,也就是決策過程。
4. 個體具有執行一個確定行為的功能。
5. 智能的個體應能從與環境的交互中學習到知識,進而在與環境交互時盡可能多的獲取獎勵,最終達到最大化累積獎勵的目的。
6. 環境應該給個體設置一個(些)終止條件,即當個體處在這個狀態或這些狀態之一時,約定交互結束,即產生一個完整的Episode。隨后重新開始一個Episode或者退出交互。
把上面的過程提煉成偽代碼可以是下面這樣:
按照上面的設計,可以寫出一個不錯的個體與環境的類。但是我們不打算按照這個寫下去,我們看看gym庫是如何描述環境,以及個體通過什么方式與環境進行交互。
二、gym庫介紹
gym庫的在設計環境以及個體的交互時基本上也是解決上述問題,但是它有它的規范和接口。gym庫的核心在文件core.py里,這里定義了兩個最基本的類Env和Space。前者是所有環境類的基類,后者是所有空間類的基類。從Space基類衍生出幾個常用的空間類,其中最主要的是Discrete類和Box類。通過其__init__方法的參數以及其它方法的實現可以看出前者對應於一維離散空間,后者對應於多維連續空間。它們既可以應用在行為空間中,也可以用來描述狀態空間,具體怎么用看問題本身。例如如果我要描述上篇提到的一個4*4的格子世界,其一共有16個狀態,每一個狀態只需要用一個數字來描述,這樣我可以把這個問題的狀態空間用Discrete(16)對象來描述就可以了。對於另外一個經典的小車爬山的問題(如下圖),小車的狀態是用兩個變量來描述的,一個是小車對應目標旗桿的水平距離,另一個是小車的速度(是沿坡度切線方向的速率還是速度在水平方向的分量這個沒仔細研究),因此環境要描述小車的狀態需要2個連續的變量。由於描述小車的狀態數據對個體完全可見,因此小車的狀態空間即是小車的觀測空間,此時再用Discrete來描述就不行了,要用Box類,Box空間可以定義多維空間,每一個維度可以用一個最低值和最大值來約束。同時小車作為個體可以執行的行為只有3個:左側加速、不加速、右側加速。因此行為空間可以用Discrete來描述。最終,該環境類的觀測空間和行為空間描述如下:
從這段代碼可以看出,要定義一個Discrete類的空間只需要一個參數n就可以了,而定義一個多維的Box空間需要知道每一個維度的最小最大值,當然也要知道維數。
有了描述空間的對象,再來看環境類如何聲明就簡單的多了。先來看看代碼中關於環境基類的一段解釋:
看得出,個體主要通過環境的一下幾個方法進行交互:step,reset,render,close,seed,而這幾個都是公用方法,具體每一個方法實際調用的都是其內部方法:_step,_reset,_render,_close,_seed。此外這段描述還指出,如果你要編寫自己的環境類,也主要是重寫這些私有方法,同時指定該環境的觀測和行為空間。_close方法可以不用重寫。這幾個方法主要完成的個性化功能如下:
_step: 最核心的方法,定義環境的動力學,確定個體的下一個狀態、獎勵信息、是否Episode終止,以及一些額外的信息,按約定,額外的信息不被允許用來訓練個體。
_reset: 開啟個體與環境交互前調用該方法,確定個體的初始狀態以及其他可能的一些初始化設置。
_seed: 設置一些隨機數的種子。
_render: 如果需要將個體與環境的交互以動畫的形式展示出來的話,需要重寫該方法。簡單的UI設計可以用gym包裝好了的pyglet方法來實現,這些方法在rendering.py文件里定義。具體使用這些方法進行UI繪制需要了解基本的OpenGL編程思想和接口,這里暫時不做細說。
可以看出,使用gym編寫自己的Agent代碼,需要在你的Agent類中聲明一個env變量,指向對應的環境類,個體使用自己的代碼產生一個行為,將該行為送入env的step方法中,同時得到觀測狀態、獎勵值、Episode是否終止以及調試信息等四項信息組成的元組:
state 是一個元組或numpy數組,其提供的信息維度應與觀測空間的維度一樣、每一個維度的具體指在制定的low與high之間,保證state信息符合這些條件是env類的_step方法負責的事情。
reward 則是根據環境的動力學給出的即時獎勵,它就是一個數值。
is_done 是一個布爾變量,True或False,你可以根據具體的值來安排個體的后續動作。
info 提供的數據因環境的不同差異很大,通常它的結構是一個字典:
{"key1":data1,"key2":data2,...}
獲取其中的信息應該不難。
最后一點,在自己的代碼中如何建立個環境類對象呢?有兩種情況,一種是在gym庫里注冊了的對象,你只要使用下面的語句:
其中不同的環境類有不同的注冊名,只要把make方法內的字符串改成對應的環境名就可以了。
另外一種使用自己編寫的未注冊的環境類,這種很簡單,同一般的建立對象的語句沒什么區別:
相信讀者已經基本清楚了如何使用gym提供的環境類了。下一步就是如何編寫自己的環境類了。