Morikatron Engineer Blog

モリカトロン開発者ブログ

【CEDEC2020】模倣学習でAIに3Dアクションゲームを攻略させてみる(前編)【ML-Agents】

こんにちは。エンジニアの竹内です。
まずはじめに、この記事はCEDEC2020というゲーム開発者向けのカンファレンスで発表した内容(CEDEC2020: 攻略、接待、変更に強いAIプレイヤー開発のためのアプローチ)の模倣学習部分について、発表で網羅しきれなかった検証過程での試行錯誤や、考察、技術的な部分の深堀りなどについて解説したものです。
CEDEC2020で行った講演の資料は以下のリンクからご覧いただけますが、この記事単体でも内容は追えるようにしています。
攻略、接待、変更に強いAIプレイヤー開発のためのアプローチ
前編では主に学習を行うためにUnityサイドで行った準備について触れていきたいと思います。

何をしたか?

人間のデモプレイを利用する模倣学習を取り入れた深層強化学習(以下単に模倣学習とする)を使って、Unityで制作された本格的な3Dアクションゲームを攻略するAIを作成しました。その際に、Unity ML-AgentsというUnity環境で強化学習を行うためのプラグインを利用しました。
特徴量にはプレイヤーの位置座標やレイキャストを用いて検出した周囲の環境情報を利用し、画像情報は使用しませんでした。
30分程度で作成した5エピソード分のデモプレイを利用し、GPUで3日間学習させた結果、3D Game Kitに収録されているサンプルステージを攻略することに成功しました。(下動画は学習後のエージェントがステージを攻略する様子です。)

▲3DGameKitStage1の模倣学習結果(CEDEC2020)



▲3DGameKitStage2の模倣学習結果(CEDEC2020)

誰のための記事?

この記事は

  • CEDEC2020の講演を見てくださった中でもう少し詳しく技術的な部分や細かい試行錯誤の部分を知りたい方
  • 強化学習、特に模倣学習を使って本格的なゲームを学習させることに興味がある方

を想定しています。強化学習とは、みたいな部分についての説明は無いのでご了承ください。(CEDEC2020のスライドの方ではもう少し基本的な内容までイラスト付きで説明しています。)

学習に使用した環境について

まず、学習に使用した環境=ゲームについて説明していきます。
今回学習に使用した環境はUnityの3D Game Kitというアセット内のサンプルステージです。3D Game KitはUnity Technologies公式でリリースされている無料のアセットであり、その名の通り3Dアクションゲームを作成するためのモデルや、プレハブ、スクリプトなどが一通り揃ったアセットとなっています。3D Game Kitの中にサンプルステージが2ステージ分用意されており、このステージを環境として強化学習を行いました。
下の動画で今回中心的に検証を行ったステージ1の概要について説明しています。サンプルとは言うもののギミックやボスなどがしっかり作り込まれておりクオリティはかなり高いです。

▲3DGameKitStage1Demo(CEDEC2020)

3D Game Kitを利用した理由としては

  • サンプルステージは強化学習を行う前提で作られた環境でない
  • 無料で手に入る
  • 検証用にステージの一部を改変したり、自作の環境を作ったりする事が簡単にできる

この3点が大きいです。特に1つ目の要素は重要で、この手の検証でしばしば見られる「強化学習させることを前提として作られた環境」で検証する場合だと「じゃあ実際遊ぶために作られたゲームをどれくらい学習できるの?」といった疑問に対して明確な答えが得られなかったり、実際のゲームへの利用時特有の問題が見えてこなかったりといった点が懸念されます。

Unity ML-Agents

今回の検証に使用した、Unity環境で強化学習を行うためのプラグインであるUnity ML-Agentsについて説明していきます。
Unity ML-Agentsには

  • 特徴量や報酬、行動といった強化学習に必要な一連の機能をゲームに実装するためのUnityパッケージ
  • レイキャストを用いたオブジェクト検出
  • 実際に学習を行う際のUnity - Python間の通信

