Unity ML-Agentsで研究したから(ほぼ)自分用

#はじめに

現在ML-Agentsには新たにメジャーリリースが出ています。この記事はメジャーリリース前の物を使用しているため、現在の使用とは異なる点がございます。参考程度にとどめておいてください。

Qiita記事とか書くの初めてだしそんなにめっちゃ詳しくかけるわけでもないしガバがあってしかるべきなのでなんか見つけたら優しく教えてください、シスタークレアがもちちゃんに話しかけるが如く。
あと基本的に[ML-Agents公式の記事](https://github.com/Unity-Technologies/ml-agents)を見るのがいいです英語は気合いで理解してください、[論文](https://arxiv.org/abs/1809.02627)もあるから読むといいかもね。

基本的に初学者&実際にやってから時間経ってるのでなかなかガバガバなのでは？？？ってなったら自分で調べてくださいごめんなさい。(ついでにどういう風に書き直したらいいのか教えてくれると嬉しい)

後、知り合いでこれ読んで「ここで詰まった」とか「ここ意味わからん」とかなったら連絡ください。頑張って必死こいて直します。

#環境について
+ Windows 10 Education (Win苦手)
+ Processer: Intel(R) Core(TM) i5-6500CPU @ 3.20Ghz (4 CPU), ~3.2GHz
+ GPU: NVIDIA GeForce RTX 2070 SUPER (Thanks to my supervisor)
+ Unity 2017.4.33f1 (そんなに新しくない)
+ ML-Agents 0.10.1 (だっけ?)
+ Anaconda (便利)
+ CUDA 9.0, V9.0.176 (メモしといてよかった)
+ cuDNN 7.0.5
+ TensorFlow 1.7.1

#使った主なAssets
+ Unity Technologies: [Standard Assets](https://assetstore.unity.com/packages/essentials/asset-packs/standard-assets-for-unity-2017-3-32351?locale=ja-JP) (車動かすのに使った)
+ NIANDREI: [Lake Race Track](https://assetstore.unity.com/packages/3d/environments/roadways/lake-race-track-55908) (Udacityの自動運転コースのサーキットに似てたから使った)

#環境設定
##GPU編(ガバ)
1. まずGPUを買って組み込みます(教授がやってくれたありがとう、あとGPU高いよね)
2. CUDAのインストール
3. cuDNNのインストール

##やったこといろいろ(ガバ)

+ Anacondaのインストール
+ Pythonのインストール
+ TensorFlowのインストール
+ Unityのインストール
+ ML-Agentsリポジトリのダウンロード
+ Pythonの仮想環境の作成

##それぞれ詳しくいってみよう
###CUDAのインストール
まずはCUDAインストーラのダウンロード、今回はCUDA version 9.0をインストールします。
[公式のCUDA toolkit downloadサイト](https://developer.nvidia.com/cuda-downloads?target_os=Windows&target_arch=x86_64&target_version=10)

使うOSとかversion、インストーラタイプを選んでダウンロードしてください。今回は最新バージョンではないので「Legacy Releases」から今回使うバージョンのCUDAインストーラをダウンロードします。
「Base Installer」をダウンロードして実行。他のパッチがある場合はそれらもダウンロードする。

###cuDNNのインストール
cuDNNも[cuDNNの公式](https://developer.nvidia.com/cudnn)からダウンロード
いろいろユーザ登録等ありますがそれは頑張ってやってください。
cuDNNはライブラリ形式で配布されているのでダウンロードしたファイルをPATHが通っている場所にコピーする。

###Unityの開発環境のインストール
「Unity ML-Agents」を利用するのは、「Unity 2017.4」以降が必要。
まずは[Unity](https://unity.com/ja)のサイトからダウンロードしてください。

###Unity ML-Agentsのダウンロード
「Unity ML-Agents」のリポジトリは、

+ 強化学習の環境を作成するためのUnity Assets
+ 学習用のPythonスクリプト

が含まれている。
[公式のリポジトリ](https://github.com/Unity-Technologies/ml-agents)から「Clone or Download」でダウンロード、その後任意の場所に展開する。「ml-agents-master」が生成される。
フォルダの構成は以下の通り。

+ UnitySDK:強化学習の環境を作成するためのあっせとが含まれるUnityプロジェクト
+ UnitySDK/Assets:強化学習の環境を作成するためのアセット
+ ml-agents:強化学習の学習を行うPythonパッケージ
+ ml-agents-envs:Unity環境とml-agentsパッケージ間のインタフェースとなるPythonパッケージ
+ config:学習のパラメータを定義したファイル。
+ gym-unity:OpenAI Gymと連携するためのパッケージ(今回は使わなかった)
+ docs:ドキュメント(なんかわからんことあったらとりあえず読め)

###Pythonの仮想環境の作成(大事)
Anacondaで「Pyhton 3.6.1」以降の仮想環境を作成し、以下のコマンドで「mlagentsパッケージ」をインストールする。

```
$ pip install mlagents
```
これにより以下の依存関係もインストールされます.

+ TensorFlow:深層学習ライブラリ
+ Jupyter Notebook:プログラムの実行結果を記録しながらデータ分析を行うためのツール
+ Matplotlib:グラフ描画ライブラリ
+ Numpy:数値計算ライブラリ
+ Pillow(PIL):画像処理ライブラリ
+ docopt:コマンドラインオプションライブラリ

#必要なものはインストールできた！次は環境作成だ！
##とでも思ったか？まずはサンプルを動かすのだポッター
まずはML-Agentsが提供している「3DBall」(以外でもいい、好きなやつ)を動かしてみましょう。板の動きでバランスを取り、ボールを落下させないようにキープする学習環境です。
手順は以下の通りです。
(1) Projectウィンドウで「Assets/ML-Agents/Agents/Examples/3DBall/Scene」を選択し、シーンを開く。
(2) HierarchyウィンドウでAcademy 「Ball3DAcademy」を選択し、InspectorでBrainsにBrain「Assets/ML-Agents/Example/3DBall/Brains/3DBallLearning」をドラッグ&ドロップし、Controlにチェックを入れる。
Academyに学習・推論対象のBrainを登録し、Controlをチェックすると「学習モード」になる。(チェックを外した場合は「推論モード」)

3DBallに用意されているBrainは3種類です。

+ 3DBallLearning: Actionの決定は、ニューラルネットワーク(学習および推論)を使用
+ 3DBallHeuristic: Actionの決定は、プレイヤー(人間)の入力を使用
+ 3DBallPlayer: Actionの決定は、ルールベース(手書きのプログラム)を使用

(3) Projectウィンドウの「Assets/ML-Agents/Examples/3DBall/Prefab/3DBall」内の「Agent」を選択し、InspectorでBrainにBrain「Assets/ML-Agents/Examples/3DBall/Brains/3DBallLearning」をドラッグ&ドロップする。
Agentで絵利用するBrainを設定する。

##Pythonスクリプトによる学習
サンプル学習環境を学習用のPythonスクリプトから実行して学習を行い、学習が完了すると推論モデルが生成される。
(1)Pythonの仮想環境で、「ml-agents-master」のルートに移動し、「mlagents-learn」コマンドで学習開始、書式は以下の通り

```
$ mlagents-learn <学習コンフィグファイルのパス> --run-id=<実行ID> --train
```
他にも役に立つオプションがあるので[公式の記事](https://github.com/Unity-Technologies/ml-agents/blob/48e21adcfed79ead46a95c37f96034abedd3328f/docs/Training-ML-Agents.md)で調べてみるのがいいかもね。
「学習コンフィグファイル」は学習パラメータをまとめたyamlファイルで、「ml-agents-master/donfig/trainer_config.yaml」にある。「実行ID」は学習の実行結果を管理するためのIDで任意の文字列を指定する。

例として、以下のようにコマンドを入力する

```
$ mlagents-learn ./config/trainer_config.yaml --run-id=testRun --train
```

すると以下のようにログが出るはずです。

```


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

       
INFO:mlagents.trainers:CommandLineOptions(debug=False, num_runs=1, seed=-1, env_path=None, run_id='testRun', load_model=False, train_model=True, save_freq=50000, keep_checkpoints=5, base_port=5005, num_envs=1, curriculum_folder=None, lesson=0, slow=False, no_graphics=False, multi_gpu=False, trainer_config_path='./config/trainer_config.yaml', sampler_file_path=None, docker_target_name=None, env_args=None)
INFO:mlagents.envs:Start training by pressing the Play button in the Unity Editor.
```
「Start training by pressing the Play button in the Unity Editor」と表示されたら準備完了です。

(2)UnityのPlayボタンを押して学習開始。
最初はボールを落としまくりますが、徐々に上達していきます。

ログにはMean Reward(平均報酬)とStd of Reward(標準偏差)が出力される。

```
INFO:mlagents.trainers: testRun-0: 3DBallLearning: Step: 1000. Time Elapsed: 8.842 s Mean Reward: 1.188. Std of Reward: 0.722. Training.
INFO:mlagents.trainers: testRun-0: 3DBallLearning: Step: 2000. Time Elapsed: 18.164 s Mean Reward: 1.222. Std of Reward: 0.688. Training.
INFO:mlagents.trainers: testRun-0: 3DBallLearning: Step: 3000. Time Elapsed: 27.447 s Mean Reward: 1.514. Std of Reward: 0.997. Training.
```
50000ステップ(学習コンフィグファイルの初期設定)に達すると学習完了。「ml-agents-master/models/testRun-0/3DBallLearning.nn」に推論モデルが出力されています。

##Pythonスクリプトによる学習その２
先ほどの学習方法ではUnityの表示画面の処理も同時に行われるため、学習に時間がかかってしまいます。(ただし、学習にCameraを使用している場合はこの方法は使用できない)
そこで一度学習環境をビルドしてから学習を始めることでグラフィックを使用せず学習をすることができます。

(1)学習環境のビルド
学習環境が完全に作成できた状態でUnityの画面のFile/Build SettingsからBuildを選択し、環境をビルドします。この時、Brainのチェックなども同時に設定する必要があり、チェックされていない状態でビルドするとBrainを使用した学習ができなくなります。

(2)Pythonスクリプトを使用した学習
学習環境のビルドが完了したら次は学習です。
Pythonの仮想環境で、「ml-agents-master」のルートに移動し、「mlagents-learn」コマンドで学習開始、書式は以下の通り。

```
$ mlagents-learn <学習コンフィグファイルのパス> --env=<作成した学習環境のパス> --run-id=<実行ID> --train
```
基本的には前述のコマンドと同じですが、envオプションでビルドした学習環境へのパスを通し学習環境を指定しています。またオプションを使用することにより色々便利な機能を使用することができます。
例としては

+ `--load`:ニューラルネットワークを初期化する際に、学習済みモデルをロードする。学習済みモデルは`models/<id-run>/`のモデルを使用します。
+ `--no-graphic`:これを使用するとUnityの実行可能ファイルが実行され、グラフィックスドライバーは初期化されない。トレーニングに視覚的な入力(Cameraなどを使用し、Pixelsからの読み取り)を含まない場合のみ使用可能です。

これ以降は基本的に前述の学習と同様です。


##Unityでの推論モデルの実行
利用手順は以下の通り
(1)推論モデル「3DBallLearning.nn」をプロジェクトの「Assets」にコピー

(2)Projectウィンドウで「Assets/Ml-Agents/Examples/3DBall/Brains/3DBallLearning」を選択し、「Learning Brain」の「Model」に「3DBallLearing.nn」をドラッグ＆ドロップ。
Brainで利用する推論モデルを指定していく。

(3)Hierarchyウィンドウで「3DBallAcademy」を選択肢、BrainsのControlのチェックを外す。
Controlのチェックを外すと「推論モード」になります。

(4)UnityのPlayボタンを押して推論開始。
「推論モード」で実行され、ボールがバランスを取る様子をみることができます。

#サンプルがしっかり動いたら次は自身の環境作成
今回私はRaycastセンサーを使用したシンプルなサーキットでの自動運転の環境を作成しました。
サーキット→[NIANDREIさん](https://assetstore.unity.com/publishers/18018)の[Lake Race Track](https://assetstore.unity.com/packages/3d/environments/roadways/lake-race-track-55908)
車→[Unity Technologies](https://assetstore.unity.com/publishers/1)の[Standard Assets (for Unity 2017.3)](https://assetstore.unity.com/packages/essentials/asset-packs/standard-assets-for-unity-2017-3-32351)

**Asset Storeは偉大**

今回の環境に必要な基本的なものは、サーキットと車です。なので、Asset Storeからサーキットと実際に動かせる車を設置して終わりです。
自分のオリジナルの環境を作りたい場合は試行錯誤しまくって作るとか何とかかんとか頑張ってください。

##ML-Agentsを自分の環境に組み込む
ML-Agentsを使用するにはAcademy、Brain、Agentを追加する必要があります。
###事前設定
Unityの画面上のEdit->Project Setting->Player->Other SettingsのScript Runtime Versionを確認し、.NET4.*になっていることを確認する（１敗）。
###Academyの追加
(1)HierarchyウィンドウにからのGameObjectを作成し、名前を指定して新規スクリプトを作成。(hogeAcademyなど)

(2)Academyのソースコードを記述
Academyとなるクラスは「Academy」クラスを継承します。
主に以下の3つのメソッドを含みます。

|メソッド|説明|
|:----|:----|
|virtual void InitializeAcademy()|環境の初期化時に呼ばれる|
|virtual void AcademyReset()|環境のリセット時に呼ばれる|
|virtual void AcademyStep()|ステップ毎に呼ばれる|

```C#:hogeAcademy.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MLAgents
public class hogeAcademy : Academy{

}
```
Academyには色々設定項目がありますが、今回は触れずにおきます。気になったら自分で調べて。

##Brainの追加
Academyの次はBrainを追加します。
(1)ProjectウィンドウからCreateのML-AgnetsからHeuristic、Learning、Player Brainの中から必要なもをの作り、hogeHeuristicBrainのように名前を指定する。
(2)AcademyのBroadcast HubからAdd newを選択し、作成したBrainを追加する。
(3)Brainの設定項目
以下はBrainの設定項目です。

|プロパティ|値|説明|
|:----|:----|:----|
|Vector Observation|Space Type: Continuous<br>Space Size: 2<br>Stacked Vectors: 1|数値型の状態情報の設定<br>　・Space Type: データ型<br>　・Space Size: サイズ<br>　・Stacked Vectors: 過去のVector Observationのスタック数|
|Visual Observation|size: 0|任意のカメラの画像情報の設定<br>　・Size: サイズ<br>　・Element 0~X: カメラ設定<br>　　・Width: 幅<br>　　・Height: 高さ<br>　　・Black and White: グレースケール|
|Vector Action|Space Type: Discrete<br> Space Size: 4<br>Action Description<br>　Size: 4<br>　Element 0: Up<br>　Element 1: Down<br>　Element 2: Left<br>　Element 3: Right|数値型の行動の設定<br>　・Space Size: データ型<br>　・Space Size: サイズ<br>　・Action Description: 行動の説明<br>　　・Size: 行動の説明の数<br>　　・Element 0~X: 行動の説明|
|Brain Type|Player|Actionの決定方法<br>　・External: 外部のTensorFlowでActionを決定<br>　・Internal: 推論モデルでActionを決定<br>　・Player: 人間の操作(キー入力など)でActionを決定<br>　・Heuristic: 手書きのプログラムでActionを決定|

(4)Player Brainの設定項目の指定
Brain Type 「Player」は、人間の操作でActionを決定します。「Brain Type」の下に「Player」の設定項目が追加されるので、そこで設定します。

|プロパティ|値|説明|
|:----|:----|:----|
|Broadcast|チェックなし|PythonスクリプトでAgentの情報を収集するか|
|Default Action|-1|キー入力なし時に通知するAction値|
|Discrete Player Actions|Size: 4<br>Element 0:<br> Key: Up Arrow<br> Value: 0<br>Element 1:<br> Key: Down Arrow<br> Value: 1<br>Element 2:<br> Key: Left Arrow<br> Value: 2<br>Element 3:<br> Key: Right Arrow<br> Value: 3|キー入力時に通知するAction値<br>　・Size: キー設定の数<br>　・Element 0~X: キー設定<br>　　・Key: キー<br>　　・Value: Action値|

##Agentの追加
「Agent」は人間の代わりにキャラクターを行動(Action)させる役割を担います。行動をもとに報酬を受け取り、最終的な報酬が最大となるように試行錯誤を繰り返します。
(ここからの話は、実際に動かしたい対象がSceneの中にある前提です、自分の実験だと車ですね。)
(1)Hierarchyウィンドウの対象「hoge」の名前を「hogeAgent」に変更して、新規スクリプト「HogeAgent」を追加

(2)hogeAgentのソースコードを記述
Agentとなるクラスは「Agent」クラスを継承します。ここでソースコードを記述しなければならないのですが、細かく解説するとめちゃ長くなります。

基本的には以下の5つのAgentクラスのメソッドをオーバーライドすることで頑張っていきます。

|メソッド|説明|
|:---|:---|
|virtual void InitializeAgent()|Agentの初期化時に呼ばれる|
|virtual void AgentReset()|リセット時に呼ばれる|
|virtual void CollectObservations()|Stateの取得時に呼ばれる|
|virtual void AgentAction(float[] vectorAction, string textAction)|ステップ毎に呼ばれる|
|virtual void AgentOnDone()|エピソード完了時に呼ばれる|

また、自分でこれらをいじるときはoverrideしなければならないので悪しからず。
ここはもう各自の環境に合わせて頑張るしかないっす。**がんば。**

###Stateの取得
CollectObservations()ではAgentが観測しているStateをBrainに提供しています。CollectObservations()内でAddVectorObs()を使ってVector Observationの情報を追加します。

|メソッド|説明|
|:---|:---|
|void AddVectorObs(float observation)|float型のVector Observationの追加。Sizeは1増加|
|void AddVectorObs(int observation)|int型のVector Observationの追加。Sizeは1増加|
|void AddVectorObs(Vector3 observation)|Vector3型のVector Observationの追加。Sizeは3増加|
|void AddVectorObs(Vector2 observation)|Vector2型のVector Observationの追加。Sizeは2増加|
|void AddVectorObs(float[] observation)|float[]型のVector Observationの追加。Sizeは配列サイズ分増加|
|void AddVectorObs(List<float> observation)|List\<float>型のVector Observationの追加。Sizeはリストサイズ分増加。|
|void AddVectorObs(Quaternion observation)|Quaternion型のVector Observationの追加。Sizeは4増加。|

**これらの入力は正規化しておくことをお勧めします。**

###エピソードの完了
自身で設定した終了条件に達したら以下のコードでエピソードを完了させることができます。

```C#
if(/*設定した終了条件*/){
   Done();//エピソード完了
   return;
}
```
###Actionの取得
AgentAction()はステップ毎に呼ばれるメソッドです。引数「vectorAction」でBrainからActionが渡されます。今回のActionのデータ型はDiscreteなのでvectorAction[0]に整数が入っています。

```C#
int action = (int)vectorAction[0];
if (action < 0) return;
```

###報酬の指定
報酬の指定または加算を行うには、Agentクラスの「SetReward(float reward)」または「AddReward(float reward)」を呼びます。望ましいActionをとった時は正、そうでない時は負の値を指定します。

```C#
if(/*望ましい行動*/){
   AddReward(1.0f);
} else if (/*望ましくない行動*/){
   AddReward(-1.0f);
} else { //どちらでもない場合(ステップ毎)
   AddReward(-0.1f);
}
```

###Agentの設定項目を指定

|プロパティ|値|説明|
|:---|:---|:---|
|Brain|MazeBrain|Agentを操作するBrainの参照|
|Agent Cameras|なし|Agentが利用するカメラの参照|
|Max Step|0|エピソードの最大ステップ数。最大ステップ数に到達するとエピソード完了。一定時間後にエピソード完了しない場合は0を指定。|
|Reset On Done|チェックあり|・チェックあり：エージェント完了後、即リセット<br>・チェックなし：エージェント完了時、リセットされるっまで完了のまま待機|
|On Demand Decision|チェックなし|・チェックあり：Action決定のタイミングをスクリプトで指定<br>・チェックなし：Decision Frequencyの数値ステップ毎にAction決定|
|Decision Frequency|1|何ステップ毎にAction決定を行うか。On Demand Decisionが見チェック時のみ利用|

##学習環境の実行
Academy、Brain、Agentの作成ができたところで学習環境の実行をしていきます。
###まずはPlayer Brainで学習環境の実行
Player Brainは人間の操作でActionを決定するBrainです。Brain TypeをPlayerに設定して方向キーで操作してみましょう。
操作が確認できたら次は学習プロセスに移行します。

###External Brainによる学習環境の実行
今度はBrain TypeをExternalに設定して学習をさせましょう。([前述の学習のやり方を参照](#pythonスクリプトによる学習))

###TensorBoardで確認
学習完了後に、TensorBoardでCumulative Rewardなどのステータスを確認することができます。積極的に利用していきましょう。

```
$ tensorboard --logdir=summaries --port=6006
```

このコマンドを打った後にでるURLをコピー&ペーストしてブラウザ上で見ることができます。

#参考にさせていただいた記事など(リファレンスは大事と存じます)
+ [Unityではじめる強化学習 / Unity ML-Agents 0.10.0のチュートリアル](https://note.com/npaka/n/n47cb6e426e65)
+ [Windows環境へのCUDA・cuDNNのインストール](https://screwandsilver.com/cuda_cudnn_tensorflow_gpu/)
+ [Unityではじめる機械学習・強化学習 Unity ML-Agents実践ゲームプログラミング](https://www.borndigital.co.jp/book/6702.html)