上一節我們安裝了機器學習mlagents的開發環境,本節我們創建第一個例子,了解什么是機器學習。
我們的例子很簡單,就是讓機器人自主移動到目標位置,不能移動到地板范圍外。
首先我們來簡單的了解以下機器學習的過程。
機器學習的過程
MLAgents機器強化學習的過程(reinforcement learning)
observation - 監視,觀察
decision - 決策
action - 行動
reward - 獎罰
這4個步驟的翻譯可能不是很準確,大概就是先觀察,后決策,然后行動,最后獎罰。
腳本開始
我們首先創建一個新腳本,我這里創建了MoveToTarget.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.MLAgents;public class MoveToTarget : Agent
{}
機器學習的類都要要繼承Agent基類。
Observation、Action(監視和行動)
我們首先通過覆寫CollectObservations函數,它負責觀察或者監視數據,本例是讓代理(agent)觀察目標target的方位。
然后覆寫OnActionReceived函數,通過接受到的緩沖區的數據進行行動,這里我們要注意機器學習的算法只適用于數字,這意味著機器不知道什么是對象(object),也不知道什么是左右移動,它只負責處理數字,例如float,int類型數據。
接下來,我們在Unity中創建一個agent(代理-盒子,藍色),target(目標-球形,黃色),還有地板plane(盒子,灰色)。如下圖:
理解重要參數
在agent上添加我們的腳本MoveToTarget,這時會自動添加一個BehaviorParameters的行為參數腳本。
?
?
離散的意義
我們先來理解下離散的意義:
假如我們離散輸入1,分支0輸入5。
代碼中覆寫Action接收。 我們看下log,因為只有一個離散分支,所以是DiscreteActions[0]
public class MoveToTarget : Agent
{public override void OnActionReceived(ActionBuffers actions){Debug.Log(actions.DiscreteActions[0]);}
}
因為我們覆寫了行動Action,我們還需要一個請求決策。我們在agent對象上添加DecisionRequester(決策請求),參數DecisonPeriod是請求的周期。
接下來我們就可以執行,看輸出了什么。
調試和查看輸出
?首先開打cmd,讓我們進入vent虛擬環境中。上一篇文章我們講過了,就是那個MLApp\venv\Scripts\activate.bat批處理文件,確保正常是這樣的。
然后我們輸入
mlagents-learn
然后會出現一個漂亮的Unity Logo,并且告訴我們,可以開始Unity運行了。如下圖:
Unity運行后,我們看到cmd窗口和Unity的輸出已經開始了。
我們可以看到離散的輸出,因為設置了5,這里也只有0-4。
連續類型
接下來我們測試連續型
在Unity中我們把SpaceType改為continuous。并且設置Size為1。
腳本也需要改為接收連續型
public class MoveToTarget : Agent
{public override void OnActionReceived(ActionBuffers actions){Debug.Log(actions.ContinuousActions[0]);}
}
我們繼續開始運行,在cmd中輸入mlagents-learn
這時我們會得到一個報錯:
是因為我們重試使用了相同的默認ID進行再次訓練,這里我們要么使用mlagents-learn --force來強制覆蓋學習,要么更換ID,mlagents-learn --run-id=test2。
那么虛擬環境開啟后,我們運行Unity。
運行后,我們得到的log如下:
我們看到了,連續的就是-1到1的浮點數。到這里我們就了解了離散和連續的區別了。
監視和行動代碼
下面我們將繼續完善腳本,收集監視信息。
我們需要覆寫CollectObservations(VectorSensor sensor)函數。這個函數你可以理解成AI,就是人工智能需要哪些數據才能解決你的問題。在本例中,我們希望盒子(agent)對象移動到球(target)對象的位置。我們思考以下,我們需要知道的數據有哪些?
?如果你想agent能夠移動到目標,是不是需要知道agent在哪,target在哪,所以要知道兩個目標的位置,所以我們通過傳感器把坐標傳入監視。所以代碼里我們把兩個物體的坐標位置傳遞給觀察器。
[SerializeField] Transform targetTfm; public override void CollectObservations(VectorSensor sensor){sensor.AddObservation(transform.position);sensor.AddObservation(targetTfm.transform.position);}
行動里,actions就是(decision - 決策)的結果,我們根據決策數據進行行動。
//行動float moveSpd = 10f;public override void OnActionReceived(ActionBuffers actions){float moveX = actions.ContinuousActions[0];float moveZ = actions.ContinuousActions[1];transform.position += new Vector3(moveX, 0f, moveZ) * Time.deltaTime * moveSpd;}
?因為我們給觀察函數的信息是兩個坐標,相當于6個float類型,所以Vector Observations 的 SpaceSize需要填寫6。而行動,我們只需要移動agent的x和z軸,所以Vector Action的SpaceSize填寫2。
如何讓機器學習
你可以把機器學習看成是訓練小狗,如果小狗完成了指定動作,你可以給它骨頭。反之,給予懲罰。
在本例中,我們在地板周圍圍上4面墻體。我們判斷如果它移動到墻就扣分,如果移動都目標就加分,我們在Unity里給Plane圍上4個wall。我們添加墻體,并勾選墻體和target 的Collider的IsTrigger,方面我們進行一個觸發處理。
?
添加獎勵模塊腳本
private void OnTriggerEnter(Collider other){Debug.Log("OnTriggerEnter:"+other.name);if (other.name.Equals("target")){ AddReward(+1f); //獎勵EndEpisode(); //結束經歷plane.material.color = Color.green;Debug.Log("獎勵");}else if (other.name.Equals("wall")){AddReward(-1f); //懲罰EndEpisode(); //結束plane.material.color = Color.grey;Debug.Log("懲罰");}}
上面的代碼中,如果碰到了target,我們調用AddReward +1,并結束本段AI,讓plane的顏色變為綠色,反之如果碰到了wall,那么就AddReward -1,plane變成灰色。
回合結束處理
每當得到獎勵或者懲罰,會調用EndEpisode。當本段落結束后我們希望它繼續訓練,我們需要把agent對象重新復位,我們要覆寫函數OnEpisodeBegin。
?
//當一段經歷開始public override void OnEpisodeBegin(){transform.position = Vector3.zero;Debug.Log("經歷開始");}
運行mlagent虛擬機后我們運行unity,可以看到機器已經開始學習如何跑到target的位置了,剛開始很艱難,常常碰到墻壁,慢慢的碰到target的概率會越來越大。
效果如下:
運行過程中,可能開始agent對象很笨,基本原地打轉,經過長周期的運行會越來越快的找尋到target。
幾個參數
這里有幾個點要說明
MaxStep(最大步)
這里的MaxStep是一段(Episode)最大步數,如果我們不想每次運行嘗試步數太長,可以給一個數值,你可以嘗試1000,10000這樣的數字,到達這個后,會重新進入OnEpisodeBegin。設置的目的是如果代理一直運行都沒有碰到過target,只是躲避了wall,那么可能達不到我們訓練的目的。
Heuristic (啟發)
這個我的理解是通過你的控制來測試你的運行邏輯是否正確。屬于一個調試功能。
?
//啟發public override void Heuristic(in ActionBuffers actionsOut){ActionSegment<float> continuousActions = actionsOut.ContinuousActions;continuousActions[0] = Input.GetAxisRaw("Horizontal");continuousActions[1] = Input.GetAxisRaw("Vertical");}
我們可以修改agent的BehaviorParameters的BehaviorType為Heuristic(啟發),然后運行Unity就可以控制agent。模擬機器是否遇到target和wall會復位,進行調試。
機器學習加快的辦法
還有一個機器學習加速的辦法,那就是把當前的訓練場景復制很多個,讓他們同時運行來達到機器訓練加速的目的,我們可以把場景和腳本稍加修改。如下:
?
我們需要修改腳本,把原來的position改為localPosition。因為這樣很容易復用我們的代碼,并且只用復制幾個圖中的ground就可以了。
全代碼如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Sensors;public class MoveToTarget : Agent
{[SerializeField] Transform targetTfm;[SerializeField] Renderer plane;float moveSpd = 30f;//通過傳感器把坐標傳入監視public override void CollectObservations(VectorSensor sensor){sensor.AddObservation(transform.localPosition);sensor.AddObservation(targetTfm.transform.localPosition);}//行動接收public override void OnActionReceived(ActionBuffers actions){float moveX = actions.ContinuousActions[0];float moveZ = actions.ContinuousActions[1];transform.localPosition += new Vector3(moveX, 0f, moveZ) * Time.deltaTime * moveSpd;}//當一段經歷開始public override void OnEpisodeBegin(){transform.localPosition = Vector3.zero;Debug.Log("經歷開始");}//啟發public override void Heuristic(in ActionBuffers actionsOut){ActionSegment<float> continuousActions = actionsOut.ContinuousActions;continuousActions[0] = Input.GetAxisRaw("Horizontal") * Time.deltaTime * moveSpd;continuousActions[1] = Input.GetAxisRaw("Vertical") * Time.deltaTime * moveSpd;}private void OnTriggerEnter(Collider other){//Debug.Log("OnTriggerEnter:"+other.name);if (other.name.Equals("target")){ AddReward(+1f); //獎勵EndEpisode(); //結束經歷plane.material.color = Color.green;//Debug.Log("獎勵");}else if (other.name.Equals("wall")){AddReward(-1f); //懲罰EndEpisode(); //結束plane.material.color = Color.grey;//Debug.Log("懲罰");}}}
我們修改完畢后,然后運行mlagents環境并運行Unity,明顯批量的速度更快了。如下圖:
從GIF中能看到,亮起綠色的頻率越來越快了。在我的機器上到最后就只有綠色的亮起了。
等機器運算完畢后會生成MovetoTart1.onnx文件。
然后在
H:\UnityProject\MLApp\venv\Scripts\results\就能看到我們所有的mlagents測試數據,包含我們需要的訓練后的MoveToTar.onnx文件,我們把它復制到Unity/Assets中。這個onnx就是經過AI訓練的大腦的神經網絡。
我們把這個文件拖動到Model里。
?
BehaviorType選擇InferenceOnly,點擊Unity運行,這樣這個agent就擁有找尋target的AI了。
環境設置
機器學習的環境是可以自定義配置的,可以參考這里。
創建一個movetarget.yaml文件,放到Unity/config文件夾(建立一個)
?
behaviors:MoveToTar1:trainer_type: ppohyperparameters:batch_size: 10buffer_size: 100learning_rate: 3.0e-4beta: 5.0e-4epsilon: 0.2lambd: 0.99num_epoch: 3learning_rate_schedule: linearbeta_schedule: constantepsilon_schedule: linearnetwork_settings:normalize: falsehidden_units: 128num_layers: 2reward_signals:extrinsic:gamma: 0.99strength: 1.0max_steps: 500000time_horizon: 64summary_freq: 10000
通過下面的指令進行,就是按照自定的參數來執行了。具體參數意義有機會我們后面再聊。
使用這個配置文件開啟機器學習,輸入下面的指令:
mlagents-learn config/movetarget.yaml --run-id=test5
進一步優化機器
我們繼續上一個測試。當運行的時候,把target的位置改變,我們發現agent可能就找不到目標了,可以思考下為什么?如下面的動畫:
對的,因為在機器學習的時候我們的target的位置一直沒有發生變化,所以AI可能覺得目標是死物,所以我們可以通過修改腳本,讓target每段運算完畢后改變位置,發生變化,機器就會變得聰明些。
我們修改代碼如下:
public override void OnEpisodeBegin(){transform.localPosition = new Vector3(Random.Range(-9f, 0f), 0f, Random.Range(-4f, 4f));targetTfm.localPosition = new Vector3(Random.Range(1f, 9f), 0f, Random.Range(-4f, 4f));//Debug.Log("經歷開始");}
我們每次開始都隨機以下target和agent的位置,但是不會重合。然后再進行機器學習。
我們輸入下面指令,在上一次運行的test5的基礎上進行test8運算
mlagents-learn config/movetarget.yaml --initialize-from=test5 --run-id=test8
運算后我們覆蓋onnx文件,繼續運行,結果如下:
Web監控
要在訓練期間監控代理性能的統計信息,請使用 TensorBoard指令。
可以開另外一個cmd,進入虛擬環境(venv),輸入下面指令:
tensorboard --logdir results
然后再瀏覽器輸入地址就可以了
http://localhost:6006/
根據圖表,你可以看到各種曲線,來修改你的機器訓練。
本章內容就到這里了,官方還有很多種機器學習的例子,如果有興趣可以自行學習。有機會下一篇文章我們進一步擴展,或者做另外一個有意思的Demo。
本章源碼
GitHub - thinbug/MLApp
引用:
https://github.com/Unity-Technologies/ml-agents/blob/main/docs/Learning-Environment-Create-New.md
?
Unity機器學習2 ML-Agents第一個例子_ml-agents小狗-CSDN博客
Unity中訓練一個ML-Agents項目—解決torch和mlagents配置問題_mlagents訓練_LLLQQQismmmmme的博客-CSDN博客
Unity 對接 ML-Agents 初探_艾沃尼斯的博客-CSDN博客
GitHub - thinbug/MLApp
Unity之ml-agents(一):環境配置及初步使用_mlagents-CSDN博客