などの機能が実装されています。

使い方をざっくりと説明すると

  1. UnityプロジェクトにML-Agentsのパッケージをインポート
  2. pipかcloneしたリポジトリからML-AgentsのPythonパッケージをインストール
  3. Unityエディタ上で操作キャラのゲームオブジェクトに観測や行動、終了判定、行動などを記述したスクリプトと各種コンポーネントを取り付ける
  4. CLIでML-Agentsの学習用コマンド(mlagents-learn)を起動してからエディタ上でゲームを再生

で学習が進むようになります。エディタ上で学習させる以外にも、ビルドした実行ファイルを使うことも可能です。*1

ドキュメントはそこそこ充実しており、サンプルなどもあるため強化学習が少しわかる方であれば割とすぐに使いこなせるようになっていると思います。
欠点としては

  • 月一回程度の頻度でメジャーアップデートが入るので仕様がコロコロ変わりやすく、その度に変更点などを追いかけるのが結構大変
  • ドキュメントが全部英語なので読むのに苦労する
  • レイキャストでのオブジェクトの検出にTagとLayerを使う→ゲーム内でのTag及びLayerの用途とコンフリクトする可能性がある

あたりかと思います。
詳しい使い方については弊ブログ内の記事MacでUnity ML-Agents(v1.0)の環境構築 - Morikatron Engineer Blogを公式のドキュメントと合わせてご参照ください。

模倣学習させるための準備

ここから本題の検証のために行った学習周りの準備について、主にUnity側の作業を中心に触れていきます。
Unity側で行った作業は主に

  • 報酬と特徴量の設定
  • エージェントの移動方法の変更
  • その他、最大ステップ数などの細かい部分の設定
  • デモの作成

となります。それぞれ順に説明していきます。

特徴量と報酬の設定

3D Game Kitのステージ1を学習させるにあたっては最終的に以下の特徴量と報酬を使用しました。

f:id:morika-takeuchi:20200908151024p:plain
設定した報酬と特徴量

以下、この様に設定した経緯などを説明していきます。

画像情報を利用しなかった理由

深層強化学習では人間と同じ条件かつEndToEndで学習させるためにCNNを使ってゲームの画面から特徴量を抽出するという手法がメジャーかと思われますが、今回はプレイヤーの位置やギミックの作動状況といったゲームの情報を特徴量としてそのままエージェントに与えています。画像情報を利用しなかったのは

  • 画像情報を利用する場合、連続的な視点操作を行動に含む必要が出てきてしまい、DQNで扱うには離散化する必要が出てくる。
  • 現在のフレームだけの画面情報ではマルコフ性が保たれなくなるため、直近の何フレームかをスタックするかRNNを利用する必要が出てくる。
  • 開発現場での応用を想定した場合、UIやオブジェクトのデザイン、配色が変わるだけで再学習する必要が出てくる。
  • CNNよりMLPの方が計算コストがかからない。
  • 視点操作はゲームを攻略する上では補助的なものであり、エージェントにプレイさせる場合はゲームの攻略へはほとんど寄与しない

といった理由からです。
特にオブジェクトのデザインや配色に対する頑健性に関しては特に開発中のゲームを想定する場合非常に重要な課題であり、特徴量のエンジニアリングのコストを鑑みても画像情報を使用しないアプローチのほうが有効だと考えています。視点が固定されたゲームや物体の位置関係がクリティカルに効いてくるゲーム(ボードゲームやパズル要素のあるゲーム)などに対しては画像情報の利用を考えても良いかと思います。

特徴量の選択

学習を成功させる上で最も重要な部分の1つです。学習に必要な特徴量を適切に与えられなければどんなに性能の良いアルゴリズムを使い、計算コストを掛けてもうまくいくことはありません。

