# ML-Agents(三)3DBall例子
前一周忙着公司的考試,都沒有怎么學新的,今天補上~
之后的記錄,我准備先只研究官方的示例,主要是把研究過程中的疑惑和想法記下來。首先我先補充一下如何利用GPU進行訓練,結合(一)中的安裝方法,需要CUDA v10.0,cuDNN v7.6.5 for CUDA v10.0,對應Tensorflow的版本是2.0.1。
一、利用GPU進行訓練
前置工作在文章(一)中都有,原先的環境可以保留。現在可以拉一個新的ml-agents源碼,然后修改ml-agents
文件下的setup.py
中如下圖:
原來是"tensorflow>=1.7,<2.1"
,現在修改為"tensorflow-gpu>=1.7,<2.1"
,然后再在Anaconda中新建一個環境,如下:
建好后在命令行中重新安裝環境(別忘記cd到新的ml-agents源碼),分別輸入:
pip install -e ml-agents-envs -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
pip install -e ml-agents -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
就是分別安裝兩個環境,可以發現他會自動下載tensorflow_gpu-2.0.1
,如下圖:
配置好后,在Unity對應的Agent腳本上,也需要將Inference Device
勾選為GPU
,然后按以前的方法開始訓練,就可以了。
二、3DBall介紹
官方示例中,3Dball是一個比較簡單的例子,主要運用了reinforcement learning(強化學習)。就是小球在平台上,萌版平台要控制自己繞x、z軸旋轉,從而保持小球在自己頭上不掉下來。
現根據官方文檔翻譯一下:
-
設置:一個平衡球任務,agent需要保持小球在其腦袋上的平衡
-
目標:Agent必須盡可能長時間地保持球在頭頂的平衡
-
Agent設置:環境中包含12個想同類型的agent,全部使用同樣的行為參數
-
Agent獎勵設置:
-
小球每一步保持在方塊頭頂上就獎勵+0.1
-
如果小球掉落,懲罰-0.1
-
-
行為參數
- 矢量觀察空間:8個變量,包括agent方塊的旋轉角度(兩個值,x、z軸方向),球和方塊之間的相對位置關系(Vector3),球上剛體的速度(Vector3)
- 矢量動作空間:類型為
Continuous
,Size
為2,分別控制繞x軸旋轉、繞z軸旋轉
-
Float屬性:三個
- scale(比例):小球的比例,默認為1,推薦最小值為0.2,最大值為5
- gravity(重力):重力加速度,默認為9.81,推薦最小值4,最大值為105
- mass(質量):小球的質量,默認為1,推薦最小值為0.1,最大值為20
-
基准平均獎勵:100
OK,上面是借鑒官方文檔,隨意翻譯了一下,大概可以了解3DBall主要用途和一些主要參數,里面的Float屬性我這里不是很明白,在代碼中是用在Agent.InitializeAgent()
中的SetResetParameters()
,這個方法顧名思義應該是對Agent進行初始化的操作。也許在這里,我們可以任意修改小球的比例、質量、重力加速度來改變小球在不同的情況下,訓練的效果如何吧。
三、3DBall代碼分析
看到目前為止,ml-agents其中的精華就在agent的腳本如何設置了,我們下面來分析一下Ball3DAgent
代碼。
初始化
public class Ball3DAgent : Agent
{
[Header("Specific to Ball3D")]
public GameObject ball;
Rigidbody m_BallRb;
IFloatProperties m_ResetParams;
public override void InitializeAgent()
{
m_BallRb = ball.GetComponent<Rigidbody>();
m_ResetParams = Academy.Instance.FloatProperties;
SetResetParameters();
}
public void SetBall()
{
//從Academy中獲取小球的屬性(質量、比例)
m_BallRb.mass = m_ResetParams.GetPropertyWithDefault("mass", 1.0f);
var scale = m_ResetParams.GetPropertyWithDefault("scale", 1.0f);
ball.transform.localScale = new Vector3(scale, scale, scale);
}
public void SetResetParameters()
{
SetBall();
}
}
這里初始化應用了InitializeAgent()
方法,這里獲取了小球的剛體,並且利用SetBall()
來設置了小球的質量和比例,這里的的m_ResetParams變量是Academy的FloatProperties變量,這里的變量好像是會作為環境參數傳遞給Python,具體的用途我也還沒研究清楚,姑且先看做是初始化小球的屬性。
環境觀察值
直接上源碼。
public override void CollectObservations(VectorSensor sensor)
{
//平台繞Z軸旋轉值
sensor.AddObservation(gameObject.transform.rotation.z);
//平台繞X軸旋轉值
sensor.AddObservation(gameObject.transform.rotation.x);
//小球與平台的相對位置
sensor.AddObservation(ball.transform.position -gameObject.transform.position);
//小球剛體的速度
sensor.AddObservation(m_BallRb.velocity);
}
以上一共運用了8個觀察值,注意Vector3類型的變量算是3個觀察值(x,y,z)。
在Ball3DHardAgent項目里,與Ball3DAgent的區別就在於這里少了小球剛體速度的收集,從而導致前者在其他設置都相同的情況下,訓練效果不佳,如下圖。
可以看到平台的抖動很大,就是因為沒有考慮到小球的速度影響而導致訓練結果天差地別,因此在用ML-Agents的時候,需要嚴謹的考慮環境的觀測項,可能由於一個觀測項的增加或刪除,就導致最終學習結果的好壞,這里我也是慢慢才學習,這就是我為什么要先研究官方的示例,到時候最壞也可以照貓畫虎。
Agent動作反饋
這里是Agent的核心實現,觀測值通過Agent收集到Brain處,Brain再通過外部Python訓練環境反饋動作,再沿相同的路線返回到Agent的AgentAction(float[] vectorAction)
上(這里不知道我的想法對不對),具體的代碼如下。
public override void AgentAction(float[] vectorAction)
{
//控制平台繞Z軸、X軸旋轉的值
//用Mathf.Clamp()將響應的動作值限制到-2到2
var actionZ = 2f * Mathf.Clamp(vectorAction[0], -1f, 1f);
var actionX = 2f * Mathf.Clamp(vectorAction[1], -1f, 1f);
//平台繞Z軸旋轉響應
if ((gameObject.transform.rotation.z < 0.25f && actionZ > 0f) ||
(gameObject.transform.rotation.z > -0.25f && actionZ < 0f))
{
gameObject.transform.Rotate(new Vector3(0, 0, 1), actionZ);
}
//平台繞X軸旋轉響應
if ((gameObject.transform.rotation.x < 0.25f && actionX > 0f) ||
(gameObject.transform.rotation.x > -0.25f && actionX < 0f))
{
gameObject.transform.Rotate(new Vector3(1, 0, 0), actionX);
}
//gameObject.transform.Rotate(new Vector3(0, 0, 1), actionZ);
//gameObject.transform.Rotate(new Vector3(1, 0, 0), actionX);
//當小球在平台上,掉落或飛出平台,分別進行獎勵或懲罰
if ((ball.transform.position.y - gameObject.transform.position.y) < -2f ||
Mathf.Abs(ball.transform.position.x - gameObject.transform.position.x) > 3f ||
Mathf.Abs(ball.transform.position.z - gameObject.transform.position.z) > 3f)
{
SetReward(-1f);//懲罰1
Done();//此次訓練結束並重新開始,會調用AgentReset()
}
else
{
SetReward(0.1f);//在平台上的時候,每次動作都獎勵0.1
}
}
首先是平台對於旋轉的響應,我又將兩個if的條件去掉訓練了一下,發現平台訓練過程中比較不穩,抖動較大,因為只要一來值就讓平台旋轉,可能這里會造成平台一直在調整姿態的過程中,而源代碼中,以繞Z軸為例,只有在平台Z軸旋轉值<0.25f且actionZ>0、或平台Z軸旋轉值>0.25f且actionZ<0時才對平台的姿態進行動作,這樣就相當於設置了一個緩沖區間,不會讓平台不停調整姿態,而是根據小球情況來適當調整姿態。
這里附上兩次訓練的tensorboard。
紅色的是不加if條件的,藍色的是官方加if的。其實從數據來看,大的趨勢都差不多,不過我從訓練現象來看,確實官方加if之后訓練過程比較穩定。
后面的獎勵代碼中,有三個條件判斷小球應該受到懲罰。
-
(ball.transform.position.y - gameObject.transform.position.y) < -2f
小球與平台y方向上的差值小於2,如下圖:
這里可以看出是小球掉落到平台下邊,其實大多數情況是其他兩種情況。
-
Mathf.Abs(ball.transform.position.x - gameObject.transform.position.x) > 3f
和Mathf.Abs(ball.transform.position.z - gameObject.transform.position.z) > 3f
這兩種放一起將,先來看看小球的位置:
此時小球的x,z值都是3,可以看出小球剛好在x軸方向或者z方向滾出了平台邊緣。
所以,上面三種情況只要發生一種就對小球Agent做出-1懲罰,同時調用Done()
。
Agent復位
復位就比較簡單了,來看代碼:
public override void AgentReset()
{
//復位平台旋轉角度
gameObject.transform.rotation = new Quaternion(0f, 0f, 0f, 0f);
//令平台隨機繞x軸旋轉-10~10度
gameObject.transform.Rotate(new Vector3(1, 0, 0), Random.Range(-10f, 10f));
//令平台隨機繞z軸旋轉-10~10度
gameObject.transform.Rotate(new Vector3(0, 0, 1), Random.Range(-10f, 10f));
//小球剛體速度變為0
m_BallRb.velocity = new Vector3(0f, 0f, 0f);
//小球在y(相對平台高度)為4的地方,同時隨機x、z值出現
ball.transform.position = new Vector3(Random.Range(-1.5f, 1.5f), 4f, Random.Range(-1.5f, 1.5f))
+ gameObject.transform.position;
//Agent重置時,同時重置參數,[Obsolete]這里是指小球的質量和比例,其實我覺得這里沒必要,估計之后別的項目有用
//[new]這里重新設置泛化參數,具體見本博“ML-Agents(四)3DBall補充の引入泛化”
SetResetParameters();
}
這里的代碼比較簡單,注釋能看明白即可。
Agent手動設置
這里主要是當訓練模式為Heuristic Only
時調用,具體設置如下:
代碼如下:
public override float[] Heuristic()
{
var action = new float[2];
action[0] = -Input.GetAxis("Horizontal");
action[1] = Input.GetAxis("Vertical");
return action;
}
這里的代碼相當於我們輸入來控制動作向量空間的值,其實就是action[]數組,我們令action[0]控制平台繞x軸的旋轉,action[1]控制平台繞z軸的旋轉。
可以試一下,其實要保持小球在平台上還有點難度。
至此第一個例子就研究到這,有什么問題歡迎大家一起探討。
寫文不易~因此做以下申明:
1.博客中標注原創的文章,版權歸原作者 煦陽(本博博主) 所有;
2.未經原作者允許不得轉載本文內容,否則將視為侵權;
3.轉載或者引用本文內容請注明來源及原作者;
4.對於不遵守此聲明或者其他違法使用本文內容者,本人依法保留追究權等。