ML-Agents Python Low Level API入門：強化学習環境の作成その2

# はじめに
今回は、Unity上で環境の作成を行います。まだML-Agentsには触れずに純Unityでの実装を行いますが、本質的に強化学習のための実装を行うので、この記事での実装の考え方はML-Agentsを使うときにも有効です。

# シーンの作成
Unityで実際に環境を実装していきましょう。今回の記事ではサンプルとして、赤い箱に当たらないようにジャンプで避けるというタスクを学習させてみます。JumpEnvironmentというシーンファイルを作成して、そこにゲームオブジェクトを追加していきます。
# Training Areaの作成
Training Areaとは、エージェントが活動するひとつの環境です。強化学習の高速化のため、ML-Agentsでは並行して同じタスクを実行するのですが、そのひとつひとつの環境を表すのがTraining Areaです。UnityにおけるTraining Areaの実装は、単なるEmpty GameObjectです。Training AreaのTransformはResetしておきます。この事情により、Agentなどの処理で座標を扱う場合には基本的にlocalPositionを用います。
# オブジェクトの配置
このステップでは、通常のUnityによる開発を行います。ML-Agentsの強みのひとつはこのステップにおいてUnityという成熟した、また慣れ親しんだ技術を使えることでしょう。
Planeを作成してFloorと命名し、Transformは以下のようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412657/f15c7b52-209f-7a40-eb73-95e20c36047d.png)
次に、Cubeを作成して赤いMaterialを適用して、Enemyと名付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412657/d085dc17-d619-e187-4f3f-bb9be02bb830.png)
最後に、Cubeを作成してAgentと名付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412657/4cb0d144-6b39-6846-967b-3601b20dd4db.png)
全体としては、このようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412657/e0b272fe-b709-1a14-0b04-f5e4fd58ff6b.png)

# スクリプトの作成
このステップでは、通常のUnityで同じようにして環境とエージェントの行動を実装していきます。まず、エージェントの行動を実行するControlPlayerクラスを作成します。エージェントは「移動」と「ジャンプ」という行動をとれます。
```ControlPlayer.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ControlPlayer : MonoBehaviour
{
    Rigidbody rBody;
    public float Speed;
    // 報酬を保存しておく
    public List<float> Rewards;

    private void Start()
    {
        rBody = GetComponent<Rigidbody>();
        Rewards = new List<float>();
    }

    private void FixedUpdate()
    {
        // テスト用処理
        if (Input.GetKey(KeyCode.Z))
        {
            Jump();
        }
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            Move(-1);
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
            Move(1);
        }

        // 報酬用処理
        if(transform.localPosition.y < -0.49f)
        {
            Rewards.Add(-1);
        }
    }

    // 行動用メソッド
    public void Jump()
    {
        if(transform.localPosition.y > 0.51f) { return; }
        rBody.AddForce(Vector3.up * 6, ForceMode.Impulse);
    }

    // 行動用メソッド
    public void Move(float direction)
    {
        if(direction == 0) { return; }
        // 正規化
        direction /= Mathf.Abs(direction);
        rBody.velocity = new Vector3(Speed * direction, rBody.velocity.y, 0);
    }

    // 報酬用メソッド
    private void OnCollisionEnter(Collision collision)
    {
        // 敵との衝突には-1の報酬を与える
        if(collision.gameObject.name != "Enemy") { return; }
        Rewards.Add(-1);
    }

    // 観測用メソッド
    public Vector2 GetVelocity()
    {
        return new Vector2(rBody.velocity.x, rBody.velocity.y);
    }

    // 観測用メソッド
    public Vector2 GetPosition()
    {
        return new Vector2(transform.localPosition.x, transform.localPosition.y);
    }
}
```
次に敵の挙動をControlEnemyクラスに作成します。内容は単純に反復移動するというものです。
```ControlEnemy.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ControlEnemy : MonoBehaviour
{
    private Rigidbody rBody;
    private float direction;
    public float Speed = 1;

    void Start()
    {
        rBody = GetComponent<Rigidbody>();
        direction = 1;
    }

    // 環境の処理を実装
    void FixedUpdate()
    {
        rBody.velocity = new Vector3(direction * Speed, 0, 0);
        if(transform.localPosition.x > 5.5f)
        {
            direction = -1;
        }
        if(transform.localPosition.x < -5.5f)
        {
            direction = 1;
        }
    }

    // 観測用メソッド
    public float GetPosition()
    {
        return transform.localPosition.x;
    }

    // 観測用メソッド
    public int GetDirection()
    {
        return Mathf.RoundToInt(direction);
    }
}
```

# 報酬の決定
強化学習の設計のうち、報酬設計は大きな要素です。どのような行動が望ましいのかをエージェントに伝える唯一の手段が報酬だからです。今回は「落下」と「敵キャラとの接触」に対してペナルティ-1を与えます。これによりエージェントは落下と敵キャラとの接触を避ける方向に学習します。実装としては```ControlPlayer```の```Reward```フィールドに報酬をを保存しています。

# 観測の実行
今回のエージェントは「自分の座標」「敵の座標」「自分の速度」「敵の移動方向」の情報を使って環境を観測します。これを行うために、```ControlPlayer```と```ControlEnemy```の両方に観測用メソッドを追加しています。

# スクリプトのアタッチ
```ControlPlayer```をAgentゲームオブジェクトにアタッチして、```ControlEnemy```をEnemyゲームオブジェクトにアタッチしましょう。Playモードで実行してみると、実際にテスト用の挙動に従って動くことが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412657/0e61a536-78f0-d039-1a3f-8bef73acf1ec.png)

# おわりに
今回はUnityを使った環境の実装を行いました。このステップではML-Agentsは全く出てきませんでしたが、報酬や観測など、多くの重要な要素があるので、非常に重要なステップです。次回はML-Agentsを使って今回作成した環境でML-Agentsが動くように設定します。