DockerでUnity ML-Agentsを動作させる(v0.6.0対応)

## 追記

v0.9.1で動作させる方法をまとめました。

DockerでUnity ML-Agentsを動作させる(v0.9.1対応) - Qiita
https://qiita.com/kai_kou/items/0c2f3c7d22363fd91e4e

## はじめに

以前、v0.5.0で動作させる方法をまとめましたが、v0.6.0がリリースされたので、改めてUnity ML-Agents(v0.6.0)をDocker上で動作させてみました。

v0.5.0で動作させたいという方は下記をご参考ください。

DockerでUnity ML-Agentsを動作させる(v0.5.0対応) - Qiita
https://qiita.com/kai_kou/items/df3f3518a5a27b66f617


UnityやUnity ML-Agentsの環境構築などは下記をご参考ください。

Macでhomebrewを使ってUnityをインストールする(Unity Hub、日本語化対応)
https://qiita.com/kai_kou/items/445e614fb71f2204e033

MacでUnity ML-Agentsの環境を構築する(v0.6.0対応) - Qiita
https://qiita.com/kai_kou/items/2a6545d1f9d83178d0c0


## 手順

基本的には公式のドキュメントに沿えばよい感じです。

Using Docker For ML-Agents
https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Using-Docker.md


### Dockerのインストール

Dockerがインストールされていない場合、インストールします。

```sh
> brew cask install docker
(略)

> docker --version
Docker version 18.09.0, build 4d60db4

> open /Applications/Docker.app
```

Dockerを初回起動すると初期設定のためにパスワード入力が求められます。


### UnityにLinuxビルドサポートコンポーネントを追加する

- Unity Hubアプリを起動する
- [Installs] > [On my machine]からUnityリスト右側にある[...]から[Add Component]を選択する
- [Add components your install]ダイアログの[Platforms]にある[Linux Build Support]にチェックを入れて[Done]ボタンをクリックする

<img width="1003" alt="Unity Hub" src="https://qiita-image-store.s3.amazonaws.com/0/48549/da5fa78f-8c55-e2a7-65da-5a8b40142a1a.png">

<img width="1000" alt="Add components to your install" src="https://qiita-image-store.s3.amazonaws.com/0/48549/9a75bb19-c45f-5239-3cc2-7941bd65c719.png">


### 学習用のappをビルドする

#### ML-Agentsリポジトリをダウンロード

適当なディレクトリにリポジトリをダウンロードする。

```sh
> mkdir 適当なディレクトリ
> cd 適当なディレクトリ
> git clone https://github.com/Unity-Technologies/ml-agents.git
```

- Unityを起動して、[open]からダウンロードしてきたリポジトリにある`ml-agents/ml-agents/UnitySDK`フォルダを開く

Unityエディタのバージョンによって、再インポートするか、確認ダイアログが立ち上がります。
<img width="422" alt="UnitySDK.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/30fd2547-6066-86e6-a52a-f7fca2191654.png">

「Continue(続行)」ボタンをクリックして進めます。

再インポート処理すると初回、プロジェクトが開くまでに時間がかかります。

<img width="1679" alt="スクリーンショット 2018-09-13 10.50.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/3b9869f9-6c07-86e9-94aa-4f85bc1d9a10.png">

起動しました。

今回は、サンプルとして含まれている[3DBall]Scenesを利用します。

- Unityの下パネルにある[Project]タブから以下のフォルダまで開く
    - [Assets] > [ML-Agents] > [Examples] > [3DBall] > [Scenes]
- 開いたら、[3DBall]ファイルがあるので、ダブルクリックして開く
<img width="557" alt="スクリーンショット 2018-07-28 13.32.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/c1bdb0d3-81cb-d050-e394-e7d6749715d3.png">


#### Scenes(シーン)の設定

- Unityアプリの[Edit]メニューから[Project Settings] > [Player]を開く
<img width="560" alt="学習のための設定" src="https://qiita-image-store.s3.amazonaws.com/0/48549/0485c977-bd34-6b60-3cc4-d9caa04377ab.png">

