Unity ML-Agents を試してみた

# 前提
### 環境
- Unity 2019.4.9f1 (LTS)
- ML Agents 1.3.0 (Preview)
- ML Agents Extension 0.0.1 (Preview)
- Anaconda Python 3.8
- Windows 10

### 次の記事
 ⇒ 「[Unity ML-Agentsでターン制ゲームを作る](https://qiita.com/tetr4lab/private/b02eca793336ed63429f)」

### 記事の範囲
- 開発環境の導入と基本的な理解までを行います。
- [Unity](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Background-Unity.md)やC#については説明しません。
- [報酬を用いた強化学習の概念や機械学習そのものについて](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Background-Machine-Learning.md)は説明しません。
- Anaconda、Python、[TensorFlow](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Background-TensorFlow.md)については触れません。
- この記事には筆者の解釈が含まれます。事実と異なるようでしたらご指摘いただけると助かります。

# リソース
- [UnityHub](https://unity3d.com/jp/get-unity/download)
    - [Archive](https://unity3d.com/jp/get-unity/download/archive)
    - [LTS](https://unity3d.com/jp/unity/qa/lts-releases)
- [Anaconda](https://www.anaconda.com/products/individual#Downloads)
- [ML-Agents (GitHub)](https://github.com/Unity-Technologies/ml-agents)
    - [導入](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Installation.md)
    - [入門ガイド](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Getting-Started.md)
    - [新しい学習環境を作る](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Learning-Environment-Create-New.md)
    - [学習環境の例](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Learning-Environment-Examples.md)
    - [エージェント](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Learning-Environment-Design-Agents.md)
    - [学習環境の設計](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Learning-Environment-Design.md)
    - [トレーニング構成ファイル](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Training-Configuration-File.md)
    - [独立実行可能な学習環境](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Learning-Environment-Executable.md)
    - [TensorBoardを使用してトレーニングを観察する](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Using-Tensorboard.md)
    - [ドキュメント一覧](https://github.com/Unity-Technologies/ml-agents/tree/master/docs#unity-ml-agents-toolkit-documentation)

# 導入
### Unity
- [Unity Hubをダウンロード](https://unity3d.com/jp/get-unity/download)して導入します。
- Unity Hubを開き、`インストール`から新しいLTS版を導入します。
    - 最初は2018.4.x (LTS)を試したのですが、頻繁にエディタが落ちたので、2019.4.x (LTS)にしました。

### Anaconda
- [ダウンロード](https://www.anaconda.com/products/individual#Downloads)してデフォルト設定で導入します。
    - パスがどうのと尋ねられるけど、何も変えずにOKするだけです。
- Anaconda Navigatorを起動して、自動設定が完了したらNavigatorを終了します。
- 開発では、スタートメニューの`Anaconda Prompt`を使用します。(Windows)

### ML-Agents
- [ガイダンス](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Installation.md)に従って導入します。
    - リポジトリをクローンするかダウンロードします。
        - 全体が開発環境になります。
        - 中の`Project`フォルダのテンプレートをベースにUnityプロジェクトを作ります。
            - フォルダ名(`Project`)は変更可能です。(そのままで複数の機械学習プロジェクトを作ると、UnityHubに同じ名前が並ぶので…)
    - Unityのパッケージマネージャで、必要なUnityパッケージを導入します。
    - `pip3`で、必要なPythonパッケージを導入します。
        - スタートメニューの`Anaconda Prompt`を使用します。(Windows)

# 入門ガイドを試す
- まずは、「[入門ガイド](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Getting-Started.md)」を試します。
    - 再学習も試してみましょう。
        - スタートメニューの`Anaconda Prompt`を使用します。(Windows)
            - まず、カレントディレクトリをUnityプロジェクトの親フォルダ(すなわちML-Agentsのリポジトリのルート)へ移動します。
            - 通常、初めての学習
                - `mlagents-learn config/ppo/3DBall.yaml --run-id=first3DBallRun`
            - 中断からの再開
                - `mlagents-learn config/ppo/3DBall.yaml --run-id=first3DBallRun --resume`
            - 以前のファイルに上書き
                - `mlagents-learn config/ppo/3DBall.yaml --run-id=first3DBallRun --force`
            - アスキーアートのUnityロゴに続き`Start training by pressing the Play button in the Unity Editor.`と表示されたら、エディタで再生ボタンを押します。
        - 学習を中断する場合はコンソール(`Anaconda Prompt`)で`Ctrl+C`を押します。するとエディタの再生が自動的に停止します。
            - いきなりエディタの再生を停止しても、コンソール側でセーブされるように見えますが、推奨されていないようです。
        - 学習結果の`~.nn`ファイルを`Result`フォルダから`Asset/TFModels`フォルダへコピーします。
            - 初めてなら、エージェントコンポーネントにファイルを設定します。
    - より多くの例が見たければ、[学習環境の例](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Learning-Environment-Examples.md)をあたります。

# 学習環境を新たに作ってみる
- 次に、「[新しい学習環境を作る](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Learning-Environment-Create-New.md)を」試します。
    - エージェントは下記のような感じになりました。
    - トレーニング構成ファイル(`~config.yaml`)はサンプルのまま使いました。

```cs:RollerAgent.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Sensors;

public class RollerAgent : Agent {

	[SerializeField] private Transform target = default; // ターゲット
	[SerializeField] private float forceMultiplier = 10; // 作用力係数
	private Rigidbody rBody; // エージェントの剛体
	private const float reachMargin = 1.42f; // 許容する接触誤差

	/// <summary>オブジェクトの初期化</summary>
	private void Awake () {
		rBody = GetComponent<Rigidbody>();
	}

	/// <summary>エージェントの初期化とリセット</summary>
	public override void OnEpisodeBegin () {
		if (transform.localPosition.y < 0) {
			// エージェントが落ちていたら初期化
			rBody.angularVelocity = Vector3.zero;
			rBody.velocity = Vector3.zero;
			transform.localPosition = new Vector3 (0, 0.5f, 0);
		}
		// ターゲットを新しい無作為な位置へ移動
		target.localPosition = new Vector3 (Random.value * 8 - 4, 0.5f, Random.value * 8 - 4);
	}

	/// <summary>環境の観察</summary>
	public override void CollectObservations (VectorSensor sensor) {
		// ターゲットとエージェントの位置
		sensor.AddObservation (target.localPosition);
		sensor.AddObservation (transform.localPosition);
		// エージェントの速度
		sensor.AddObservation (rBody.velocity.x);
		sensor.AddObservation (rBody.velocity.z);
	}

	/// <summary>行動と報酬の割り当て</summary>
	public override void OnActionReceived (float [] vectorAction) {
		// 行動, size = 2
		var controlSignal = Vector3.zero;
		controlSignal.x = vectorAction [0];
		controlSignal.z = vectorAction [1];
		rBody.AddForce (controlSignal * forceMultiplier);
		// 報酬
		var distanceToTarget = Vector3.Distance (transform.localPosition, target.localPosition);
		// ターゲットへの到達
		if (distanceToTarget < reachMargin) {
			SetReward (1.0f);
			EndEpisode ();
		}
		// プラットフォームからの落下
		if (transform.localPosition.y < 0) {
			EndEpisode ();
		}
	}

	/// <summary>環境のテスト</summary>
	public override void Heuristic (float [] actionsOut) {
		actionsOut [0] = Input.GetAxis ("Horizontal");
		actionsOut [1] = Input.GetAxis ("Vertical");
	}

}
```

# 課題と対処
以下のような課題が生じ対処しました。

### Windowsで実行形式での学習をしようとしても起動できないと言われる
「[独立実行可能な学習環境](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Learning-Environment-Executable.md)」に従って、`mlagents-learn`の引数`--env=<env_name>`にビルドした**フォルダ**を指定しましたが、「mlagents_envs.exception.UnityEnvironmentException ~ Couldn't launch」などと出て学習を開始できません。

#### 対処
`<env_name>`に実行**ファイル**のフルパス(`/`区切り)を指定しました。
拡張子はあってもなくても大丈夫でした。

### `EndEpisode ()`でフリーズする
エディタごとフリーズしました。

#### 対処
`EndEpisode ()`は内部で`OnEpisodeBegin  ()`を呼び出します。
そのため、`OnEpisodeBegin  ()`の中で`EndEpisode ()`を呼ぶと再入が発生します。

# 応用できるようにさらに学ぶ
- 紹介されている例は、いずれも3D空間上の挙動を学習するものですが、「[エージェント](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Learning-Environment-Design-Agents.md)」と「[学習環境の設計](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Learning-Environment-Design.md)」を学ぶことで、ターン制ゲームへの道筋が見えるようになりました。
- 「[トレーニング構成ファイル](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Training-Configuration-File.md)」を学ぶことで、独自の`~config.yaml`ファイルを作れるようになります。
- 「[独立実行可能な学習環境](https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Learning-Environment-Executable.md)」を学ぶことで、長時間の学習の際にもエディタを占有されずに済むようになります。
- 学習を最適化し、より良い結果を得るためには、機械学習そのものについてより深く学ぶ必要があるようです。