Unityの強化学習エンジン(ML-Agents)でオリジナル課題に挑戦 (動画付き)

# この記事について
Unityが提供する強化学習エンジン(ML-Agents)の使い方がある程度わかってきたので情報共有します. 
　また今回の仕事は<a href="https://www.youtube.com/watch?v=gs5hqqclXzo">YouTube動画</a>にまとめたので興味があればどうぞ.

# 対象読者

ML-Agentsを使ったUnity上での強化学習プログラミングの**雰囲気**が知りたい方.


# プログラミング環境

OS: Window10
CPU: Corei5-8250U (4コア, 8スレッド)
Software: Unity, Anaconda Prompt

# モデルと課題設定

学習させる3Dモデル (人形とか) を作成し, 課題を設定します.
## 3Dモデルの構築
ProBuilderとProGridを使い, 直方体パーツを並べ体を作りました. 

![body.png](https://qiita-image-store.s3.amazonaws.com/0/363923/55a7bf1d-51f2-a4d8-940e-3eee9b389e67.png)

上の図で茶色い矢印がいくつも見えますが, これが関節であって, パーツ同士を接続しています. これは関節オブジェクト (<a href="https://docs.unity3d.com/ja/2018.2/Manual/class-ConfigurableJoint.html">configurable joint</a>) を各パーツ毎に定義することで可能です. 

![spine_property.png](https://qiita-image-store.s3.amazonaws.com/0/363923/26ad3618-a5fb-a7bf-afa0-b6d8495f3407.png)

脊椎(spine)のconfigurable jointのパラメータ設定を上の図に載せています. 
connected Body: 接続先のオブジェクトを指定します.
Anchor, axis: 関節の場所と, 向きを指定します. これが関節のローカル座標(x,y,z)を決定します.
x,y,z motion: 接続先のオブジェクトの相対的な動きを定義します. 今回は, 固定された関節なのでlockedです.
Angular x,y,x motions: 関節周りの回転を指定します. 一方向だけの回転だけにしたいので,xだけ許可して(limit), あとはlockedにしています.
High/Low X Angular Limit: 回転できる角度を制限します. 今回は$-60^\circ$から$60^\circ$としました. 
　最後に, 丸太とステージも作成し, 3Dモデルが以下のように完成しました.

![body_and_stage.png](https://qiita-image-store.s3.amazonaws.com/0/363923/0e305884-f23a-9731-8a2d-0353da0b0c54.png)

## 課題の設定

今回の課題は, 人形がステージ上に長く居続けることです. そのために, 報酬(reward)を以下のように設定しました:

```math
\begin{align}
reward[t] &= 0.01, \text{ for every time step}, \\
if(fall): \\
rew&ard[t] = reward[t]  - 1 \\
end&~episode.
\end{align}
```  
報酬は毎時間ステップ与えられます. また, ステージ上から落ちると罰が与えられ, タスク (1エピソード) が終了します. C#上で, エージェントのオブジェクトから AddReward(報酬値) を呼ぶことで報酬(罰)を与えることができます.


## エージェントの並列化

一般的な学習のフレームワークでは, 取得されるデータは無相関であることが仮定されています. しかし, 強化学習では、エージェント(学習する対象)が行動しながらデータを取得するため, 得られるデータに強い相関があります. このため学習を成功させるには, データの相関を軽減する何らかの処置が必要です.
　ML-Agentsでは, (ニューラルネットワークが共有された) 複数のエージェントを並列に学習させることで相関を軽減します. チュートリアルに倣い, エージェントを9個並べて同時に学習できるようにしました.

![parallel.png](https://qiita-image-store.s3.amazonaws.com/0/363923/ad8e2ac9-fedb-c8b9-03fb-114cfae30327.png)
 

# ML-Agentsの設定

ML-Agentsのインストール方法は<a href="https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Readme.md">公式のドキュメント</a>に沿えば問題ないと思います. ML-Agentsで学習を行うためにはUnityソフト側でいくつかのオブジェクトを作成する必要があります. チュートリアルのプログラムを自分のプログラムに改編してくのが一番の近道だと思います. やることは, HumanAcademyオブジェクトと, その子オブジェクトであるBrainオブジェクトの定義です.

また, Anaconda Prompt上で, 以下のコマンドによって学習を開始できます.

```terminal
mlagents-learn config/config.yaml --run-id=Run1 --train
```
config.yamlは学習時間やニューラルネットワークの構造などのハイパーパラメータを設定するパラメータです. 

## ニューラルネットワークの入力と出力の定義

ニューラルネットワークへの入力は、エージェントが観測する量で, 自分の関節の位置やステージの位置, またそれらの速度などを割り当てました. ニューラルネットワークへの出力は、関節の目標位置としました. これらは, それぞれ以下のC#の関数を呼ぶことで定義できます.

```c#
AddVectorObs(入力値);
vectorAction[出力インデックス];
```

## ハイパーパラメータ設定
ハイパーパラメータの最適化はニューラルネットワークを用いる場合, 一番大変なところだと思います. ベイズ最適化法とかいろんな手法が提案されていますが、まずは、似た課題の学習パラメータを用いるのが良いと思います. また, 学習率が最も重要なパラメータなので, これに限っては細かく調整した方がいいです. 今回は, チュートリアルでにある人形が走る課題<a href="https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Learning-Environment-Examples.md">Walker</a>からパラメータをコピーして, 学習時間と学習率だけ調整しました. 今回用いた学習パラメータは以下になります. 

| パラメータ | 説明 | 値(Walker) |値(今回)|
|:-----------|------------:|:------------:|-----:|
| batch_size | バッチサイズ | 2048 | 2048 |
| beta | エントロピー制約の強さ | 5e-3 | 5e-3 |
| buffer_size | ポリシー更新のステップ数 | 20480 | 20480 |
| epsilon | 方策更新の閾値 | 0.2 | 0.2 |
| gamma | Advantageの割引率 | 0.995 | 0.995 |
| hidden_units | 隠れ層のニューロン数 | 512 | 512 |
| lambd | 一般化Advantageの係数 | 0.95 | 0.95 |
| **learning rate** | 学習率 | 3e-4 | **5e-4** |
| **max_steps** | 総ステップ数 | 2e6 | **1e4** |
| normalize | 正規化するか | true | true |
| num_epoch | ?? | 3 | 3 |
| num_layers | 隠れ層の数 | 3 | 3 |
| time_horizon | ?? | 1000 | 1000 |
| sequence_length | ?? | 64 | 64 |
| summary_freq | サマリの出力回数 | 3000 | 3000 |

パラメータの詳しい説明は<a href="https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Training-PPO.md">ここ</a>にあります. ??のところはまだ理解できていません.
パラメータを変更したところはmax_stepsとlearning_rateだけです. max_stepsは学習時間に比例するので, 許容範囲ぎりぎり(~10時間)の大きさに設定しました. learning_rateはいくつか試し, 一番いいものを載せています. 

# 結果

学習結果は以下のコマンドで見ることができます.

```terminal
tensorboard --logdir=summaries
```
下の左図は学習率を示し、右の図はエピソードあたりの報酬値を示しています. どちらの場合も横軸は学習ステップ数です.
![rewards.png](https://qiita-image-store.s3.amazonaws.com/0/363923/b40894b0-c52f-7051-3071-879e43893685.png)

学習率はデフォルトの設定で線形に減少するようになっているようです. 報酬は, 学習が進むごとに大きくなっていることがわかります. また, 初期学習率の大きさによって, 最終的な報酬値が大きく異なっています. やはり学習率は重要らしいです.

# まとめ

ML-Agentsを使ってオリジナルの課題を作って学習させることができました. 動画を見ていただけるとわかると思いますが、人形がバランスをうまくとっていることが観測できました. しかし, ML-Agentsの使い方は大体わかったのですが, ML-Agentsが何をしているかとか, 強化学習自体の理解が全然追いついていないです. 実際, 学習パラメータをどのように調整していいか全然検討がついていませんし, 学習率のスケジューリングは線形でいいのだろうか, とかわからないことだらけです. 今後, コードを読むなり, 参考書や論文を読むなりして勉強していきます.


# 参考

・<a href="https://www.youtube.com/watch?v=gs5hqqclXzo">YouTubeにまとめた動画</a>

・牧野 貴樹, 他, "これからの強化学習", 森北出版 (2016)

・V. Mnih, et al., "Asynchronous Methods for Deep Reinforcement Learning", PMLR 48:1928-1937 (2016)
(エージェントを並列に学習させることで学習データの相関を軽減する手法を提案した論文)
・J. Schulman, et al., "Proximal Policy Optimization Algorithms", arXiv:1707.06347 (2017)
(ML-Agentsの強化学習アルゴリズム)