- Unityアプリの右側にある[Inspector]ビューで以下の設定を確認する
    - [Resolution and Presentation]の[Run In Background]がチェックされている
    - [Display Resolution Dialog]がDisableになっている
<img width="477" alt="ResolutionAndPresentation.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/3969581a-eb97-348c-485b-3f7974bb2ad0.png">

- Unityアプリの[Hierarchy]ビューから[Ball3DAcademy]を開く
    - Unityアプリの[Inspector]ビューで[Broadcast hub] > [3DBallLearning(LearningBrain)]横にある**[Control]にチェックを入れる**

<img width="393" alt="Unity_2018_2_7f1_Personal__64bit__-_3DBall_unity_-_UnitySDK_-_PC__Mac___Linux_Standalone__Personal___OpenGL_4_1_-3.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/0f6521cd-d517-da47-8387-7345f78c8e12.png">


- [Ctrl] + [s]キーでシーンを保存する

設定変更後、しっかりと保存しないとビルド時に設定が反映されなくてハマります。


#### Prefabs(プレハブ)の設定

- Unityアプリの下にある[Project]タブから以下のフォルダまで開く
    - [Assets] > [ML-Agents] > [Examples] > [3DBall] > [Prefabs]
- Prefabsフォルダ内の[Game]の[▶]ボタンをクリックして展開し、[Pratform]を選択する
- Unityアプリの右側にある[Insoector]パネルのBall 3D Agent(Script)にあるBrainを[3DBallLearning(LearningBrain)]に以下の手順で変更する
    - [Project]タブから以下のフォルダまで開き、
        - [Assets] > [ML-Agents] > [Examples] > [3DBall] > [Brains]
    - [3DBallLearning]を[Insoector]パネルのBrainにドラッグ&ドロップする
<img width="985" alt="Unity_2018_2_7f1_Personal__64bit__-_3DBall_unity_-_UnitySDK_-_PC__Mac___Linux_Standalone__Personal___OpenGL_4_1_.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/48e8d1a9-4baf-3015-3606-d2b6a43ee451.png">
<img width="984" alt="Unity_2018_2_7f1_Personal__64bit__-_3DBall_unity_-_UnitySDK_-_PC__Mac___Linux_Standalone__Personal___OpenGL_4_1_-2.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/f3ba2069-76c5-1bd5-86c8-91a29272d3bb.png">
- [Ctrl] + [s]キーでシーンを保存する
    - 設定変更後、しっかりと保存しないと、ビルド時に設定が反映されなくてハマります。



#### ビルド設定

- Unityアプリの[File]メニューから[Build Settings]を選択する
- [Build Settings]ダイアログで[Platform]で`PC, Mac & Linux Standalone`が選択されていることを確認する
- [Target Platform]を`Linux`に変更する
- [Architecture]を`x86_64`に変更する
- [Headless Mode]にチェックを入れる
- [Build Settings]ダイアログで[Build]ボタンをクリックする
- ファイル保存ダイアログで以下を指定してビルドを開始する
    - ファイル名: 3DBall
    - フォルダ名: 任意のディレクトリ/ml-agents\unity-volume
<img width="821" alt="スクリーンショット 2018-12-25 11.51.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/1eb2555d-8690-fed3-8ccf-51640aa02987.png">

すると、unity-volumeに以下フォルダ・ファイルが出力されます。

```sh
> ls 任意のディレクトリ/ml-agents\unity-volume
3DBall.x86_64 3DBall_Data
```

### ハイパーパラメーターの用意

ハイパーパラメーターファイルをunity-volumeフォルダにコピーしておきます。

```sh
> cp config/trainer_config.yaml unity-volume
```

### Dockerコンテナを構築する

Dockerが起動していることを確認してから、ml-agentsリポジトリ直下でdockerコンテナを構築する。
すでにDockerfileが用意されているので、buildするだけ。楽々ですね。