中でも特に重要な特徴量がエージェントの位置座標です。今回の様にステージの構造が予め決まっており、画像情報のような局所的な情報を使用していない場合、正しいコースでステージを移動するために現在の位置座標は必要不可欠な情報となります。
位置座標の情報を与える場合、シンプルな手法としては各座標のxyzの値を0~1に正規化してfloatとして与える方法が考えられます。(ML-Agents公式のドキュメントでも推奨されているやり方です。)
当初はこの方法で位置座標を与えていましたが、橋を渡り武器を取得するところまで安定して到達することができませんでした。

▲3DGameKitの模倣学習結果(座標の正規化のみ)

なぜこの方法がうまくいかないかについてですが、今回のような広大なマップをある程度決まったルートで探索するゲームの場合、位置座標の値は質的な変数としての側面が強くなるという要因が大きいためと考えられます。確かにXYZ座標が同じような値、つまりある特定の座標の周辺にいるときはほぼ同じ行動を取るのが望ましいと考えられますが、ある程度XYZ座標の値が異なる場合、その量的な関係は最適な行動を考慮する上でほとんど意味のない情報となります。
位置座標を質的な変数として与えるため、XYZ座標の各値について0が最小となるようにシフトさせた上で適当な大きさ(今回は10を採用)で割ったあと、整数部分をone-hotエンコーディング、小数部分をそのまま与える、という手法を取りました。

f:id:morika-takeuchi:20200909143702p:plain
位置座標のone-hot化の具体例

これはマップ全体を1辺の長さが10のグリッドに分割し、今現在エージェントがどのグリッドにいるかという情報と、そのグリッド内のどの位置にいるかという情報を与えることに相当します。この位置座標のone-hot化は今回3Dアクションゲームを模倣学習させる上で最も効果のあった工夫の1つです。

また、与える特徴量は最小限に抑えることが重要です。
当初地形への引っ掛かりを解消するためレイキャストを全方位に飛ばして障害物となりうる全ての地形を検出させることも試みましたが、障害物を避けるどころかパフォーマンスの著しい低下を招く結果となりました。レイキャストで検出する地形の情報などはステージのクリアにあたってはほとんど不要な情報であり、観測→最適な行動のパターンを学習するためには激しいノイズとなります。

▲Stage1(レイキャストによって地形を検出した場合)

一方で区別しなければならない状態はしっかり区別できるように特徴量を与えることも重要です。
例えば前方に存在する敵の有無を検出するセンサーを取り付けない場合、デモと位置座標を頼りに敵を倒すことができたとしても、その後も報酬を得ようと攻撃を振り続けることになります。今回は前方の敵やボスのコアは下画像のようなレイキャストによって検出しています。レイの長さは武器の当たり判定より少し長い程度に調節しました。前方に飛ばすだけでは自身に密着した状態の敵が検出できず、ボスの攻略が難しくなることが判明したため、最終的にはにエージェントの頭上から真下に飛ばすセンサーも導入しました。

f:id:morika-takeuchi:20200909150040p:plain
前方の敵を検出するためのレイセンサー

特徴量を設定する際には、目的を達成(ステージをクリア)するためには最低限どの情報が必要なのか、どのような状態を区別するべきなのかを考えながら設定していくことが重要かと思います。

報酬の設定

報酬の与え方は特徴量ほどではないですがそこそこ重要です。
主に留意すべき点としては

  • 報酬の絶対値は予め0~1の間にしておく
  • 安易に負の報酬を設定しない

あたりでしょうか。
報酬の絶対値に関しては0~1に正規化することが推奨されますが、正直あまりに極端な値でなければ割と適当でも良いかなというのが実感です。
また、今回の環境ではゲームオーバーが存在せず、何度でもチェックポイントからやり直せるため落下時やライフが0になった際の罰則、つまり負の報酬は導入していません。
落下しないでクリアしてほしいため落下時や死亡時に負の報酬をつけたくなりますが(というか最初はつけていた)、死亡時に負の報酬を入れるとそれを避けるように方策を学習するため攻略が困難になることがあります。
今回のステージの例でいうと、武器を取得する前の橋からジャンプして向こう岸まで行く部分が特に落下しやすい部分ですが、落下のたびに負の報酬を与えるとそれ以降のイテレーションでそもそも橋まで行きにくくなり、クリアまで遠ざかるという現象が見られました。
敵からのダメージに関しても、そもそもライフが0になった場合、チェックポイントに戻った時点の状態価値が戻る前の状態価値より低ければ罰則としてはある程度機能します。(今回はそもそも残りライフを特徴量として与えていないためこの罰則は方策には影響されませんが。)

