Windows10でUnityとML-Agentsを使った強化学習 ～自分で新しく環境を作成して学習することろまで～

---
# はじめに

Windows10でUnityとML-Agentsというパッケージを使用して，強化学習を行うやり方を説明します．
基本的に[こちらのドキュメント](https://github.com/Unity-Technologies/ml-agents/blob/release_3_docs/docs/Readme.md)にそって説明を行いたいと思います．

この記事では次のようにボールにAgentを搭載し，四角いボックスを追いかけさせるプログラムを0から作成していきます．

![](https://i.imgur.com/k7yNiAv.gif)


## 目次

- [実行環境](#実行環境)
- [環境構築](#環境構築)
    - [Unityのインストール](#unityのインストール)
    - [Python(Anaconda3)のインストール](#pythonanaconda3のインストール)
    - [ML-Agents Release 3 のセットアップ](#ml-agents-release-3-のセットアップ)
- [サンプルプログラムの動作チェック](#サンプルプログラムの動作チェック)
- [新しい学習環境の作成と強化学習](#新しい学習環境の作成と強化学習)
- [さいごに](#さいごに)

---
---
# 実行環境

| | バージョン |
| -------- | -------- |
| Windows10 | 1909 |
| Unity | 2019.4.3f1 |
| Python(Anaconda3) | 3.7.6 |
| ML-Agents | Release 3 |

**ML-Agents**は現在(2020/07/14)で最新である`Release 3`を使用します．
`Release 3`ではUnityに関しては`2018.04`以降，Pythonに関しては`3.6.1`以上が対応しています．
Pythonの環境にAnacondaを使用する場合は関係ないですが，ML-Agentsのインストールの際に`pip3`が必要になります．

---
---
# 環境構築

## Unityのインストール

次のサイトにアクセスし，**Unity Hub**をダウンロートします．
**Unity Hub**はUnityのバージョン管理をしてくれるソフトウェアです．

https://unity3d.com/get-unity/download

![](https://i.imgur.com/WHTxVnb.png)

**Unity Hub**のインストールが完了したら，次にアカウントの登録を行います．
Unityのサイトでアカウントを作成し，サインインしてください．

![](https://i.imgur.com/MBbT7st.png)

次にライセンスの登録を行います．
今回は**Unity Personal**を使用するので次のように選びます．

![](https://i.imgur.com/xpUsDeG.png)

続いてUnity本体のインストールを行います．次の画面から適当なバージョンを選択してインストールしてください．もし**Visual Studio**がない場合，ここで自動的にインストールしてくれます．
インストールに結構時間がかかります．

![](https://i.imgur.com/UanNGET.png)


## Python(Anaconda3)のインストール

次のサイトにアクセスし，下の方のPython3.7 64-Bit Graphical Installerをダウンロードします．

https://www.anaconda.com/products/individual#windows
![](https://i.imgur.com/V8inZV0.png)

インストーラーがダウンロードできたら，後は流れでインストールしていきます．

途中にAnaconda3のパスを環境変数に登録するかしないかの確認が入りますが，特になにも無ければ環境変数に登録しても大丈夫だと思います(もうすでにPythonの環境を構築していたり複数のバージョンを管理しているなどがなければ...)が推奨されていないみたいです．

ここのチェックを付けない場合，自分で環境変数を通す必要があります(Anaconda Promptを使う場合は必要ないかも)．
今回は**PowerShell**を使って実行していきます．

![](https://i.imgur.com/bUsHt0Y.png)

### PowerShellでAnacondaを使用できるようにする

環境変数にパスが通っているとPowerShellでPythonが実行できるようになります．
しかし，Anacondaで仮想環境を作ってそこでいろいろしたいときに`conda activate`で仮想環境に入ったりするんですが，デフォルトのPowerShellだと上手く実行できません．

自分はPowerShellが好きなので，なんとしてでもPowerShellで実行したいです．
いろいろ探していたら次の記事でやり方が報告されていたので参考にしました．

[conda activateでエラーが出る話](https://qiita.com/8128/items/877caf785b20758ba7e0)

PowerShellで次のコマンドを実行します．

```bash
conda init powershell
```
ここでPowerShellを再起動したりするとエラーがでるようになると思います．
次に管理者権限でPowerShellを開き，次のコマンドを実行します．
実行しますか～と聞かれるのでYESを選択．

```bash
Set-ExecutionPolicy RemoteSigned
```

PowerShellを再起動して，次のように`(base)`と表示されたら成功です．

![](https://i.imgur.com/gctOzse.png)

### Anacondaに強化学習用の仮想環境を作成する

Anacondaが使えるようになったら，強化学習用の仮想環境を作成します．
仮想環境を作成することで，環境の切り替えや，壊れた際に簡単にやり直すことができます．

次のコマンドで仮想環境を作成します．

```bash
conda create -n ml-agents python=3.6
```

仮想環境を切り替える際は`conda activate <名前>`を実行します．

```bash
conda activate ml-agents
```

次のように`(ml-agents)`を表示されれば成功です．

![](https://i.imgur.com/vnLeseY.png)

## ML-Agents Release 3 のセットアップ

次のML-AgentsのReleaseのページから，ソースコードをダウンロードします．

https://github.com/Unity-Technologies/ml-agents/releases/tag/release_3

![](https://i.imgur.com/UglMnGv.png)

下の方に行くとボタンがあるので`Source code(zip)`を選択して，ダウンロードします．
そして，適当なフォルダに解凍してください．

![](https://i.imgur.com/B9znTS9.png)

コマンドラインからGitを実行できる人は以下のコマンドでRelease3をダウンロードすることができます．

```
git clone --branch release_3 https://github.com/Unity-Technologies/ml-agents.git
```

ここからは先程作成した仮想環境`(ml-agents)`に入った状態おこないます．

次のコマンドでPythonのmlagentsパッケージをインストールします．

```bash
pip3 install mlagents
```

次に先程GitHubからダウンロードして解凍したフォルダに移動して，PowerShellを開きます．
そして次のコマンドを実行します．

```bash
pip3 install -e ./ml-agents-envs
pip3 install -e ./ml-agents
```

---
---
# サンプルプログラムの動作チェック

ここではML-Agentsのサンプルの1つである`3DBall`の強化学習を行っていきます．
次のようにボックスが上にボールを乗せている状況を考えます．強化学習を行いボールを落とさないようにボックスにバランスをとらせることが目標です．

![](https://i.imgur.com/li0GuD9.gif)

それではやっていきましょう．
Unity Hubを開いて，**プロジェクト→リスト**に追加をクリックします．フォルダを開くダイアログがでるので先程ダウンロードした**ml-agents-release_3\Project**を指定します．

![](https://i.imgur.com/6mJlPym.png)

次にUnityのバージョンを更新します．**Unityバージョン**の欄をクリックして先程インストールしたバージョンを選択します．

![](https://i.imgur.com/9w1PfMw.png)

その状態で**Project**をクリックするとUnityが開かれるのですが，初回は次のようにプロジェクトをアップグレードしますか？というダイアログがでるので確認をクリックして進みます．

![](https://i.imgur.com/OKAp3GV.png)

プロジェクトが開かれたら，次のように**Projectビュー**から**ML-Agents→Examples→3DBall→Scenes**をダブルクリックで開き，**3DBall**というシーンをダブルクリックしてください．

![](https://i.imgur.com/lS5G5sk.png)

すると，青いボックスが12個並んだシーンが表示されます．こちらのシーンには最初からトレーニング済みの学習モデルがセットされているため，この状態で上の再生(Play)ボタンを押すと，しっかりとバランスを取る動作を行います．

今回は学習を行うので，トレーニング済みの学習モデルを一度プレハブから取り除きます．

取り除く際は下の手順のようにプレハブを操作することで，シーン全体のボックスの内容を変更することができます．

1. Projectビューから**ML-Agents→Examples→3DBall→Prefabs**をダブルクリックで開く
2. **3DBallのプレハブを**をダブルクリック．するとHierarchyビューに表示されます．
3. **Hierarchyビュー**から**Agent**を選択
4. **Inspectorビュー**から**Behabior Parameteresのモデルを選択**
5. モデルとして**None**を選択．

![](https://i.imgur.com/QOkjaah.png)

![](https://i.imgur.com/sHHIsnm.png)

これで学習済みモデルを引き剥がすことができました．
この状態で，再生(Play)ボタンを押すと，青いボックスを矢印キーで動かすことができるようになります．
試しにやってみます(すべてのボックスが同期して動くので次のGIFでは左上のボックスに注目しています)が結構難しいです．

![](https://i.imgur.com/twgONbW.gif)


それでは学習を行っていきましょう．
`PowerShell`でGitHubからダウンロートしてきた**ml-agents-release_3**フォルダを開きます．
そこで次のコマンドを実行します．
`run-id`は識別子のようなもので重複は許されません．この名前のフォルダに学習モデルが保存されます．

```bash
mlagents-learn config/ppo/3DBall.yaml --run-id=first3DBallRun
```
![](https://i.imgur.com/fTGFkPg.png)

実行して次の画面のようにUnityのロゴが出て，`Start training by pressing the Play button in the Unity Editor.`という表記がでたら，Unityの画面に戻り，再生(Play)ボタンを押してください．

![](https://i.imgur.com/AuoEcCN.png)

すると次のように自動で高速にボックスが動き，学習が始まります．
しばし待ちましょう(10分もしないくらい？)．
もし，途中で終了する場合はUnity側の再生ボタンを再度クリックすることで終了できます(PowerShell側からやると学習結果が消えます)．

![](https://i.imgur.com/oDOkLfZ.png)

正常に終了したら，学習は成功です．
学習結果は**ml-agents-release_3\results\first3DBallRun\3DBall.nn**に保存されます．

![](https://i.imgur.com/28D4VSE.png)

その学習結果を次の画面参考に，Projectビューから**ML-Agents→Examples→3DBall→TFModels**を開き，そこにドラッグ＆ドロップします．

![](https://i.imgur.com/adqJJOu.png)

すると次のように**3DBall 1**という名前で保存されます．

![](https://i.imgur.com/EuIokLa.png)

後は先程学習モデルを引き剥がしたときと同様にこの学習モデルを割り当てます．

1. Projectビューから**ML-Agents→Examples→3DBall→Prefabs**をダブルクリックで開く
2. **3DBallのプレハブを**をダブルクリック．するとHierarchyビューに表示されます．
3. **Hierarchyビュー**から**Agent**を選択
4. **Inspectorビュー**から**Behabior Parameteresのモデルを選択**
5. モデルとして**3DBall 1**を選択．

![](https://i.imgur.com/pT8RCHi.png)

そうしたら，再生ボタンを押して学習したモデルを確認してみましょう．
下のGIFではわかりにくいですが，最初からついていた事前学習済みモデルより，少し動きが荒い気がしましたが，ちゃんとボールを落とさないように学習しています！

![](https://i.imgur.com/MSTlSSw.gif)

また，訓練の過程をグラフィカルに観察することができます．
学習を開始すると`result`フォルダが作成されるので，それがあるフォルダで以下のコマンドを実行してください．

```bash
tensorboard --logdir=results --port=6006
```

すると以下のパスで学習過程を見ることができます．

http://localhost:6006/

![](https://i.imgur.com/PEZvRWa.png)

以上でサンプルプログラムの動作チェックは終了です．

---
---
# 新しい学習環境の作成と強化学習

ここでは強化学習を行うためにUnityのプロジェクトを作るところから自分でやっていこうと思います．

Unity Hubを開いてプロジェクトから新規作成を選択します．

![](https://i.imgur.com/O2r4wj1.png)

プロジェクト名を**RollerBall**にして作成をクリック．

![](https://i.imgur.com/7Uz8Y9q.png)

プロジェクトが作成されたら，最初のシーンの名前が**SampleScene**になっているので下のようにProjectビューから**RollerBall**にリネームします．

![](https://i.imgur.com/K9OwB67.png)

次にML-AgentsのUnityパッケージをインストールします．
左上のWindowタブから**Package Manager**を選択してください．

![](https://i.imgur.com/qv1gH6j.png)

Package Managerが開いたら左上の+ボタンから**Add package from disk**を選択してください．

![](https://i.imgur.com/ZtCdDZS.png)

そうしたら**ml-agents-release_3\com.unity.ml-agents\package.json**を開いてください．
インポートに少し時間がかかります．

![](https://i.imgur.com/LIK3rt0.png)

次にゲームオブジェクトを配置していきます．
Hierarchyビューで右クリックして，まず最初に次のように**Plane**を追加します．
そして名前を**Floor**にリネームしてください．

![](https://i.imgur.com/aT2azfx.png)

同様に**Cube**を追加します．

![](https://i.imgur.com/ZWfjEnm.png)

そして名前を**Target**にし，Positionを(3,0.5,3)に変更します．
この時のInspectorビューは下のようになります．

![](https://i.imgur.com/ojrzbay.png)

同様に**Sphere**を追加します．

![](https://i.imgur.com/qaOh7R9.png)

そして名前を**RoolerAgent**にして，positionを(0,0.5,0)に変更します．
この時のInspectorビューは下のようになります．

![](https://i.imgur.com/YlXck5M.png)

次に**RollerAgent**のInspectorビューの下にいくと，**Add Component**というボタンがあるのでクリックして，下の図のように**Rigidbody**を追加してください．

![](https://i.imgur.com/khmQ9kn.png)

これでゲームオブジェクトの追加は終わりで，最後にHierarchyビューで**EmptyなGameObject**を作成し，TrainingAreaとリネームして，そこに先程作成したゲームオブジェクトたちを入れておきます．
このようにすることで，管理が楽になります．

![](https://i.imgur.com/C7CrEAo.png)

次にスクリプトの追加を行っていきます．
**RollerAgent**のInspectorビューの下の**Add Component**をクリックし，**New Script**で新しくスクリプトを追加します．
名前を次のように**RollerAgent**としてください．

![](https://i.imgur.com/ZEVMQai.png)

するとRollerAgentにスクリプトが適用されます．
このスクリプトを編集したいので**Projectビュー**から，**RollerAgent**というスクリプトが追加されていると思うので，ダブルクリックして開いてください．デフォルトだとVisual Studioが起動します．

![](https://i.imgur.com/iIjTTai.png)

Visual Studioが起動したら，全文を以下のスクリプトに書き換えてください．

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

public class RollerAgent : Agent
{
    Rigidbody rBody;
    void Start()
    {
        rBody = GetComponent<Rigidbody>();
    }

    public Transform Target;
    public override void OnEpisodeBegin()
    {
        if (this.transform.localPosition.y < 0)
        {
            // If the Agent fell, zero its momentum
            this.rBody.angularVelocity = Vector3.zero;
            this.rBody.velocity = Vector3.zero;
            this.transform.localPosition = new Vector3(0, 0.5f, 0);
        }

        // Move the target to a new spot
        Target.localPosition = new Vector3(Random.value * 8 - 4,
                                           0.5f,
                                           Random.value * 8 - 4);
    }

    public override void CollectObservations(VectorSensor sensor)
    {
        // Target and Agent positions
        sensor.AddObservation(Target.localPosition);
        sensor.AddObservation(this.transform.localPosition);

        // Agent velocity
        sensor.AddObservation(rBody.velocity.x);
        sensor.AddObservation(rBody.velocity.z);
    }

    public float speed = 10;
    public override void OnActionReceived(float[] vectorAction)
    {
        // Actions, size = 2
        Vector3 controlSignal = Vector3.zero;
        controlSignal.x = vectorAction[0];
        controlSignal.z = vectorAction[1];
        rBody.AddForce(controlSignal * speed);

        // Rewards
        float distanceToTarget = Vector3.Distance(this.transform.localPosition, Target.localPosition);

        // Reached target
        if (distanceToTarget < 1.42f)
        {
            SetReward(1.0f);
            EndEpisode();
        }

        // Fell off platform
        if (this.transform.localPosition.y < 0)
        {
            EndEpisode();
        }
    }

    public override void Heuristic(float[] actionsOut)
    {
        actionsOut[0] = Input.GetAxis("Horizontal");
        actionsOut[1] = Input.GetAxis("Vertical");
    }
}
```
最後に少しだけML-Agentsのコンポーネントを追加して終了です．

1. RollerAgentコンポーネントのInspectorビューの下からAdd Componentをクリックします．
2. `Decision Requester`を検索して追加します．
3. `Decision Period`を10に変更します．
4. Hierarchyビューから**Targetオブジェクト**をRollerAgentスクリプトのTargetフィールドにドラッグ＆ドロップします．
5. RollerAgentコンポーネントのInspectorビューの下からAdd Componentから`Behavior Parameters`を追加します．この時もうすでに追加されている場合があります．
6. `Behavior Parameters`のパラメータを次のように変更します．
    - `Behavior Name` to RollerBall
    - `Vector Observation` > `Space Size` = 8
    - `Vector Action` > `Space Type` = Continuous
    - `Vector Action` > `Space Size` = 2

以上の変更をしたあとにRollerAgentのInspectorビューが次のようになっていればOKです．

![](https://i.imgur.com/kJJASNo.png)

これでUnity上での作業は終了です．
サンプルプログラム同様に手動でボールを動かすことができます．この状態で再生ボタンを押して矢印キーで動かしてみましょう．結構難しいです．

次に強化学習用のハイパーパラメータ用のConfigファイルを作成します．
UnityのRollerBallプロジェクトのフォルダに`config`フォルダを作成して，そこに新しく`rollerball_config.yaml`を作成してください．内容は以下をコピペしてください．

```yaml:rollerball_config.yaml
behaviors:
  RollerBall:
    trainer_type: ppo
    hyperparameters:
      batch_size: 10
      buffer_size: 100
      learning_rate: 3.0e-4
      beta: 5.0e-4
      epsilon: 0.2
      lambd: 0.99
      num_epoch: 3
      learning_rate_schedule: linear
    network_settings:
      normalize: false
      hidden_units: 128
      num_layers: 2
    reward_signals:
      extrinsic:
        gamma: 0.99
        strength: 1.0
    max_steps: 500000
    time_horizon: 64
    summary_freq: 10000
```

各ハイパーパラメータについては検索するといろいろ詳細な情報が得られるので検索してみてください．

準備が完了したので早速学習を行っていきましょう．
サンプルプログラムと同様にまずPowerShellを開き先程作成したAnacondaの`ml-agens`仮想環境に入ります．

そして，次のコマンドを実行したあと，Unity側で再生ボタンを押して学習を開始します．

```bash
mlagents-learn config/rollerball_config.yaml --run-id=RollerBall
```

![](https://i.imgur.com/D9y77eh.png)

試行回数が50万回に設定されていますが，ログを見ていたら10万回くらいには平均報酬が収束していたので，途中で学習をやめ，これを学習済みモデルとしました(学習をやめるときはUnity側で停止するようにしましょう)．
下は終了時点での出力です．

![](https://i.imgur.com/CzUqNKs.png)

resultフォルダに結果が保存されているので，サンプルプログラムを動かしたときと同様にドラッグ＆ドロップでProjectビューの適当なところにインポートします．

![](https://i.imgur.com/QKLnIgx.png)

学習済みモデルがインポートできたら，これをAgentに適用します．

1. Hierarchyビューから**RollerAgent**を選択する
2. Inspectorビューから**Behabior Parameter→Model**をクリック
3. 先程追加した**RollerBall**を選択

![](https://i.imgur.com/o85WQBs.png)

以上の操作をやって再生ボタンを押してみましょう．
はじめに示したように，ボールがボックスを追いかけるAgentを作成することができたでしょうか？

---
---
# さいごに

UnityとML-Agentsを使って強化学習を行う環境を構築してきました．
シンプルな問題設定の場合，意外と簡単に実行できることがわかりました．
今後はこれらを使って様々なモデルの強化学習を行っていきたいと思います．