Unity
機械学習
強化学習
ゲーム制作
ML-Agents

自作シンプル物理演算ゲームを強化学習で攻略させる【Unity ML-Agents v3.0】

自作シンプル物理演算ゲームを強化学習で攻略させる【Unity ML-Agents v3.0】

以前から機械学習などに興味があったものの、難しそうでためらっていたのですが、Unityで強化学習ができるML-Agentsというものがあると知り、理論についてほぼ何も知識のない自分でも、なんだかいけそうだと思って試してみました。

スクリーンショット 2018-04-25 23.17.07.png

まずは公式サンプルゲームを動かしてみたのですが、何か自作ゲームを学習させてみないと理解が進まないと思い、なるべくシンプルで最小限の構成になるようなゲームを考えました。そして、せっかくUnityを使うので物理演算も入れたい。

bounce-player.mov.gif

そこで考えたのが、上から降ってくる物体をヘディングのようにバウンドさせるゲームです。人間が手動でプレイするとこんな感じになります。これを強化学習させて攻略させてみました。

これ以下の内容はML-Agentsの公式サンプルを動かすところまで出来る方が対象です。Qiitaにも他の方が書いた記事があるので参考にして下さい。

ゲームを作る

スクリーンショット 2018-04-25 10.18.07.png

左右の壁とボール(小)、Player(ボール大)のみです。ボールは反発係数1にしてあるのでよく弾みます。というか、弾むたびに微妙に動きが大きくなります。なぜ?でも割と面白いのでそのままにしています。何度も弾ませたり、壁を使って連続で弾ませると、壁を超えてしまいます。それをクリア条件にすればよかった、と後から思いつきました、が本題ではないので省いてます。

このゲームのセットを1つのAgentとして、8つのAgentsを配置し学習を開始します。

シーンをスタートすると、Playerの上からボールが降ってきて、下の大きいボール(Player)で跳ね返します。操作は左右キーのみです。
→MoveAgentに記述

ボールをPlayerのy座標よりも下に落とすと、Done()が呼ばれてリセットされます。
→AgentActionに記述

取得する状態数は1つだけ。ボールとPlayerのx座標の相対位置を取得します。
→CollectObservations()に記述

報酬はボールを跳ね返したら1点追加します。
→OnCollisionEnter()にSetReward(1f)を記述

ボールを落とした場合に報酬を減らす方法も試しましたが、うまくいかなかったのでコメントアウトしています。

BounceAgent.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BounceAgent : Agent {

    public float speed;
    public GameObject ball;

    public override void CollectObservations()
    {
        AddVectorObs(ball.transform.position.x - gameObject.transform.position.x);

        //状態数は少なくても上手くいったため、削除
        // AddVectorObs(gameObject.transform.position);
        // AddVectorObs(ball.transform.GetComponent<Rigidbody>().velocity);
    }

    void OnCollisionEnter(Collision col)
    {
        if (col.gameObject.tag == "ball")
        {
            //衝突する度に報酬を与える
            SetReward(1f);
        }
    }
    public override void AgentAction(float[] vectorAction, string textAction)
    {
        MoveAgent(vectorAction);

        //以下のような報酬の与え方でもよいが、単に衝突時に報酬を与えてもうまくいくため削除
        // if (Mathf.Abs(ball.transform.position.x - gameObject.transform.position.x) < 0.2f)
        // {
        //     SetReward(0.1f);
        // }
        // else{
        //     SetReward(-0.1f);
        // }

        if ((ball.transform.position.y - gameObject.transform.position.y) < 0)
        {
            //ゲームオーバー時に報酬を引く方法だとうまくいかなかった
            // SetReward(-10f);
            Done();
        }

        Monitor.Log ("Reward", reward);
    }

    public override void AgentReset()
    {
        ball.transform.position = new Vector3(Random.Range(-0.2f,0.2f),1.5f,0) + gameObject.transform.position;
        ball.GetComponent<Rigidbody>().velocity = new Vector3(0f, 0f, 0f);
    }

    public void MoveAgent(float[] act)
    {
        int action = Mathf.FloorToInt(act[0]);

        //アクション
        if (action == 1)
        {
            transform.Translate(-speed, 0, 0);
            // AddReward(-0.01f);
        }
        if (action == 2)
        {
            transform.Translate(speed, 0, 0);
            // AddReward(-0.01f);
        }

        //移動制限
        if (transform.localPosition.x < -1.5f)
        {
            transform.localPosition = new Vector3(-1.5f, 0, 0);
        }
        if (1.5f < transform.localPosition.x)
        {
            transform.localPosition = new Vector3(1.5f, 0, 0);
        }
    }
}

学習結果

スクリーンショット 2018-04-25 8.56.12.png

10万回学習させたところ、7回ぐらい跳ね返せるようになりました。

Bounce_learned_trim.mov.gif

スクリーンショット 2018-04-25 8.57.02.png

その他の値はこのようになっています。
ただ、これらが何を意味しているのかよくわかっていないので、分かる方はぜひ教えてください。

改良点と課題

今回の学習結果では、ボールのx座標に追従するような動きになっています。このような動きは、わざわざ強化学習でなく、コードを書くだけでも似たような動きが実現できると思います。

より人間的な動きとしては、落下地点を予測してあらかじめ移動しておくものになるはずです。そのために、状態数や報酬の与え方を工夫する必要が有る、ということを実感できました。

また、今回WebGL版をビルドしようとしましたが、エラーが出てビルドできませんでした。これについても詳しい方、コメント頂けると幸いです。

制作物

macでとりあえず動きを見たい人用のappです。
http://mixsandwich.info/Bounce.zip

ソースコードです。スクリプト見たり、ビルド&学習させたい時用。
https://github.com/mixsandwich/Bounce