概要
Unity ML-Agentsで強化学習させるのに、これまでサンプルを利用していたのですが、Agentや報酬をどうやって設定すればよいのか、いまいち理解できてなかったので、自分でゲームを作ってみました。
ちなみにゲーム開発経験はありません。
なので、ベースとなるゲームについては下記チュートリアルをほぼそのまま参考にさせていただきました。感謝!
【Unity入門】60分で作るシューティングゲーム 第1回
http://nn-hokuson.hatenablog.com/entry/2016/07/04/213231
実際に強化学習させている様子です。
2DシューティングをUnity ML-Agentsで強化学習させてみた。
— 甲斐甲@GCPとUnity勉強中 (@k_aik_ou) 2018年8月17日
いい感じに動くようになった! pic.twitter.com/b9IdZB32bK
仕様
ゲームの仕様です。まだ実装できてないものもあります。
強化学習をさせてながめてて面白くなりそうな仕様にしました。
- 2D
- シューティング
- ゲームオーバーなし
- 自機からミサイルたくさん発射される
- 自機は左右に移動できる
- 自機が移動すると勝手にミサイルが発射される
- 隕石的なものがランダムに落ちてくる
- 隕石は大きさと質量がランダム
- 隕石にミサイルが当たると破壊できる
- 隕石の質量によって破壊に必要なミサイル数が変わる
- 隕石にミサイルが当たると得点が入る
- 自機に隕石が当たるとマイナス得点が入る
- 獲得した得点によって隕石の量が増える
- 獲得した得点によってミサイルの発射頻度が変わる
- 得点が画面上部に表示される
- 得点はリセットされず、維持される
- 得点の桁数が増えると画面表示量が増えて邪魔になる
手順
GitHubに作成したゲームを置いてます。READMEを参照ください。(なげやり
強化学習させるのにしたこと
本題です。ゲームを作成するところまでは良かったのですが、Unity ML-Agentsで強化学習させるのに、試行錯誤したので、ポイントをまとめてみます。
Unity ML-Agentsのバージョンは0.4.0になります。
オブジェクトの配置とスクリプト
AcademyオブジェクトとBrainは親子関係になるようにGameObjectを新規作成して配置します。
Brainにはml-agents/unity-environment/Assets/ML-Agents/Scripts/Brain.csを紐つけます。
Academyにはml-agents/unity-environment/Assets/ML-Agents/Examples/Template/Scripts/TemplateAcademy.csをコピペしてファイル名とクラス名を変更したコードを紐つけます。
RocketオブジェクトにはチュートリアルだとRocketControllerスクリプトを用意して紐付けれているけれど、これをml-agents/unity-environment/Assets/ML-Agents/Examples/Template/Scripts/TemplateAgent.csをコピペして紐つけなおします。
状態取得
下記記事にあるRayPerception
を利用して、隕石の位置を検知しようとしましたが、2Dには対応していませんでした。
【Unity ML-Agents】強化学習で物体を避ける
https://qiita.com/God_KonaBanana/items/e56fcee3ae544be7dfca
なので、泥臭くロケットと隕石の位置を自前で取得してAddVectorObs
で設定しています。
状態数は固定である必要があるので、最後に足りない分を0で埋めてます。うーん泥臭い。
隕石オブジェクトを取得するにはオブジェクトにTagを設定しています。
public override void CollectObservations()
{
AddVectorObs(transform.position.x);
AddVectorObs(transform.position.y);
var rockCount = 1;
foreach (var rock in GameObject.FindGameObjectsWithTag("Rock"))
{
AddVectorObs(rock.transform.position.x);
AddVectorObs(rock.transform.position.y);
AddVectorObs(rock.GetComponent<RockController>().endurance);
if (rockCount++ >= 20)
{
break;
}
}
for (int i = rockCount;i <= 20;i++)
{
AddVectorObs(0f);
AddVectorObs(0f);
AddVectorObs(0f);
}
}
報酬設定
RocketAgent内で報酬設定するにはAddReward
をそのまま呼べるのですが、隕石を破壊した際にも報酬を設定したい。となると玉オブジェクトに実装があるので、どうしようと考えて、以下のようにRocketAgentsにpublicなメソッドを追加して対応しました。
public void Rocket_AddReward(float reward)
{
AddReward(reward);
if (reward < 0)
{
Done();
}
}
GameObject.Find("Rocket").GetComponent<RocketAgent>().Rocket_AddReward(3f);
スピード設定
Academyオブジェクトで学習時のスピードを指定できますが、なにも対応していないと、隕石の発生頻度はあがりつつも、落下スピードはそのままで、超大量の隕石が発生。。。とか挙動が面白くなります。
なので、速度設定している箇所にTime.timeScale
を追加して対応しました。
void Start () {
this.fallSpeed = (0.01f + 0.01f * Random.value) * Time.timeScale;
this.rotSpeed = (5f + 3f * Random.value) * Time.timeScale;
this.endurance = 1f + Random.value * 5f;
}
というわけで、強化学習させるための2Dシューティングゲームを作ることができました。
ただ、学習をさせてみると、まだ報酬設定やハイパーパラメータの設定がいまいちなようで、まだまだ詰めが必要な状況です。
興味がある方はぜひここからチューニングしてみてくださいね^^