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

Unity ML-Agents(v0.9.1)をDocker上で動作させてみました。

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

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

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


## 手順

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

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


### Dockerのインストール

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

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

> docker --version
Docker version 19.03.1, build 74b1e89
```

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


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

Unity Hubを利用してUnityにLinuxビルドサポートコンポーネントを追加します。
Unityのバージョンは`2019.1.13f1` を利用しています。


- Unity Hubアプリを起動する
- [Installs] > [On my machine]からUnityリスト右側にある[...]をクリックして[Add Component]を選択する
<img width="1001" alt="Unity" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/212d8313-dcf3-370e-31d9-e06ae26b3475.png">
- [Add components your install]ダイアログの[Platforms]にある[Linux Build Support]にチェックを入れて[Done]ボタンをクリックする
<img width="998" alt="Unity" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/84d09438-9e12-ddc7-990a-9ceb56b6f5c8.png">


### 学習用のappをダウンロードしてビルドする

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

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

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

### Unityアプリからサンプルプロジェクトを開く

Unity Hubでアプリを立ち上げます。Unity Hubがインストールされていない場合は下記をご参考ください。

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

ML-Agentsを利用するにはUnityのバージョン`2017.4` 以上が必要となります。今回は`2019.1.13f1` を利用しました。

アプリが立ち上がったら「開く」ボタンから`任意のディレクトリ/ml-agents/UnitySDK` フォルダを選択します。

<img width="1001" alt="Unity_Hub.png" src="https://qiita-image-store.s3.amazonaws.com/0/48549/2bcdf91e-649e-6a71-3061-f7cfb8e89374.png">


Unityエディタのバージョンによっては、アップグレードするかの確認ダイアログが立ち上がります。
<img width="999" alt="スクリーンショット 2019-08-09 16.37.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/7f419ae8-4090-2f57-abc5-2ffa72eae602.png">

「Upgrade」ボタンをクリックして進めます。

アップグレード処理に少し時間がかかります。

<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="Scenes選択" src="https://qiita-image-store.s3.amazonaws.com/0/48549/d77344b9-ba61-94ca-a515-88fd5ff6e199.png">


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

ML-Agentsで学習させるための設定です。

- Unityアプリの[Edit]メニューから[Project Settings]を開く
<img width="373" alt="スクリーンショット 2019-08-19 14.23.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/c326d6cd-9ead-6e8f-3dee-b07212079a4f.png">

- [Inspector]パネルで以下の設定を確認する
    - [Resolution and Presentation]の[Run In Background]がチェックされている
    - [Display Resolution Dialog]がDisableになっている
<img width="631" alt="スクリーンショット_2019_08_19_14_25のコピー.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/36514763-700d-47d5-d0a1-05a24329989c.png">


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

- Unityアプリの下にある[Project]タブから以下のフォルダまで開く
    - [Assets] > [ML-Agents] > [Examples] > [3DBall] > [Prefabs]
- Prefabsフォルダ内の[Game]をダブルクリックする
- Unityアプリの左側にある[Hierarchy]パネルから[Game] > [Pratform]を選択する
  <img width="911" alt="Unity" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/d3998e14-862f-741a-8780-c7526129dc4b.png">
- Unityアプリの右側にある[Inspector]パネルのBall 3D Agent(Script)にあるBrainが[3DBallLearning(LearningBrain)]であることを確認する
- [3DBallLearning(LearningBrain)]ではない場合、
    - [Project]タブから以下のフォルダまで開き、
        - [Assets] > [ML-Agents] > [Examples] > [3DBall] > [Brains]
    - [3DBallLearning]を[Inspector]パネルのBrainにドラッグ&ドロップして指定する
<img width="1347" alt="Unity" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/380b3277-b685-1335-86a9-d8f83854ff55.png">
- [Ctrl] + [s]キーでシーンを保存する
    - ※設定変更後、しっかりと保存しないと、ビルド時に設定が反映されなくてハマります。


#### Brainsの設定

- Unityアプリの下パネルにある[Project]タブから以下のフォルダまで開く
    - [Assets] > [ML-Agents] > [Examples] > [3DBall] > [Scenes]
- Unityアプリの左側にある[Hierarchy]パネルから[Ball3DAcademy]を選択する
- Unityアプリの右側にある[Inspector]パネルの[Broadcast Hub] > [Brains]に「3DBallLearning (LearningBrain)」が指定されていることを確認する
- 「3DBallLearning (LearningBrain)」横にある[Control]にチェックを入れる
<img width="1348" alt="Unity" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/3804c2c7-c7ba-1903-d19b-05230fe2a370.png">

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

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



#### ビルド設定

- Unityアプリの[File]メニューから[Build Settings]を選択する
- [Build Settings]ダイアログで[Add Opne Scenes]をクリックする
- [Scenes In Build]で[ML-Agents/Examples/3DBall/Scenes/3DBall]にチェックを入れる
- [Platform]で`PC, Mac & Linux Standalone` が選択されていることを確認する
- [Target Platform]を`Linux` に変更する
- [Architecture]を`x86_64` に変更する
- [Server Build]にチェックを入れる
  - 以前は[Headless Mode]でした。
- [Build Settings]ダイアログで[Build]ボタンをクリックする
- ファイル保存ダイアログで以下を指定してビルドを開始する
    - ファイル名: 3DBall
    - フォルダ名: 任意のディレクトリ/ml-agents/unity-volume

<img width="815" alt="Build_Settings" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/b462d5f6-745b-4013-cfe3-cd5eb161384f.png">
<img width="617" alt="Build_Settings" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/d54d6002-0946-b61c-4275-7f286bf01051.png">


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

```console
> ls 任意のディレクトリ/ml-agents/unity-volume

