ML-Agents Python Low Level API入門：Pythonによる環境操作その3

# はじめに
今回もPythonによる環境操作の理論的な側面について理解を深めていきます。

# DecisionStepsとTerminalSteps
```DecisionSteps```および```TerminalSteps```は```mlagent_envs.base_env```パッケージで定義されている、エージェントの収集した情報を報告してくれる通信用のクラスです。これらは非常に似た機能を持っているので、一度に解説します。まずは、どちらのクラスにも含まれる有用なフィールドについて解説します。

## obs
```obs```フィールドは観測を表すNumPy配列のリストです。1次元目(最初のインデックス)はエージェントのインデックスを示しており、2次元目以降でUnityで設定した情報がNumPy配列として保存されています。このNumPy配列の内容は、Unityで指定したものになります。```Behavior Parameters```コンポーネントの```Vector Observation```の```Vector Size```の数だけの要素が含まれた配列で、その格納順序は、```Agent```クラスを継承したコンポーネントの```CollectObservation```メソッドで指定した順番と一致します。

## reward
```reward```フィールドは単数形ですが、すべてのエージェントの直前のステップでの報酬を表すNumPy配列です。エージェントは直前のステップでも決定を受け取って行動し、```OnActionReceived```メソッドが呼ばれているはずです。そこで指定された報酬が帰ってきます。このように書くと単純に思えますが、実際は```Decision Requester```コンポーネントの効果により、Decisionを要求しないActionが複数回行われているので注意が必要です。(ここは調査した結果を書く。おそらく、何もしないフレームはRewardは消化されずに、Decisionを要求したときに一度に開放されるはず)

## agent_id
```agent_id```フィールドも単数形ですが、中に入っているのはNumPy配列です。int32型が保存されていて、エージェントIDが保存されています。この情報を使うことで、```reward```および```obs```が、どのエージェントから収集した情報なのかが分かります。
前回の記事で解説しましたが、```get_steps```メソッドは、そのステップの開始時点での決定待ち状態のエージェントをすべて収集してくるという機能を持つだけであり、**すべてのエージェントの情報を1ステップ分集めてくるものではない**のです。したがって、```obs```や```rewards```に入っている情報は0個のエージェントについての情報である可能性も十分にあるわけでし、並んでいる順番が常に一定であることも保証してくれません。```agent_id```はそのときのためのフィールドです。```agent_id```の数値は、そのインデックスに保存されているエージェントのIDを示します。例えば、以下のような場合を考えます。
```python
agent_id == [0, 1, 4]
reward == [-1, 1, 0]
obs == [[1, 1, 0], [0, 0, 0], [-1, 0, -1]]
```
このとき、エージェントIDが1のエージェントは報酬1を受け取っており、現在の状態は[0, 0, 0]であるということが識別できます。これは、最初から2番目に1がある、という```agent_id```からの情報から、```reward```および```obs```の同じ最初から2番目の情報を集めたエージェントのIDが分かる、という仕組みです。

## action_mask
```action_mask```フィールドは、```Agent```を継承するコンポーネントで```WriteDiscreteActionMask```をオーバーライドした場合に使うことができるもので、離散的アクションの一部の選択を禁止することができます。禁止することができる、といっても、mlagents-envsにその機能があるわけではなく、決定を作成するときにそれを使わない決定をするようにPython側が実装しないといけないというものです。あくまで環境側からのリクエストを示す値になります。ちなみに、mlagents-learnの場合にはこのフラグをいい感じに解釈してくれるという仕組みになっています。注意点として、Trueが使用できないフラグになっています。これは、```actionMask.SetActionEnabled```とは**逆**です。Unity側でfalseを設定すると、対応する```action_mask```はTrueになっています。

# BehaviorSpec
## Spec系クラスの存在価値
ここまでで```obs```や```reward```など、```Behavior Parameters```コンポーネントの設定によって形状が変化する引数を扱いました。そのような引数の形状は、事前に把握しておくだけでなく強化学習の環境に問い合わせて取得することができます。そうすることで、より汎用的なプログラムを書くことができます。例えば、得られる観測のベクトルを増やすという変更を行ったときにも、Python側がニューラルネットワークの入力層の入力の数を観測ベクトルの個数を取得してから設定するようにプログラムをかいておけば、Python側の変更は不要になります。
## BehaviorSpec
Unityで環境を作成するときにも解説しましたが、ML-Agentsにおける"Bahavior"とはひとつの強化学習モデルで表現されるエージェントというような意味を持っています。したがって、```BehaviorSpec```クラスには、Behaviorについての情報が保存されています。Unityにおける```Behavior Paramaters```コンポーネントに対応するような存在です。詳しくは次回の記事で解説しますが、```BaseEnv.behavior_specs```メソッドで取得することができます。主要なフィールドは以下の2つです。

- ```observation_specs```：ObservationSpecのリストを返す。リストに格納されている順番と、```obs```フィールドで取得できる順番は一致している
- ```action_spec```：ActionSpecを返す

## ActionSpec
特定のBehaviorがとることができる行動についての情報を取得することができます。```Behavior Parameters```コンポーネントのActionsの設定に対応しています。

- continuous_size：連続的な行動の個数
- discrete_size：離散的な行動の個数
- discrete_branches：それぞれの離散行動が何個の選択肢を持っているのかのタプル。例えば、```Branch 0 Size```が2で、```Branch 1 Size```が3の場合は、int型のタプル(2, 3)が得られる。```discrete_size```が大きくなれば、```discrete_branches```の長さも大きくなる
## ObservationSpec
あるひとつの観測の情報を取得することができます。前提としてセンサーなどの知識が必要なので、ここでは深く触れませんが、プロパティについて簡単に概要を述べておきます。

- ```shape```：入力の形状をint型のタプルで表す。例えば、6つの値を取得する場合は(6,)となる。
- ```dimension_property```：入力の種類を表す列挙型を形状に従ったタプルで表す。例えば(DimensionProperty.NONE,)ならば通常のベクトルであることを示す。


# おわりに
今回まで実際のコードに触れずに解説してきたので、実感がわきにくかったとおもいます。しかしながら、理論面を分かっておくと実際のコーディングでも色々な応用・予測が聞くようになるのでその2およびその3の内容についてはしっかりと把握しておきましょう。