はじめに
今回も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を示します。例えば、以下のような場合を考えます。
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の内容についてはしっかりと把握しておきましょう。