3DBall.x86_64 3DBall_Data
```

### ハイパーパラメーターファイルの用意

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

```console
> cd 任意のディレクトリ/ml-agents
> cp config/trainer_config.yaml unity-volume
```

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

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

```console
> cd 任意のディレクトリ/ml-agents
> docker build -t 3dball-ml-docker .

(略)
Step 20/20 : ENTRYPOINT ["mlagents-learn"]
 ---> Running in 56532e0d2127
Removing intermediate container 56532e0d2127
 ---> 073d8b1040e9
Successfully built 073d8b1040e9
Successfully tagged 3dball-ml-docker:latest
```

### Dockerコンテナの実行

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

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

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

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

```console:fishの場合
> docker run -it \
  --name unity-ml-docker-3dball \
  --mount type=bind,source="$PWD"/unity-volume,target=/unity-volume \
  -p 5005:5005 \
  -p 6006:6006 \
  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` コマンドのサンプルがおそらく動作検証していなくて、パラメータ指定エラーとなります。(2019/08/20時点)

以下は`mlagents-learn` コマンドのヘルプです。
Dockerで実行する場合には、Dockerイメージの指定(`3dball-ml-docker:latest` )の後からが、`mlagents-learn` コマンドのパラメータ指定となるため、`--docker-target-name` は`trainer_config.yaml (<trainer-config-path>)` の後ろに指定します。

```console: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].
      --sampler=<file>            Reset parameter yaml file 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].
      --base-port=<n>             Base port for environment communication [default: 5005].
      --num-envs=<n>              Number of parallel environments to use for training [default: 1]
      --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].
      --debug                     Whether to run ML-Agents in debug mode with detailed logging [default: False].
```

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

```console
> docker run (略)

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


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


INFO:mlagents.envs:
'Ball3DAcademy' started successfully!
Unity Academy name: Ball3DAcademy
        Number of Brains: 1
        Number of Training Brains : 1
        Reset Parameters :
                scale -> 1.0
                mass -> 1.0
                gravity -> 9.8100004196167
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: ,
2019-08-19 09:26:41.207263: 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 PPOTrainer of brain 3DBallLearning:
        trainer:        ppo
        batch_size:     64
        beta:   0.001
        buffer_size:    12000
        epsilon:        0.2
        hidden_units:   128
        lambd:  0.99
        learning_rate:  0.0003
        max_steps:      5.0e4
        memory_size:    256
        normalize:      True
        num_epoch:      3
        num_layers:     2
        time_horizon:   1000
        sequence_length:        64
        summary_freq:   1000
        use_recurrent:  False
        vis_encode_type:        simple
        reward_signals:
          extrinsic:
            strength:   1.0
            gamma:      0.99
        summary_path:   /unity-volume/summaries/docker-first-run-0_3DBallLearning
        model_path:     /unity-volume/models/docker-first-run-0/3DBallLearning
        keep_checkpoints:       5
