[Project Bonsai] Bonsaiのシミュレータを触ってみる

# はじめに
Project Bonsaiについて調べてみましたの3回目です。
過去の内容はこちら。
1回目は[「Bonsaiを触ってみる」](https://qiita.com/masaya3/items/5cca56a773386bb80262)
2回目は[「Bonsaiをいじってみる」](https://qiita.com/masaya3/items/5cca56a773386bb80262)

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

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

# Bonsaiの[シミュレータ](https://docs.microsoft.com/ja-jp/bonsai/resources/sim-platform-support)
Cartpoleのチュートリアルは、すでにシミュレータも用意されていますが、基本的には自分で用意する必要があります。

サポートされているソフトウェア

* MATLAB Simulink
* AnyLogic
* VP Link
* Docker でコンテナー化されたカスタムのコンテナー

APIによるPythonやRESTのサポートもあるようですが、一からの作成だとなかなかお手軽にできる感じではありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/1caeb52a-4e5a-aaef-fe98-dca16fd0da9c.png)

実は、Cartpoleのシミュレータのソースコードは、公開されています。

https://github.com/microsoft/microsoft-bonsai-api

今回は、このソースコードを、ローカル上で動かすアンマネージシミュレータを試してみたいと思います。

## ローカル環境でシミュレータを動かす

今回チェックをした環境及び、使用したソフトは以下の通りです。

* Windows10 Pro 21H1
* Visual Studio Community 2019 version 16.9.6

### BonsaiのIDとキーの取得する
BonsaiのWorkspcaeIDと、[AccessKey](https://docs.microsoft.com/en-us/bonsai/cookbook/get-access-key)を取得します。
BosnsaiのWebページの右上にあるアカウント情報をクリックし、メニューから、「Workspace info」を選択します。
![キー取得](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/806cbf6a-f782-6d6b-fffe-cd6a83ffeb39.png)

Workspace Infoが表示されたら、WorkspaceIDをメモします。
AccessKeyは、右下にある「+ New access key」をクリックすると画面上に表示されます。
セキュリティ上、AccessKeyは、一度だけしか表示されません。もしキーを紛失した場合は、再度発行処理を行ってください。
![キー取得２](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/4d8201ec-7cd3-7633-c8e9-654fd06a8d6f.png)

### シミュレータのソースコードを取得する
[BosnsaiAPIのGitHubページ](https://github.com/microsoft/microsoft-bonsai-api)からソースコードを取得します。
CSharpやJava、Pythonなどのサンプルがありますが、今回はCSharpを使用します。
![GitHub-microsoft-microsoft-bonsai-api-A-collection-of-libraries-for-interfacing-simulators-with-the-Bonsai-platform-.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/b06d96b5-814c-9f7d-c683-55ca6ac8b52f.png)

### BonsaiのIDとキーを設定する

取得してきたソースコードの中にある「[Microsoft.Bonsai.Api.Samples.Cartpole.sln](https://github.com/microsoft/microsoft-bonsai-api/tree/main/CSharp/samples/Microsoft.Bonsai.Api.Samples.Cartpole)」ファイルをVisual Studioで開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/cfffe35e-1e20-bea8-dcdf-36be68b56234.png)

メニューより、プロジェクトのプロパティを開き、環境変数の項目に以下を追加します。

|名前|値|
|---|---|
|SIM_WORKSPACE| Bonsaiで取得したWorkspcaeID|
|SIM_ACCESS_KEY| Bonsaiで取得したAccess key|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/e958d100-2235-2cf1-c463-60f8049c886a.png)

ここまで出来たら準備万端です。
「F5」を押すか、メニューから「デバッグの開始」を選択します。
実行すると「コマンドプロンプト」が表示され、Bonsaiからの指示待ち状態になります。
![コマンドプロンプト](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/f2149acb-dec4-b328-e9d0-fa41156202b7.png)
※赤線の部分は、Bonsaiのシミュレータ画面の名称の一部として表示されます。

### Cartpole学習の準備をする
#### ローカルシミュレータの連携確認
プロジェクトを実行すると、BonsaiのSimulatorsの一覧に「Cartpole-CSharp-Unmanaged」 が追加されます。
赤線の部分は、「コマンドプロンプト」の起動時に表示されたものと同じ内容が表示されます。
もし、表示されていない場合は、WorkspcaeIDや、AccessKeyが間違っていないか確認してください。
![シミュレータ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/c8d5cc72-7dd9-6835-16e6-d050f813e986.png)

#### 教育カリキュラムを作る
ローカルシミュレータを使用するCartpole用教育カリキュラム作成します。
「+ Create Brain」を選択し、チュートリアル用のCartpoleを追加します。
作り方は「[Bonsaiを触ってみる](https://qiita.com/masaya3/items/5cca56a773386bb80262)」を参照ください
今回は、「Cartpole Trial Unmanaged」という名前で作成しています。


#### シミュレータの変更
そのまま使うと、チュートリアルのシミュレータと連携するので、ローカルのシミュレータに接続できるように教育カリキュラムを修正します。

```shell:変更前
source simulator (Action: SimAction): SimState {
    package "Cartpole"
}
```
```shell:変更後
source simulator (Action: SimAction): SimState {
}
```
使用するシミュレータのpackageを削除します。
削除することで、学習開始時にシミュレータを選択することが可能になります。


### 学習を開始する
「Train」をクリックすると、シミュレータを選択するポップアップが表示されるので、「Unmanaged simulators Cartpole-CShape」をクリックします。
![シミュレータ選択](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/93f450d9-58c0-4909-5c05-5f60a8b1020c.png)

選択すると、学習が開始されます。
この後は、基本的にチュートリアルの学習時度の動作と同じです。
![学習](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/9aad6d0e-f5da-40ee-d11c-146afee78ff2.png)
チュートリアルと大きく違うのは、実行速度です。
ローカルで実行しているためシミュレータ数が1個になります。
実行速度も1秒間に5回程度となり、かなり遅いです。
※チュートリアルだと、35インスタンスで、1秒間に300回程度実行されていました。

同じ結果になるか、気長に待つとします。

### まさかのエラー
なんかエラーでました。。（通信エラー・・？）
![エラー](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/a758bee3-e319-849e-3cf2-027e01e3b4c0.png)

6時間近くシミュレータ動かしてたのに。。ひどすぎる。。
課金の額を見るのが怖いのに。。
![失敗](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/39b221d2-a476-5c2d-5a55-ef3e9161280c.png)

## シミュレータをデバッグする
コノヤロー！ってことで、終わってもよかったのですが、
原因が知りたいのでデバッグしてみます。
Bonsaiには、シミュレータを[デバッグ](https://docs.microsoft.com/ja-jp/bonsai/tutorials/debug-your-simulator?pivots=sim-type-managed)する仕組みが用意されているようなので、少し試してみます。

デバッグ画面は、Simulatorsから、Cartpole-CSharpを開き、Debuggerタブを選択することで表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/68e344ff-56e3-e4d1-6583-f88b8ec08b4d.png)

デバッグ画面で使用するのは、4つです。

* デバッグのタイムライン
* タイムラインの操作パネル
* シミュレータへの送信情報
* シミュレータからの受信情報

#### デバッグのタイムライン
4つのEventTypeを組み合わせて、学習動作を再現します。
Startと、Finish、Unregisterの登録は必須です。
Unregisterは、Finish後の設定が一般的な様です。
Stepは、一回の学習を意味します。
補足）Unregisterは、無くても動作しています。Unregisterの実行後にローカルシミュレータとの接続がきれるので無いほうがデバッグしやすいかも。。

「＋」ボタンを押すことで、タイムライン上に項目を追加し、EventTypeのラジオボタンを選択することで、種類を変更することができます。
今回は、「Start」「Step」「Finish」「Unregister」を登録します。
ちなみに削除したいときは、タイムラインの項目を選んで、Backspaceです。

#### タイムラインの操作パネル
タイムラインの実行や、ステップ実行、リセットなどの動きを操作することができます。

#### シミュレータへの送信情報
シミュレータへ送信するJSON形式の情報を設定することができます。
「Start」では、シミュレータのSimConfigにあたる設定、Stepでは、SimActionのあたる値の設定可能です。

今回は、Stepの項目を以下のように修正します。

```shell:変更前
{
    "type": "EpisodeStep",
    "episodeStep": {
        "action": {}
    }
}
```
```shell:変更後
{
    "type": "EpisodeStep",
    "episodeStep": {
        "action": {
            "command:"1 
        }
    }
}
```
「右にcommand:1で進めろ」という命令を設定しています。

#### シミュレータからの受信情報
Bonsai側からの操作に対するレスポンスの情報が表示されます。
Stepの場合は、シミュレータの実行結果になります。

###デバッグを実行
準備ができたので、デバッグを開始します。
まず、ローカルのシミュレータと接続するために、「Start session」をクリックします。

接続ができた場合は、ボタンが「End session」に変わり、Simulator StatusがIdleに変わります。
![デバッグ開始](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/415eae5d-e6fa-46ba-a6e3-b4ecd2deb79c.png)

次に、タイムライン上の「-R」を選択します。
選択すると、「Reset」や、「Step」、「Run」が選択可能になります。
「Step」は、タイムライン上の項目を一つずつ実行、「Run」は、連続で実行します。
![タイムライン](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/4eca6092-ac41-d0cc-9431-9e223d425475.png)

「Run」ですべて実行してみます。

### エラーが発生。。
エラーがでましたが、問題のエラーと違いますね。。
※前回は通信系のエラーだったけど、今回はParseに失敗してる系？

![エラーログ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/98f383d2-372f-9914-8050-67599baf62ce.png)

動作ログを見ると、`EpisodeStep`までログが出てるので、`Finish`で失敗していることがわかります。

```
System.AggregateException: One or more errors occurred. (Unable to deserialize the response.)
```
Exceptionの内容を確認すると、Deserializeに失敗している事が見えます。

Exceptionログをもとに、Visual Studioでブレイクポイントを張って詳しくデバッグしていきます。
ローカルなので、細かくチェックできるのは便利です。
![VSデバッグ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/ee4745b9-f07a-9845-4dbc-fd1294d7e915.png)

#### 原因発覚
原因がわかりました。
タイムライン上で追加されるFinishのJSONの入力値が問題でした。

[仕様](https://docs.microsoft.com/en-us/python/api/microsoft-bonsai-api/microsoft_bonsai_api.simulator.generated.models.episodefinishreason?view=bonsai-api-python-1.0)を確認してみると、`EpisodeFinish`なんて無いじゃないですか。。

```shell:変更前
{
    "type": "EpisodeFinish",
    "episodeFinish": {
        "reason": "EpisodeComplete"
    }
}
```

```shell:変更後
{
    "type": "EpisodeFinish",
    "episodeFinish": {
        "reason": "LessonChanged"
    }
}
```
パラメータに説明がないので、とりあえず`LessonChanged`に変えます。

### 再度実行！
修正後、再度デバッグを実行しました。
今回は無事成功、コマンドプロンプトには各操作のログが表示され、エラーは出てないです。

![デバッグ成功](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/68943035-3b94-a6de-e84b-e32c48ca9a94.png)

ってことは、シミュレータの問題ではなくて、やっぱり通信が原因だったんでしょうか・・（ログ残してたらよかった・・）

# まとめ
とりあえず、自分でシミュレータを用意して動かす＆予想外のデバッグまで試してました。
学習時のエラー（たぶんタイムアウト系・・？）と、デバッグでのエラーは違うと思いますが、デバッグのやり方を理解できたのでよしとします。

再度動作の確認をしたいのですが、ちょっと時間とコストが割に合わないです・・。

今回は、Visual Studioから直接実行しましたが[Dockerのコンテナ](https://github.com/microsoft/microsoft-bonsai-api/tree/main/CSharp/samples/Microsoft.Bonsai.Api.Samples.Cartpole)にすることで、スケーリング可能なマネージドのシミュレータとしてBonsaiに登録できるそうなので、それをしないと使い物にならないかなと思います。

最終は、今回のBonsaiのコストで締めたいと思います。
![コスト](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189829/4fa1f2df-f304-fa7b-91ca-1207b2490387.png)