AIプログラムとかUnityゲーム開発について

探索や学習などを活用したAI系ゲームを作りたいと思います。

ml-agentsで3Dballの学習にY軸回転も追加してみる

強化学習ってのは水族館でのアシカの調教と同じで、うまくやったら餌をやり、失敗したら怒る学習
教師あり学習と違って、アシカがどう動くはアシカに任せて、結果だけで評価する
初期状態では基本的にはランダムに動く
成功したら、その成功に道筋すべての行動に報酬を与えて次回それをやるように強化する(失敗したら逆のことをする)
↑といってランダム行動は忘れない。一回の成功だけで行動は決められないので
報酬の与えかたは過去にたどるにつれて係数をかけて弱めたりするするんだけど、そういう調整はconfigのyamlに定義されている

学習にY軸回転を追加してみる

サンプルのソースを見るとボールを落とさない学習にY軸は使って無くて、X軸とZ軸だけ使ってるんで試しにY軸回転も追加してみた
f:id:yasu9780:20201015141354g:plain

学習にY軸回転が不要ならY軸はほとんど動かさない方に学習が進むかも?
XZ軸とXYZ軸で学習が収束するまでのステップを比較すればどっちが条件として望ましいかの比較もできるかも?
↑比較してみるとXZ軸だけで学習した方が学習進度も早く報酬100に達するのも速かった(XZ軸16.8万steps XYZ軸18万steps)

修正したのはbrainへステートを渡すCollectObservations()とアクションを定義するonActionReceived()と初期化のOnEpisodeBegin()の三つ

あとY軸を増やしたので、3DBallのAgentにaddされているBehaviorParametersのVector Observationを9にして、VectorActionを3に増やした

    public override void CollectObservations(VectorSensor sensor)
    {
        sensor.AddObservation(gameObject.transform.rotation.z);
        sensor.AddObservation(gameObject.transform.rotation.x);
        sensor.AddObservation(gameObject.transform.rotation.y);
        sensor.AddObservation(ball.transform.position - gameObject.transform.position);
        sensor.AddObservation(m_BallRb.velocity);
    }

    public override void OnActionReceived(float[] vectorAction)
    {
        var actionZ = 2f * Mathf.Clamp(vectorAction[0], -1f, 1f);
        var actionX = 2f * Mathf.Clamp(vectorAction[1], -1f, 1f);
        var actionY = 2f * Mathf.Clamp(vectorAction[2], -1f, 1f);

        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);
        }

        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);
        }

        if ((gameObject.transform.rotation.y < 0.25f && actionY > 0f) ||
            (gameObject.transform.rotation.y > -0.25f && actionY < 0f))
        {
            gameObject.transform.Rotate(new Vector3(0, 1, 0), actionY);
        }

        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);
            EndEpisode();
        }
        else
        {
            SetReward(0.1f);
        }
    }

    public override void OnEpisodeBegin()
    {
        gameObject.transform.rotation = new Quaternion(0f, 0f, 0f, 0f);
        gameObject.transform.Rotate(new Vector3(1, 0, 0), Random.Range(-10f, 10f));
        gameObject.transform.Rotate(new Vector3(0, 0, 1), Random.Range(-10f, 10f));
        gameObject.transform.Rotate(new Vector3(0, 1, 0), Random.Range(-10f, 10f));
        m_BallRb.velocity = new Vector3(0f, 0f, 0f);
        ball.transform.position = new Vector3(Random.Range(-1.5f, 1.5f), 4f, Random.Range(-1.5f, 1.5f))
            + gameObject.transform.position;
        //Reset the parameters when the Agent is reset.
        SetResetParameters();
    }

処理の流れとしては、

  1. OnEpisodeBegin()で状態の初期化
  2. CollectObservations()が、箱の向きとボールの位置・速度をBrain→Academy→TensorFlowへ報告
  3. TensorFlowからActionを受け取ってonActionReceived()で箱が回転と報酬処理

というループ処理と思う。
TensroFlow側が常駐してない状態でagentをPlayしてもソケット通信が失敗するのでActionVectorが戻らず、失敗して初期化を繰り返すだけ

要は、sensor(感覚神経)→TensorFlow(せき髄)→VectorAction(運動神経)

まださすがに脳とまではいきませんね。
SetReward(0.1f);のところはエンドルフィン出たぁって感じだろうけど


参考記事
enjoy-unity.net