報酬の与え方はRewardManagerクラスを作成し、管理しました。
f:id:morika-takeuchi:20200909151104p:plain

エージェントの移動方法の変更

もともとサンプルステージでのデフォルトの移動方法は、カメラが向いている方向を正面とした4方向にキー操作で移動するという仕様ですが、今回の場合は画像情報を使用しないことに伴いカメラ操作を学習の対象から外しています。そのため移動する際にどこを正面としてどのように移動するかを設定し直す必要があります。(デモを作成する際も学習時と乖離が起きないように設定し直した移動方法で操作する必要があります。)
これについては以下の2通りのアプローチを試しました。

  • 移動操作における正面を常にワールド座標上に固定する

最終的に採用した方法です。エージェントの現在地に対して正解となる移動操作が一意に定まるため、正しいコースでステージを攻略するためには最も効率的なアプローチです。
欠点としてはデモを作成する際、カメラの視点と移動方向の前方が一致していないため非常に操作しにくくなるという点です。また、現在地から8方向にしか移動ができなくなります。
レイキャストで検出したオブジェクトの方向とプレイヤーの向きが無関係となるため、例えば「動く足場を検出してそれに向かってジャンプする」などといった「レイキャストで検出したオブジェクトの方向に進む、離れる」動作が求められる場合は、レイを飛ばす方向をプレイヤーの向きに関わらず常に固定する必要があります。

  • 移動操作は前後のみとし、左右キーでプレイヤーの向きを回転させる(初代バイオハザード風)

現在地から任意の角度で移動できる、デモの作成がしやすいといった利点があります。また、エージェントの向きとレイキャストの方向が常に対応するため、検出したオブジェクトに対する相対的な情報を処理しやすくなります。
欠点としてはエージェントの現在地に対して正解となる移動操作が現在の向きに依存するため、エージェントの向きの情報を特徴量として加える必要があるという点です。
エージェントの向きを与える方法は角度の他にも色々と考えられますが、いずれにせよ調整が難しく、しかも方策に与える影響が非常に大きいため採用を断念しました。
エージェントとオブジェクトとの相対的な位置関係のみで完結するゲーム(2Dシューティングなど)であれば採用を検討しても良さそうです。

最大ステップ数などの細かい部分の設定

最大ステップ数は20000ステップ(時間にして大体10分程度)に設定しました。特に深い理由はありませんが、人間が順調にプレイして大体10000ステップかからない程度なので、20000ステップで攻略できなければそれ以上掛けても攻略の見込みは薄いという判断です。20000ステップを越えた場合シーンがリセットされるようにしました。
また、ML-Agentsの機能によりUnity側でフレームスキップを入れる事もできますが、デモ作成時にうまく攻撃が出せないなどの支障をきたしたためフレームスキップはPython側で行うようにしました。

デモの作成

模倣学習に使用するデモの作成にはML-AgentsのDemonstration Recorderコンポーネントを使用しました。1エピソード毎に.demoファイルが作成されるため、うまくいかなかったデモファイルはあとから削除することができます。デモファイルの読み込みはアルゴリズムを実装しているPython側で行いました。

まとめ

主にUnity側で行う作業と、特徴量・報酬を設定する際に留意すべき点などをまとめました。
次の記事では主にPython側で行う作業や工夫、アルゴリズムの詳細などについてまとめていく予定です。

*1:今回の検証ではローカルのWindowsマシンでLinux用にビルドした実行ファイルをリモートのGPUマシンに送ってから学習を行うといった方法(一番手っ取り早かった)を取りました。