あそびをせんとや生まれけむ!
(つづき)こんにちわ、こんばんわ。かえるのクーの助手の「井戸中 聖」(いとなか セイ)でございます。
連休はML-Agentsで遊ぶ準備が整いました。
⚽サッカーがいちばんおもしろそうなので、これをターゲットにします。
計画:遊ぶ内容
1.プログラムの解析をして、どのようなパラメータ(情報)をもとにしているのか調べます。
2.強化学習の方法を検討します。できれば、
・狭い間のスルーパスを通したい。
・ドリブルで突破したい。
・超人的な神シュートを決めたい
・献身的な守備ができるようになりたい。
を目標として、評価報酬の与え方を検討します。(1日目)
3.ひたすらコーディングして実験。ものになりそうな教育方針のチームを4パターンくらい考えます。
a) ひたすら引いて守り、一瞬のスキをカウンターで突破。
b) 後方からビルドアップし、サイドを起点に突破してセンタリング・シュート。
c) ワントップ(2人しかいないけど)にパスを通して神シュート!
d) 高い対人守備能力とゾーンディフェンスを兼ね備え、鋭い裏への走り込みでチャンスメイク。
4.評価報酬の与え方が異なるそれらのチームを各6時間以上学習させます。(2~3日目)
5.トーナメント方式で、チームを競わせます。(3日目)
6.優勝チームにはKooAword、副賞に今話題の「蛙化現象」グッズが進呈されます。
※3日で終わらない可能性がとても高いので、延長戦は十分ありえます。
プログラム解析
Unity のML-Agentsにある、サッカーの学習サンプルをもとに強化学習方法をかえて強いチームをつくります。
ますは、このサンプルプログラムで使用している技術を解析します。
おおまかなプログラム構成は次の4本です。(主な管理情報と機能です)
Script/AgentSoccer.cs 選手エージェントのプログラムです。選手ごとに呼ばれます。
メソッド | 処理タイミング | 機能 |
---|---|---|
Initialize() | 起動時に1度だけ | 自分(選手)のチームとポジションを指定しまします。選手はストライカ、ゴール守備、ゼネラリストの3種類がいる。(種類はUnityAgentオブジェクト側で指定。) |
OnActionReceived() | アクション受信の時 |
キーパーなら+微小報酬(0.0002オーダ)、ストライカなら-微小罰報酬を与える。 経過時間、点がはいっていなければ、キーパーなら「よし」だけど、ストライカははよ決めれ的な監督の怒りを表現していると思われます。MoveAgent()を呼ぶ。 |
MoveAgent() | OnActionReceivedから呼ばれる | 前後、左右、回転の動作を計算します |
Heuristic() | 呼ばれない? | キー操作によって、前後、左右、回転のディレクション(方向)を変える |
OnCollisionEnter() | ボールか選手同士と接触 | ゴール守備なら球際に強い。ボールと接触なら+報酬(0.2)意外と高い。 |
OnEpisodeBegin() | 試合開始かゴール決定後の再開 | 指定パラメータのリセット |
<クラスタイプ> | ... | Agent |
Script/SoccerBallController.cs サッカーボールの制御をおこないます。
メソッド | 処理タイミング | 機能 |
---|---|---|
start() | 起動時に1度だけ | SoccerEnvControllerのインスタンスを取得する |
OnCollisionEnter() | ボールがなにか(選手、サイド、コーナー、ゴール)と接触 | ゴールに接触した場合 SoccerEnvController の GoalTouched を呼ぶ |
<クラスタイプ> | ... | MonoBehaviour |
Script/SoccerEnvController.cs 試合の開始/終了/ゴールの制御を行います。
メソッド | 処理タイミング | 機能 |
---|---|---|
Start() | 起動時に1回だけ | ゲーム全体の初期設定をする。チームグループを作成する。完了したらResetScene()を呼ぶ。 |
FixedUpdate() | 不明:1Step毎に呼ばれる感じ | 制限時間内にゴールが決まれなければ、対戦をリセットする |
ResetBall() | ゴールで呼ばれる | ゴールしたあと、ボールを最初のポジションにセットする |
GoalTouched() | ゴールで呼ばれる | SoccerBallControllerから呼ばれ、ゴールチームに報酬(1-経過時間/制限時間)、されたチームに罰報酬(-1)を与える |
ResetScene() | ゴールなどで呼ばれる | 各選手の位置を初期リセットし、ResetBasss()を呼ぶ |
<クラスタイプ> | ... | MonoBehaviour |
Script/SoccerSettings.cs チームやゴール、トレーニング方法などの設定をします。
メソッド | 処理タイミング | 機能 |
---|---|---|
<クラスタイプ> | ... |
MonoBehaviour メソッドは持っておらず、データを管理するだけです。 |
シンプルですが、とても凝った作りになっています。開発者の「さあ、これを改造して遊べよ!」の声が聞こえるようです。
学習している(と思われる)パラメータ群
何をもとに学習しているか正確に知らなければなりません。
他のサンプルではCollectObservationsという関数で明示的にセンシングする情報を定義しているのですが、このAgentにはそれがありません。
Unity側にはStriker(Player=Agent)に「Ray Perception Sensor」(光学知覚センサー)が設定されています。おお!自動運転できるやつですね。設定では前方60度に11の視線を飛ばし、「ボール、自ゴール、相手ゴール、壁、自チームエージェント、相手チームエージェント」のセンシングをしています。これはC#側には(センサの)接続定義がないのですが、学習の入力対象となっていそうです。
となると、限られた視野で行動するためにはいくつか前の情報がとても大事になります。Stacked Raycastsが以前の視野情報をいくつ保持できるか(だと思うの)ですが、調整のしがいがありそうです。
動作確認
プログラムをみただけではどのように実行されるのかがわからない部分があるので、動かしながら確認します。そのためにデバッガの設定を行います。
VisualStudioをみたところUnity関連の拡張機能ははいっていないみたいなので、よさげなものをいれてみます。入れる前はこんな感じでした。
拡張機能の管理でUnityと入力してよさげなものを探します。
Code Assist とDebugExtensionが名前的によさげです。いれてみます。
インストールがスケジューリングされ、すぐには開始しないようです。
VisualStudioの再起動時にインストールされました。Unityの再起動も要求されました。
インストール済となりました。
Unityに関するコードアシストとデバッグの機能が追加されています。(メニューにでてきます)
デバッグを開始する前にUnity側のデバッグモードを変更しないといけません。
変更すると一旦黄色になります。
この状態になったあと、デバッガのインスタンス選択すれば、つながって
青色の表示になります。
ソース側でブレークポイントを指定して、Unity側の開始[▶]を押すと所定のブレークポイントで止まります。
これで、どのように学習しているかを学習させながらみることが可能です。(ただしデバッグモードは遅い!です)
考察/方針
オウンゴールがとても多いです。これをできるだけ行わないように教えたいです。
「ボールキープの判断」と敵味方・ボールポジションから「チャンスの形」に対して報酬を与えたいと思います。
やりながら勢いで書いているので、誰もみてくれない気がしますが、1年後の自分はきっと面白がって見てくれます。
各エージェントのセンサーは自分視点なので、これとは別に神視点で評価をして報酬を与える方法を考えなくてはなりません。
では報酬を判断できる情報(神視点)をどのように取得するか考えてみます。
当面、報酬を与える先は(チームではなく)各エージェントであるとします。
自エージェントの光学知覚センサーにアクセスできるか
Unityの定義ではAgent内に「RayPerceptionSennsor3D」を定義しています。そのためAgentのインスタンス内に「RayPerceptionSennsor3D」perceptionオブジェクトを含有ている「はず」で、何等か取得の方法がある「はず」と仮定してみます。
はたして、あるでしょうか?
こんなときのデバッガの威力は絶大です。Agentオブジェクト(this.base)の直下にsensorが2つあり、1つが「StackingSensor_size3_PurpleRayPerceptionSensor」(=「スタックサイズ3の紫用の光学知覚センサー」です。)もう一つが「StackingSensor_size3_PurpleRayPerceptionSensorReverse」なのですが、Reverseが何を意味するのか未解明です。レーダのような応答のことなの「かも」しれませんが、ここでは深追いしないことにします。
あれ?デバッガでは継承元情報を確認できるのにソース上では取得できません。。。お作法が違うのかな。う、カプセル化の罠かも。見えるのに手が届かない。。。
さて、こまったときには何でも知っている大先生にきいてみよう。ちょっと感動したので全部貼ります。
UnityのML-AgentsにおいてRayPerceptionSensorへのアクセスを取得するためには、以下の手順を実行する必要があります。
-
エージェント(Agent)のスクリプト内で、
Initialize()
メソッドをオーバーライドします。このメソッドはエージェントの初期化時に呼び出されます。 -
Initialize()
メソッド内で、RayPerceptionSensorComponent
を使用してセンサーコンポーネントへの参照を取得します。RayPerceptionSensorComponent
は、センサーのタイプやパラメーターを設定するためのコンポーネントです。 -
センサーコンポーネントから、必要なセンサーの参照を取得します。通常、
RayPerceptionSensorComponent
はセンサーを1つ以上保持しています。センサーの数や名前によって、取得方法が異なる場合があります。
以下に、具体的なコードの例を示します。
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using UnityEngine;
public class MyAgent : Agent
{
private RayPerceptionSensorComponent rayPerceptionSensor;
public override void Initialize()
{
// センサーコンポーネントへの参照を取得する
rayPerceptionSensor = GetComponent<RayPerceptionSensorComponent>();
// センサーの参照を取得する(例:名前が"RayPerceptionSensor"のセンサー)
var rayPerception = rayPerceptionSensor.GetSensor("RayPerceptionSensor") as RayPerceptionSensor;
// センサーへのアクセス
if (rayPerception != null)
{
// センサーの設定やデータへのアクセス
// ...
}
}
}
上記の例では、MyAgent
という名前のエージェントクラスを定義し、Initialize()
メソッド内でGetComponent<RayPerceptionSensorComponent>()
を使用してセンサーコンポーネントへの参照を取得しています。その後、GetSensor()
メソッドを使用して特定のセンサー(例:名前が"RayPerceptionSensor"のセンサー)への参照を取得しています。これにより、センサーの設定やセンサーデータへのアクセスが可能になります。
なお、このコードはML-Agents 2.x以降のバージョンに対応しています。バージョンによっては異なる記述方法が必要な場合がありますので、ご注意ください。
貼ったので、ソースコード部が崩れてますが、一読する限り完璧な答えです。質問に3Dが抜けていたので、その部分だけ違ってました(質問間違い)でしたが、正しい答えで、動作できました!Googleで1時間検索してもわからなかったので、これはすごいです。
ところが、Component
からsensorを取り出す方法がどうしてもわかず。。。
デバッガでみるとオブジェクト内に問題なく値(センサーオブジェクト)があるのだが、コーディングでそれを取得しようとするとコンパイルエラーになってしまいます。。。
とっても停滞中。該当部分はバージョンによっていろいろかわっている部分らしいので、情報がありません。てっとり早そうなので、まずセンサー情報をみようとしましたが、進展がないので、置いておきます。
少佐、すみません、目は盗めませんでした。
各エージェントの位置や向き、ボール情報を取得する
さて、気をとりなおしてゲーム全般の状況判断をどのようにするか考えましょう。
エージェントの情報やボールの情報はすべてSoccerEnvControllerに集約されているので、このクラスに状況判断をお願いするのが適切に思われます。
仮実装で、特定シチュエーションのとき報酬をもらえるようにしてみます。
・相手ゴールと自分の間には他プレーヤーがおらず、自分より前(相手ゴール側)にボールがあるとき。(オフェンス微小報酬)
・自ゴールとボールの間に他プレーヤーがおらず、かつ自分より前(相手ゴール側)にボールがあるとき。(ディフェンス微小報酬)
では続きはまた明日。ごきげんよう。