```sh
> cd 任意のディレクトリ/ml-agents
> docker build -t 3dball_ml_docker .
(略)
Successfully built db0b82f60411
Successfully tagged 3dball_ml_docker:latest
```

### Dockerコンテナの実行

Dockerコンテナが構築できたら実行してみます。

```sh:bash
# unity-ml-docker-3dball: コンテナ名(任意)
# 3DBall: Unityでbuild時に付けたアプリの名前(拡張子なし)
# 3dball_ml_docker: Dockerでbuild時に付けた名前
# docker_first_run: 機械学習結果を保存する際の名称(任意)

> docker run --name unity-ml-docker-3dball \
  --mount type=bind,source="$(pwd)"/unity-volume,target=/unity-volume \
  -p 5005:5005 \
  3dball_ml_docker:latest \
  trainer_config.yaml \
  --docker-target-name=unity-volume \
  --env=3DBall \
  --train \
  --run-id=docker_first_run
```

fishシェルで実行する場合は、`"$(pwd)"`を`"$PWD"`に置き換えます。

```sh:fishの場合
> docker run --name unity-ml-docker-3dball \
  --mount type=bind,source="$PWD"/unity-volume,target=/unity-volume \
  -p 5005:5005 \
  3dball_ml_docker:latest \
  trainer_config.yaml \
  --docker-target-name=unity-volume \
  --env=3DBall \
  --train \
  --run-id=docker_first_run
```

Unity ML-Agents公式にある```docker run``` コマンドがおそらく動作検証してなくて、パラメータ指定エラーとなります。以下は`mlagents-learn コマンドのヘルプです。
Dockerで実行する場合には、Dockerイメージ名```3dball_ml_docker:latest``` の後からが、```mlagents-learn``` のパラメータ指定になります。

```sh:mlagents-learn　help

    Usage:
      mlagents-learn <trainer-config-path> [options]
      mlagents-learn --help

    Options:
      --env=<file>               Name of the Unity executable [default: None].
      --curriculum=<directory>   Curriculum json directory for environment [default: None].
      --keep-checkpoints=<n>     How many model checkpoints to keep [default: 5].
      --lesson=<n>               Start learning from this lesson [default: 0].
      --load                     Whether to load the model or randomly initialize [default: False].
      --run-id=<path>            The directory name for model and summary statistics [default: ppo].
      --num-runs=<n>             Number of concurrent training sessions [default: 1].
      --save-freq=<n>            Frequency at which to save model [default: 50000].
      --seed=<n>                 Random seed used for training [default: -1].
      --slow                     Whether to run the game at training speed [default: False].
      --train                    Whether to train model, or only run inference [default: False].
      --worker-id=<n>            Number to add to communication port (5005) [default: 0].
      --docker-target-name=<dt>  Docker volume to store training-specific files [default: None].
      --no-graphics              Whether to run the environment in no-graphics mode [default: False].
```

実行すると、学習が始まります。
trainer_config.yamlの```max_steps``` で指定されているステップ数が完了するか、ctrl+cキーで学習が終了します。

```sh
> docker run (略)