INFO:mlagents.trainers: docker-first-run-0: 3DBallLearning: Step: 1000. Time Elapsed: 33.147 s Mean Reward: 1.107. Std of Reward: 0.588. Training.
INFO:mlagents.trainers: docker-first-run-0: 3DBallLearning: Step: 2000. Time Elapsed: 61.621 s Mean Reward: 1.227. Std of Reward: 0.680. Training.
INFO:mlagents.trainers: docker-first-run-0: 3DBallLearning: Step: 3000. Time Elapsed: 94.060 s Mean Reward: 1.511. Std of Reward: 0.973. Training.
INFO:mlagents.trainers: docker-first-run-0: 3DBallLearning: Step: 4000. Time Elapsed: 143.072 s Mean Reward: 1.959. Std of Reward: 1.307. Training.
INFO:mlagents.trainers: docker-first-run-0: 3DBallLearning: Step: 5000. Time Elapsed: 201.150 s Mean Reward: 2.899. Std of Reward: 2.278. Training.
(略)
INFO:mlagents.trainers: docker-first-run-0: 3DBallLearning: Step: 50000. Time Elapsed: 2084.527 s Mean Reward: 100.000. Std of Reward: 0.000. Training.
INFO:mlagents.envs:Saved Model
INFO:mlagents.trainers:List of nodes to export for brain :3DBallLearning
INFO:mlagents.trainers: is_continuous_control
INFO:mlagents.trainers: version_number
INFO:mlagents.trainers: memory_size
INFO:mlagents.trainers: action_output_shape
INFO:mlagents.trainers: action
INFO:mlagents.trainers: action_probs
INFO:tensorflow:Restoring parameters from /unity-volume/models/docker-first-run-0/3DBallLearning/model-50001.cptk
INFO:tensorflow:Froze 14 variables.
INFO:mlagents.trainers:Exported /unity-volume/models/docker-first-run-0/3DBallLearning.nn file
Converted 14 variables to const ops.
Converting /unity-volume/models/docker-first-run-0/3DBallLearning/frozen_graph_def.pb to /unity-volume/models/docker-first-run-0/3DBallLearning.nn
IGNORED: Cast unknown layer
IGNORED: StopGradient unknown layer
GLOBALS: 'is_continuous_control', 'version_number', 'memory_size', 'action_output_shape'
IN: 'vector_observation': [-1, 1, 1, 8] => 'sub_3'
IN: 'epsilon': [-1, 1, 1, 2] => 'mul_1'
OUT: 'action', 'action_probs'
DONE: wrote /unity-volume/models/docker-first-run-0/3DBallLearning.nn file.
```

`TensorBoard` を利用して学習の進捗状況を視覚的に確認することもできます。

```console
> docker exec \
  -it unity-ml-docker-3dball \
  tensorboard \
  --logdir=/unity-volume/summaries \
  --host=0.0.0.0

2019-08-19 09:43:12.596652: I tensorflow/core/platform/cpu_feature_guard.cc:140] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
TensorBoard 1.7.0 at http://0.0.0.0:6006 (Press CTRL+C to quit)
```

<img width="1193" alt="スクリーンショット 2019-08-19 19.07.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/0e7bb5b0-6f2e-fe3a-fa6b-3f64c0b4b927.png">



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

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

```console
> ls unity-volume/

3DBall.x86_64       3DBall_Data         models              summaries           trainer_config.yaml
```

#### Unityアプリの設定

Playerの設定を行います。

- Unityアプリの[Edit]メニューから[Project Settings]を選択する
- [Inspector]ビューの[Other Settings]欄で以下を確認・設定する
    - [Scripting Runtime Version]を[.Net 4.x Equivalent]にする
<img width="632" alt="Unity" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/f5f4d17d-4d94-a8ad-7c98-8e8a8a78b62d.png">


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

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

- 学習結果ファイル: `unity-volume/models/docker-first-run-0/3DBallLearnig.nn`
- 保存先: `UnitySDK/Assets/ML-Agents/Examples/3DBall/TFModels/`

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

```console
> cp unity-volume/models/docker-first-run-0/3DBallLearnig.nn 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.nn`ファイルをドラッグ＆ドロップする
<img width="1351" alt="Unity" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/38137bb9-549d-e2bc-43a6-6f7c9709d713.png">
- Unityアプリの[Hierarchy]パネルから以下を選択する
    - [3DBall] > [Ball3DAcademy]
- Unityアプリの[Inspector]パネルにある[Broadcast Hub] > [Brains] > [3DBallLearning(LearningBrain)]横の[Control]のチェックを外す
<img width="1350" alt="Unity" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/515b7c71-0b56-98d8-466c-33be00bbeca1.png">
- Unity上部にある[▶]ボタンをクリックする

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

<img width="1677" alt="" 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

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

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

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