ML-Agents(二)創建一個學習環境
- 2020 年 3 月 16 日
- 筆記
ML-Agents(二)創建一個學習環境
一、前言
上一節我們講了如何配置ML-Agents環境,這一節我們創建一個示例,主要利用Reinforcement Learning(強化學習)。
如上圖,本示例將訓練一個球滾動找到隨機放置的立方體,而且要避免從平台上掉下去。
本示例是基於ML-Agents官方的示例,官方有中文版和英文版兩個文檔,英文版的是最新的,中文版中大部分內容和英文版的一致,但也有不同,本文是基於最新版所做(v0.15.0,master分支),需要參考官方文檔的也可參照如下地址食用。
英文:https://github.com/Unity-Technologies/ml-agents/tree/master/docs
二、概述
在Unity項目中使用ML-Agents涉及以下基本步驟:
- 創建一個容納agent的環境。該環境可以從包含少量對象的簡單物理模擬環境到整個遊戲或生態系統,環境的樣式可以多種多樣;
- 實現Agent子類。Agent子類定義了必要的程式碼以供agent用於觀測自身環境、執行指定動作以及計算用於強化訓練的獎勵。你同樣可以實現可選方法,從而在agent完成任務或任務失敗時重置agent;
- 將實現Agent子類的腳本加到適當的GameObject上,當該對象在場景中,即代表對應agent在模擬環境中了。
(PS.在官方中文文檔中,第2,3步需要實現Academy子類和Brain,但在新版中,這兩個東西已經不需要在場景里定義了,所以比較重要的就是這個Agent子類,基本學習邏輯都在這裡)
三、設置Unity項目
第一步,我們先新建一個Unity項目,並且將ML-Agents包導入到裡面:
-
打開Unity,新建一個項目隨意叫個名字,例如「RollerBall」;
-
在Unity菜單「Edit」->「Project Settings」,在彈出的窗口中,找到「Player」,將「Api Compathbility Level」改為「.NET 4.x」,如下圖;
-
在上一節中,我們已經將ml-agents程式碼庫克隆到了本地,如果沒有克隆,請參考上一篇「Unity ML-Agents v0.15.0(一)環境部署與試運行」中的五、1,這裡我們默認大家都是已經克隆了庫,則在Unity中需要將ML-Agents插件導入Unity中。我這裡的版本是Unity2019.2,方法如下:
- 在項目根目錄中找到Packages文件夾;
-
文件夾中有一個「manifest.json」的文件,編輯它,這個就是工程中的Packages包集合,在最後加入"com.unity.ml-agents" : "file:D:/Unity Projects/ml-agents/com.unity.ml-agents",這裡file:後是你自己克隆的ml-agents源碼路徑,別照抄我的哦,除非你也是這個路徑- -,如下圖;
修改後保存,在切到Unity中,如果路徑正確,則會出現導入package包的畫面,在工程的Packages文件夾下也會成功出現「ML Agents」文件夾,如下圖:
-
創建環境
下面,我們創建一個簡單的ML-Agent環境。該環境的「physical」組件包括一個Plane(充當agent移動的地板)、一個Cube(充當agent尋找的目標)和一個Sphere(表示agent本身)。
-
創建地板
-
在 Hierarchy 窗口中右鍵單擊,選擇 3D Object > Plane。
-
將遊戲對象命名為「Floor」。
-
選擇 Plane 以便在 Inspector 窗口中查看其屬性。
-
將 Transform 設置為 Position = (0,0,0)、Rotation = (0,0,0)、Scale = (1,1,1)。
-
修改Plane材質,變的好看點。
-
以上過程我都是複製的,其實就是創建一個Plane,然後換個好看的材質就行,隨意定義一個都OK。
-
創建目標立方體
-
在 Hierarchy 窗口中右鍵單擊,選擇 3D Object > Cube。
-
將遊戲對象命名為「Target」
-
選擇 Target 以便在 Inspector 窗口中查看其屬性。
-
將 Transform 設置為 Position = (3,0.5,3)、Rotation = (0,0,0)、Scale = (1,1,1)。
-
修改Cube材質。
-
-
添加Agent球體
- 在 Hierarchy 窗口中右鍵單擊,選擇 3D Object > Sphere。
- 將遊戲對象命名為「RollerAgent」
- 選擇 Target 以便在 Inspector 窗口中查看其屬性。
- 將 Transform 設置為 Position = (0,0.5,0)、Rotation = (0,0,0)、Scale = (1,1,1)。
- 在 Sphere 的 Mesh Renderer 上,展開 Materials 屬性並將默認材質更改為 checker 1。
- 單擊 Add Component。
- 向 Sphere 添加 Physics/Rigidbody 組件。(添加 Rigidbody)
-
OK,以上過程就將Unity中的三維環境創建好了,下面我們來實現Agent。
四、實現Agent
在官方中文文檔中還有「實現Academy」及「添加Brain」,最新版里已經不需要了!直接設置Agent就行。
要創建Agent:
- 選擇 RollerAgent 遊戲對象以便在 Inspector 窗口中查看該對象。
- 單擊 Add Component。
- 在組件列表中單擊 New Script(位於底部)。
- 將該腳本命名為「RollerAgent」。
- 單擊 Create and Add。
然後,編輯新的RollerAgent
腳本:
- 打開
RollerAgent
腳本; - 令
RollerAgent
繼承Agent
類,同時引用using MLAgents
和using MLAgents.Sensors
命名空間; - 刪除
Update()
方法,先保留Start()
方法之後要用。
到目前為止,以上的步驟都是為了將ML-Agents添加到任何Unity項目中而需要的基本步驟。接下來,我們將添加邏輯,使我們的agent能夠利用reinforcement learning(強化學習)技術學習找到立方體。
初始化和重置Agent
當agent(球體)到達目標位置(方塊)後,會將自己標記為完成狀態,而agent的重置函數(Reset)會將方塊再重新移動到新的位置。另外,如果agent從平台上掉落,也會觸發重置函數,使得agent初始化,目標位置也將隨機刷新。
為了重置agent的速度(以及之後給它施加力移動),我們需要引用到球體的Rigidbody
組件。這個組件的引用就可以寫到Start()
方法中,以以上的邏輯,我們的RollerAgent
腳本如下:
using MLAgents; using MLAgents.Sensors; using UnityEngine; public class RollerAgent : Agent { public Transform Target;//方塊 public float speed = 10;//小球移動速度 private Rigidbody rBody;//球剛體 private void Start() { rBody = GetComponent<Rigidbody>(); } /// <summary> /// Agent重置 /// </summary> public override void AgentReset() { if (this.transform.position.y < 0) {//如果小球掉落,小球初始化 rBody.velocity = Vector3.zero; rBody.angularVelocity = Vector3.zero; transform.position = new Vector3(0, 0.5f, 0); } //方塊位置隨機 Target.position = new Vector3(Random.value * 8 - 4, 0.5f, Random.value * 8 - 4); } }
接下來,我們來實現Agent.CollectObservations(VectorSensor sensor)
方法。
注意,這裡和老版本方法不同,之前的函數中並沒有VectorSensor sensor
參數,不過用法差不多。
觀測環境(Observing the Environment)
Agent將我們收集的資訊發送給Brain,由Brain使用這些資訊來做決策。當你訓練Agent(或使用已經訓練好的模型)時,數據將作為特徵向量輸入到神經網路中。為了讓Agent成功學習某個任務,我們需要提供正確的資訊。一個好的經驗法則則是考慮你在計算問題的分析解決方案時需要用到什麼。
這裡比較重要,就是說你在訓練的時候,需要考慮到的變數是哪些,我們來看看這個例子中我們需要agent收集的資訊有哪些:
-
目標的位置
sensor.AddObservation(Target.position);
-
agent的位置
sensor.AddObservation(transform.position);
-
agent的速度,這有助於agent去學習控制自己的速度,而不會使其越過目標和從平台上滾下來
sensor.AddObservation(rBody.velocity.x);
sensor.AddObservation(rBody.velocity.z);
這裡一共有8個觀測值(一個position算x,y,z三個值),之後需要在Behavior Parameters
組件的屬性里進行設置,如下圖:
在中文文檔里,對這些值進行了歸一化處理,最新英文文檔中並沒有進行歸一化處理,直接加上就行。然後這裡的重載函數如下:
/// <summary> /// Agent收集的觀察值 /// </summary> /// <param name="sensor"></param> public override void CollectObservations(VectorSensor sensor) { sensor.AddObservation(Target.position);//目標的位置 sensor.AddObservation(transform.position);//小球的位置 sensor.AddObservation(rBody.velocity.x);//小球x方向的速度 sensor.AddObservation(rBody.velocity.z);//小球z方向的速度 }
Agent的最後一部分是Aegnt.AgentAction()
函數,這個方法主要是用來接收Brain的決策命令以及根據不同情況來進行Reward(獎勵)。
動作(Actions)
Brain的決策以動作數組的形式傳遞給AgentAction()函數。此數組中的元素主要由agent的Brain的Vector Action
、Space Type
和Space Size
來決定的。這裡分別代表 了向量運動空間、向量運動空間類型以及向量運動空間數,ML-Agents將動作分為兩種:Continusous向量運動空間是一個可以連續變化的數字向量,例如一個元素可能表示施加到agent某個Rigidbody
上的里或扭矩;Discrete向量運動空間則將動作定義為一個表,提供給agent的具體動作是這個表的索引。
我們在這裡利用的是Continusous
向量運動空間,即將Space Type
設置為Continuous,並將Space Size
設置為2。這就表示了Brain生成的決策利用第一個元素action[0]
來確定施加沿x軸的力,通過action[1]
來確定施加沿z軸的力(如果agent是三維移動,則將Space Size
設置為3)。注意,這個裡Brain並不知道action[]
數組中每個值的具體含義,在訓練的過程中只是根據觀測輸入來調整動作,然後看看會得到什麼樣的獎勵。具體設置如下圖,同樣是在Behavior Parameters
組價中進行設置:
引申一下,這裡也可以使用Discrete類型來訓練,不過相應的Space Size
就要變成4了,因為有4個方向需要控制。
OK,上述的動作程式碼如下:
//Space Type=Continuous Space Size=2 Vector3 controlSignal = Vector3.zero; controlSignal.x = vectorAction[0];//x軸方向力 controlSignal.z = vectorAction[1];//z軸方向力 //當然上面這兩句可以互換,因為Brain並不知道action[]數組中數值具體含義 rBody.AddForce(controlSignal * speed);
獎勵(Rewards)
Reinforcement Learning(強化學習)需要獎勵。同樣獎勵(懲罰)也在AgentAction()
函數中實現,與上面動作實現的重寫函數在一起。學習演算法使用在模擬和學習過程的每個步驟中分配給agent的獎勵來確定是否為agent提供了最佳的動作。當agent完成任務時,對它進行獎勵。在這個示例中,如果Agent(小球)到達了目標位置(方塊),則給它獎勵1分。
RollerAgent會計算到達目標所需的距離,當到達目標時,我們可以通過Agent.SetReward()
方法來講agent標記為完成,並給它獎勵1分,同時使用Done()
方法來重置環境。
//計算自身與目標的距離 float distanceToTarget = Vector3.Distance(transform.position,Target.position); //不同情況進行獎勵 if (distanceToTarget < 1.42f) {//到達目標附近 SetReward(1); Done(); }
最後,如果小球掉落平台,則讓agent重置。這裡沒有設置懲罰,有興趣的童靴可以自己試試設置懲罰。
if (transform.position.y < 0) {//小球掉落 //SetReward(-1); 懲罰先不設置 Done(); }
AgentAction()方法
OK,由以上的動作和獎勵構成了AgentAction()方法,主要理解裡面每一步的意義是為何,最後AgentAction()
方法如下:
public override void AgentAction(float[] vectorAction) { //Space Type=Continuous Space Size=2 Vector3 controlSignal = Vector3.zero; controlSignal.x = vectorAction[0];//x軸方向力 controlSignal.z = vectorAction[1];//z軸方向力 //當然上面這兩句可以互換,因為Brain並不知道action[]數組中數值具體含義 rBody.AddForce(controlSignal * speed); //計算自身與目標的距離 float distanceToTarget = Vector3.Distance(transform.position, Target.position); //不同情況進行獎勵 if (distanceToTarget < 1.42f) {//到達目標附近 SetReward(1); Done(); } if (transform.position.y < 0) {//小球掉落 Done(); } }
最終Editor設置
到這一步,所有的遊戲對象和ML-Agent組件都已準備就緒,然後我們需要在場景的小球上加一些腳本,修改一些屬性。
-
在場景中選擇RollerAgent小球,先添加
Behavior Parameters
腳本,並設置其中的Space Size
為8,Space Type
為Continuous,Space Size
為2。這步要是之前幾步里已經搞定了,就不用管了。不過這裡面有個Behavior Name
,這個屬性應該就是區分Brain的名稱,新版中多個Agent擁有相同Brain的話,應該是在這裡進行區分,我個人是這麼覺得的,要是有錯的話,請指正; -
這步一定要記得,老版里沒有,需要加
Decision Requester
組件,並將Decision Period
改為10!這裡英文文檔也寫得不起眼,要是不加這個腳本,你的小球是動不起來的。
手動測試環境
在開始長時間訓練之前,手動測試你的測試環境是一個明智的做法。為了手動測試,我們需要在Roller Agent
腳本中添加Heuristic()
方法,以此來代替Brain決策,程式碼如下:
/// <summary> /// 手動測試 /// </summary> /// <returns></returns> public override float[] Heuristic() { var action = new float[2]; action[0] = Input.GetAxis("Horizontal"); action[1] = Input.GetAxis("Vertical"); return action; }
其實這裡就是通過鍵盤來對動作空間action[]數組進行賦值來使得agent動作。
然後還需要在Behavior Parameters
組件中將Behavior Type
改為Heuristic Only
,如下:
這個時候我們可以運行一下,(突然發現Roller
中的Targer忘記拖了,將方塊目標拖進來),然後就可以使用WSAD或上下左右來控制小球了,到方塊附近,方塊會自動重新置位,如果小球掉落,也會重新置位。
OK,我們Behavior Type
改回Default
,準備開始訓練了~
五、進行訓練
打開Anaconda3,找到我們之前建好的訓練環境,啟動「Terminal」。
cd到ml-agent的根目錄,例如我的路徑:
cd /d D:Unity Projectsml-agents
這裡插一下,我們修改一下ml-agents中的Config文件,找到ml-agents
中config
文件夾,並打開trainer_config.yaml
配置文件,在最後加一句
RollerBallBrain: batch_size: 10 buffer_size: 100
這裡可以看到RollerBallBrain其實就是我剛才在Behavior Parameters
組件中設置的Behavior Name
。這裡修改這兩個參數會覆蓋配置文件最前的默認項default
,這兩個超參數值修改小可以使得訓練速度加快,如果用原來的參數(batch_size: 1024,buffer_size: 10240),則需要訓練大約300000步,但是修改後只需要低於20000步。這裡應該是根據具體項目具體設置的。
我們配置完之後,回到命令行,輸入:
mlagents-learn config/trainer_config.yaml --run-id=RollerBall-1 --train
運行Unity中的程式。
如果Unity與Anaconda訓練環境成功通訊,則會在命令行中發現你的訓練配置:
同時,可以看到Unity中小球開始自己快速運動,方塊也會根據不同狀態來隨機生成。
過一段時間,命令行中會顯示相應的執行步數,經過的時間,平均獎勵等資訊。
隨著訓練的進行,會發現小球很難掉下平台,且一直跟隨方塊的位置:
最後訓練時間太長了,可以通過配置文件中的max_steps
來修改最大訓練步驟,所以我這裡直接Ctrl+C了,這樣也會將訓練模型存下來。
找到這個RolerBallBrain.nn
文件,在ml-agent的models
文件夾下,將這個.nn文件考入Unity中,如下:
然後選中場景中的小球,將Behavior Parameters
組件中Model
屬性中,選擇剛才訓練好的模型,並將Behavior Type
選為Inference Only
,如下:
然後點擊運行,就可以看到小球利用我們訓練好的模型開始找方塊了。
TensorBoard統計資訊
我們在命令行中,還可以找到剛才訓練的圖表資訊,在命令行中輸入:
tensorboard --logdir=summaries
然後將地址在瀏覽器中打開,一般都是http://localhost:6006/
則可以看到隨著訓練步數各個數值變化值。
這些值的含義,抄一些官方中文文檔:
-
Lesson – 只有在進行課程訓練]時才有意義。
-
Cumulative Reward – 所有 agent 的平均累積場景獎勵。 在成功訓練期間應該增大。
-
Entropy – 模型決策的隨機程度。在成功訓練過程中 應該緩慢減小。如果減小得太快,應增大
beta
超參數。 -
Episode Length – 所有 agent 在環境中每個場景的 平均長度。
-
Learning Rate – 訓練演算法搜索最優 policy 時需要多大的 步驟。隨著時間推移應該減小。
-
Policy Loss – policy 功能更新的平均損失。與 policy (決定動作的過程)的變化程度相關。此項的幅度 在成功訓練期間應該減小。
-
Value Estimate – agent 訪問的所有狀態的平均價值估算。 在成功訓練期間應該增大。
-
Value Loss – 價值功能更新的平均損失。與模型 對每個狀態的價值進行預測的能力相關。此項 在成功訓練期間應該減小。
OK,以上就是官方訓練的一個小例子全過程了。
記錄這個還挺累,不過歡迎大家一起留言討論,轉載麻煩請註明原地址哦,感謝大家~
引用:
https://github.com/Unity-Technologies/ml-agents/tree/master/docs