INFO:mlagents.trainers:{'--curriculum': 'None',
 '--docker-target-name': 'unity-volume',
 '--env': '3DBall',
 '--help': False,
 '--keep-checkpoints': '5',
 '--lesson': '0',
 '--load': False,
 '--no-graphics': False,
 '--num-runs': '1',
 '--run-id': 'docker_first_run',
 '--save-freq': '50000',
 '--seed': '-1',
 '--slow': False,
 '--train': True,
 '--worker-id': '0',
 '<trainer-config-path>': 'trainer_config.yaml'}


                        ▄▄▄▓▓▓▓
                   ╓▓▓▓▓▓▓█▓▓▓▓▓
              ,▄▄▄m▀▀▀'  ,▓▓▓▀▓▓▄                           ▓▓▓  ▓▓▌
            ▄▓▓▓▀'      ▄▓▓▀  ▓▓▓      ▄▄     ▄▄ ,▄▄ ▄▄▄▄   ,▄▄ ▄▓▓▌▄ ▄▄▄    ,▄▄
          ▄▓▓▓▀        ▄▓▓▀   ▐▓▓▌     ▓▓▌   ▐▓▓ ▐▓▓▓▀▀▀▓▓▌ ▓▓▓ ▀▓▓▌▀ ^▓▓▌  ╒▓▓▌
        ▄▓▓▓▓▓▄▄▄▄▄▄▄▄▓▓▓      ▓▀      ▓▓▌   ▐▓▓ ▐▓▓    ▓▓▓ ▓▓▓  ▓▓▌   ▐▓▓▄ ▓▓▌
        ▀▓▓▓▓▀▀▀▀▀▀▀▀▀▀▓▓▄     ▓▓      ▓▓▌   ▐▓▓ ▐▓▓    ▓▓▓ ▓▓▓  ▓▓▌    ▐▓▓▐▓▓
          ^█▓▓▓        ▀▓▓▄   ▐▓▓▌     ▓▓▓▓▄▓▓▓▓ ▐▓▓    ▓▓▓ ▓▓▓  ▓▓▓▄    ▓▓▓▓`
            '▀▓▓▓▄      ^▓▓▓  ▓▓▓       └▀▀▀▀ ▀▀ ^▀▀    `▀▀ `▀▀   '▀▀    ▐▓▓▌
               ▀▀▀▀▓▄▄▄   ▓▓▓▓▓▓,                                      ▓▓▓▓▀
                   `▀█▓▓▓▓▓▓▓▓▓▌
                        ¬`▀▀▀█▓


E1225 03:40:19.381223700      14 server_chttp2.cc:40]        {"created":"@1545709219.381184700","description":"Only 1 addresses added out of total 2 resolved","file":"src/core/ext/transport/chttp2/server/chttp2_server.cc","file_line":316,"referenced_errors":[{"created":"@1545709219.381167100","description":"OS Error","errno":97,"file":"src/core/lib/iomgr/socket_utils_common_posix.cc","file_line":259,"os_error":"Address family not supported by protocol","syscall":"socket","target_address":"[::1]:5005"}]}
INFO:mlagents.envs:
'Ball3DAcademy' started successfully!
Unity Academy name: Ball3DAcademy
        Number of Brains: 1
        Number of Training Brains : 1
        Reset Parameters :

Unity brain name: 3DBallLearning
        Number of Visual Observations (per agent): 0
        Vector Observation space size (per agent): 8
        Number of stacked Vector Observation: 1
        Vector Action space type: continuous
        Vector Action space size (per agent): [2]
        Vector Action descriptions: ,
2018-12-25 03:40:21.048460: I tensorflow/core/platform/cpu_feature_guard.cc:140] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
INFO:mlagents.envs:Hyperparameters for the PPO Trainer of brain 3DBallLearning:
        batch_size:     64
        beta:   0.001
        buffer_size:    12000
        epsilon:        0.2
        gamma:  0.995
        hidden_units:   128
        lambd:  0.99
        learning_rate:  0.0003
        max_steps:      5.0e4
        normalize:      True
        num_epoch:      3
        num_layers:     2
        time_horizon:   1000
        sequence_length:        64
        summary_freq:   1000
        use_recurrent:  False
        summary_path:   /unity-volume/summaries/docker_first_run-0_3DBallLearning
        memory_size:    256
        use_curiosity:  True
        curiosity_strength:     0.01
        curiosity_enc_size:     128
        model_path:     /unity-volume/models/docker_first_run-0/3DBallLearning
INFO:mlagents.trainers: docker_first_run-0: 3DBallLearning: Step: 1000. Mean Reward: 1.266. Std of Reward: 0.732. Training.
(略)
```

### 学習結果をアプリに組み込む

学習結果は、```ml-agents/unity-volume``` フォルダ内に保存されます。


### TensorFlowSharpプラグインのインポート

学習結果を組み込むのに必要となるプラグインのインポートと設定を行います。
TensorFlowSharpプラグインをダウンロードしてインポートします。

