[Project Bonsai] Bonsaiをいじってみる

# はじめに
Project Bonsaiについて調べてみましたの2回目です。
一回目はこちら[「Bonsaiを触ってみる」](https://qiita.com/masaya3/items/5cca56a773386bb80262)
前回は、チュートリアルをそのまま実行しただけなので、今回は学習カリキュラム（Inklingコード）を、変えてみたいと思います。

# [Bonsai](https://azure.microsoft.com/ja-jp/services/project-bonsai/)って何？
Microsoftが提供している自律システム向け機械教示サービスです。（現在はPublic Preview）
産業制御システムに焦点を当てた汎用ディープ強化学習プラットフォームのようです

# 目標を変えてみる。
動かすのはできたので、次は目標の内容を変えてみたいと思います。
現状チュートリアルの目標は、

* Poleの角度を、12度以上傾けないようにする
* Cartの位置を、0.25m以上動かないようする

Poleを維持するだけでなく、Cartを**前方に進む**ようにしたいと思います。

### 目標の指定方法
Inklingコードの[goal](https://docs.microsoft.com/ja-jp/bonsai/inkling/keywords/goal)で設定することができます。
Goalがサポートしている目標は以下の通り
#### Goalの目標
| 目標 | 動作 | 成功条件 |
|---|---|---|
|[avoid](https://docs.microsoft.com/ja-jp/bonsai/inkling/keywords/goal?tabs=avoid)|シミュレート結果が特定の状態にならないように維持します。|指定の状態にならなければ **成功** |
|[drive](https://docs.microsoft.com/ja-jp/bonsai/inkling/keywords/goal?tabs=drive) |できるだけ早く指定された状態にし、その状態を維持します|反復中に指定された状態の範囲に戻る場合は **成功**|
|[maxmize](https://docs.microsoft.com/ja-jp/bonsai/inkling/keywords/goal?tabs=max)|特定の状態の平均値を、指定された範囲内で可能な限り大きくするようにします|トレーニング中の平均テスト値が範囲内の下限を上回っている場合は **成功**|![＊Learn.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/2f9ffc22-a299-37fb-6e33-38617d7818c2.png)
|[minimize](https://docs.microsoft.com/ja-jp/bonsai/inkling/keywords/goal?tabs=min)|特定の状態の平均値を、指定された範囲内で可能な限り小さくするようにします。|トレーニング中の平均テスト値が範囲内の上限を下回っている場合は **成功**|
|[reach](https://docs.microsoft.com/ja-jp/bonsai/inkling/keywords/goal?tabs=reach)|きるだけ早く指定された状態に移行させるが、維持する必要はない|テスト値が規定の範囲内に少なくとも１回収まると、**成功**|

#### ゴールの範囲

| 定義| 範囲 |
|---|---|
|Goal.Range(X, Y)|X と Y の間の値 (X と Y を含む)|
|Goal.RangeAbove(X)|X 以上 の値|
|Goal.RangeBelow(X)|X 以下 の値|
|Goal.Sphere(X, R)|半径 R、中心ポイント (X) の 1D 球 (線) 内の値|
|Goal.Sphere([X, Y], R)|半径 R、中心ポイント (X, Y) の 2D 球 (円) 内の値|
|Goal.Sphere([X, Y, Z], R)|半径 R、中心ポイント (X, Y, Z) の 3D 球内の値|
※テスト値の次元と、提供された範囲の次元は一致させる必要があります。
例えば、3次元上で動くものが定義されている場合は、テスト値も3次元空間内の位置に対応している必要があります。

#### 補足
目標を使ったGoalの設定が推奨されているようですが、報酬関数や、ターミナル関数という方法を使用して指定することも可能なようです。
[「報酬関数とターミナル関数」](報酬関数とターミナル関数)に記述方法が記載されていますが、一般的なプログラムの書き方に似ているので、機会があれば使ってみたいと思います。



### goalの修正

Cartを前方に進ますために、以下のようにGoal条件を修正

```shell:修正前
goal (State: SimState) {
    avoid `Fall Over`:
        Math.Abs(State.pole_angle)
        in Goal.RangeAbove(MaxPoleAngle)
     avoid `Out Of Range`:
        Math.Abs(State.cart_position)
        in Goal.RangeAbove(TrackLength / 2)
}
```
Cartの位置を維持するだけの`Out Of Range`を削除し、
cart_positionが前向きに2m以上で一番大きくなるような`move`を追加しています。

```shell:修正後
goal (State: SimState) {
    avoid `Fall Over`:
        Math.Abs(State.pole_angle)
        in Goal.RangeAbove(MaxPoleAngle)
    maximize `move`:
        State.cart_position
        in Goal.RangeAbove(2)
}
```

## 実行する

教育カリキュラムの修正が終わったので、
前回と同じように「Train」ボタンをクリックして、学習を始めます。
![学習１](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/bfde8f87-7426-46f6-3491-04f09b415ceb.png)

##### 余談
ちなみに余談ですが、教育カリキュラムの内容が変わると自動的にバージョン管理されていきます。
変更後も前回の学習を活用することが可能です。
※そもそも一度学習が完了すると、教育カリキュラムを変えるか、[学習回数を増やさない限り](https://docs.microsoft.com/ja-jp/bonsai/inkling/keywords/curriculum#noprogressiterationlimit)学習を実行できません。
![バージョン](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/53065144-6683-e3b3-2bd9-02233e9f9b38.png)

#### 何かおかしい。
13分（前回の半分程度）実行したけど、目標の達成率が伸びていないです。
![学習おかしい](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/4f467a8c-0a1e-dea0-8c57-860286a2c9b9.png)

グラフ見ると、Poleを倒さない目標（青線）はほぼ100%達成しているけど、2m移動させる目標（緑線）が全然達成で来ていない事がわかります。

#### 入力値を変更
教育カリキュラムを見直した結果、入力値の移動距離が少ないが原因ではないかと過程し、
もっとがっつり動くように移動範囲を修正してみました。

```shell:修正前
type SimAction {
    command: number<-1 .. 1>
}
```

```shell:修正後
type SimAction {
    command: number<-3 .. 3>
}
```
### 再度実行！
今回も14分くらい学習を実行しました。
前よりも3%ほど改善していますが、まだまだ目的が遠そうです。
![学習おかしい２](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/7faab8f0-c313-7c94-9dfa-41db55079f3e.png)

合わせて、データグラフも確認してみると、
`pole_angle`は、安定し、`cart_position`も順調に右移動しています。
動きは問題ないので、目標に達するまでに反復回数が終わるの問題と判断しました。
![データグラフも確認](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/92f05b80-bd7a-f079-f1ff-c1b1166621cc.png)



#### 反復回数を増やす
120回で0.55m進んでいるので、2m進めるためにとりあえず回数を4倍に変更してみます。

```shell:修正前
training {
    EpisodeIterationLimit: 120
}
```

```shell:修正後
training {
    EpisodeIterationLimit: 480
}
```

### 3度目の正直！
ついに目標100%の達成が見えてきました！

![3度目の正直](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/3a9ee237-d9ef-0bd1-86e8-fb8943ec657d.png)

失敗するパターンもまだありますが、目標である2mを進むを達成する状況もでてきてます。
このまま、学習が終了するまで待ってみます。
![データグラフも確認２](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/0ff5a360-3776-be62-764b-77afc5a8703a.png)

#### ついに終了！
前回のPoleを保持する学習が28分でしたが、今回は148分なので、4倍にした反復回数以上に増えてます。
変更前は約10万回の反復で目標達成率が100%に達していましたが、
今回は、30万回の反復が必要になるなど、難易度があがっていることがわかります。
![終了](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/3acbd5b4-707d-a416-6f41-ef6b8f962c6c.png)

目標は2m以上としていましたが、結果として45mも進むようになっています。
![データグラフ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/dfecded5-5918-6000-4088-fa20f88eeda9.png)

## 気になるから実行
ここで思ったのが`SimAction`の変更不要だったのでは？ということです。
移動量を戻して、反復回数を増やすパターンも試してみました。
![気になる](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/ca3dcaaa-10cd-8b84-289d-2240b1bf6b96.png)

無事に、学習が完了しました。移動量は関係なかったようです。。
ちなみに、移動範囲を小さくした場合の移動量は26mほどでした。

# まとめ
教育カリキュラムの修正で、目的の結果は得られたかなと思います。
何度か失敗をしたため、注意する点に気づけたのも大きいです。
※どんどんお金がかかるのはちょっと痛いですが・・

今回は、教育カリキュラムをいじったので、次はシミュレータを触ってみたと思います。