https://s3.amazonaws.com/unity-ml-agents/0.5/TFSharpPlugin.unitypackage

[プラグインをダウンロード](https://s3.amazonaws.com/unity-ml-agents/0.5/TFSharpPlugin.unitypackage)してファイルを開くと、UnityでImport確認ダイアログが表示されます。[Import]ボタンをクリックしてインポートします。こちらはv0.5.0から変わりないようです。
<img width="473" alt="スクリーンショット 2018-12-20 14.30.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/87d057f3-2b42-2044-b7aa-84dcddc92837.png">



プラグインがインポートできたら、再度Playerの設定を行います。

- Unityアプリの[Edit]メニューから[Project Settings] > [Player]を選択する
- [Inspector]ビューの[Other Settings]欄で以下を設定する
    - [Scripting Runtime Version]を[.Net 4.x Equivalent]にする
    - [Scripting Defined Symbols]に`ENABLE_TENSORFLOW` を入力する
    - **[Allow 'unsafe' Code]にチェックを入れる**

<img width="377" alt="スクリーンショット 2018-12-20 14.36.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/50f40328-2f44-8ef7-5a05-7a730e289cd3.png">

[Allow 'unsafe' Code]にチェックを入れないとエラーがでて実行できないのでご注意ください。

設定変更したらUnityアプリの[File] > [Save Project]で保存して、Unityアプリを再起動します。


#### 学習結果ファイルの取り込み

ターミナルかFinderで学習結果を以下フォルダにコピーします。

- 学習結果ファイル: `ml-agents/unity-volume/models/docker_first_run-0/3DBallLearnig.bytes`
- 保存先: `UnitySDK/Assets/ML-Agents/Examples/3DBall/TFModels/`

※すでに保存先に`3DBallLearnig.bytes` ファイルが存在していますので、リネームしてください。

```sh
> cp ml-agents/unity-volume/models/docker_first_run-0/3DBallLearnig.bytes ../UnitySDK/Assets/ML-Agents/Examples/3DBall/TFModels
```

- Unityアプリの[Project]パネルで以下ファイルを選択する
    - [Assets] > [ML-Agents] > [Examples] > [3DBall] > [Brains] > [3DBallLearning]
- Unityアプリの[Project]パネルで以下フォルダを選択する
    - [Assets] > [ML-Agents] > [Examples] > [3DBall] > [TFModels]
- Unityアプリの[Inspector]パネルにある[Model]という項目に[TFModels]フォルダ内の`3DBallLearning.bytes`ファイルをドラッグ＆ドロップする
<img width="1488" alt="スクリーンショット_2018-12-21_10_21_10.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/cbc5bdb7-75c3-f071-9233-083f8b46c4b8.png">
- Unityアプリの[Hierarchy]パネルから以下を選択する
    - [3DBall] > [Ball3DAcademy]
- Unityアプリの[Inspector]パネルにある[Brains] > [3DBallLearning(LearningBrain)]横の**[Control]のチェックを外す**
- Unity上部にある[▶]ボタンをクリックする

これで、学習結果が組み込まれた状態でアプリが起動します。

<img width="1677" alt="スクリーンショット 2018-09-13 12.05.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/5ac93f64-7289-bfc2-fe7c-41f8a8da796d.png">


## 参考

Using Docker For ML-Agents
https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Using-Docker.md

DockerでUnity ML-Agentsを動作させる(v0.5.0対応) - Qiita
https://qiita.com/kai_kou/items/df3f3518a5a27b66f617

Docker for Macをインストールしてみた
https://qiita.com/scrummasudar/items/750aa52f4e0e747eed68

Macでhomebrewを使ってUnityをインストールする(Unity Hub、日本語化対応)
https://qiita.com/kai_kou/items/445e614fb71f2204e033

MacでUnity ML-Agentsの環境を構築する(v0.6.0対応) - Qiita https://qiita.com/kai_kou/items/2a6545d1f9d83178d0c0