うどんゲームメモ

ゲームの技術メモをまったりと

Unityで強化学習しよう! ML - Agentsのススメ

始めに

本記事はサムザップ Advent Calendar 2017 10日目の記事になります
昨日の記事は@xxxxxxxxxxさんのLua+PEGでiniをparseするでした!


今年の9月にUnity公式から強化学習ライブラリが公開されました blogs.unity3d.com

公開したてでまだ若干使い勝手の悪い部分もありますが、TensorflowSharpなどで自力で強化学習環境を構築するよりは断然楽だと思います
今回はこちらのライブラリの紹介と、それを使うために構造の解説をしていきます
環境導入であったり実際にどう学習させるかは、いくつかのサイトがサンプルを用いて説明されているので、そちらを参考にしていただければ幸いです(自分が参考にしたサイトはこちら)

Unityの公式サンプルml-agentsでAIを試す - tanaka's Programming Memo

ML-Agentsとは

先程も書きましたが、ML-AgentsはUnity内で強化学習をするためのライブラリです
Googleが公開している機械学習用ライブラリ「Tensorflow」、OpenAIが2017年に発表した強化学習アルゴリズム「PPO(Proximal Policy Optimization)」、これら2つがこのライブラリのベースとなっています
python等をこちらが組む必要はなく、普段のUnity開発の要領で学習環境などを組み上げれば強化学習が行える優れものです


ML-Agentsの構造

MLAgentsの構成は主に3種類のパートに分かれます
f:id:myudon:20171208155603p:plain
Unityのシーン内にこれら3パートを配置することで強化学習を行うことができます(BrainはAcademyの子にする)
Agent、Brain、Academy、この3パートをソースコードを交えつつ解説していきたいと思います

Agent

まず学習するのに必要な状態、行動、ルールの定義を行うパートです
例としてスーパーマリオで考えてみたいと思います
最初に状態を考えてみましょう、マリオの位置や敵の位置、キノコを所持しているか等がありますね
次に行動です、歩く、ジャンプする、状態によってはファイアボールで攻撃するなどが挙げられます
最後にルールを考えます、マリオは敵に当たってはいけないため敵に当たったときには負の報酬を、前に進めば進むほどゴールに近づいていくため正の報酬を与えてあげます
実際はこんなに単純ではないですがこのようにして学習のために機械が考えることを定義してやるのがAgentパートです

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

//簡易的なサンプル
public class AgentBase : Agent
{
    [SerializeField]
    private Transform _mario;

    /// <summary>
    /// エージェントの初期化処理です
    /// 必須ではなく、Agentを立ち上げた際に1回だけ呼ばれます
    /// </summary>
    public override void InitializeAgent()
    {

    }

    /// <summary>
    /// 状態の取得です
    /// ここで状態を定義するため、学習において必須になります
    /// Transformやコンポーネントの状態、他のオブジェクトの情報など、学習の判定に際して必要な情報を定義づけします
    /// 状態はfloat配列で渡すため、情報をfloat化してあげる必要があります
    /// </summary>
    /// <returns>エージェントの状態</returns>
    public override List<float> CollectState()
    {
        List<float> state = new List<float>();
        state.Add(_mario.position.x);
        state.Add(_mario.position.y);
        state.Add(_mario.position.z);
        return state;
    }

    /// <summary>
    /// 1ステップごとにエージェントが行う動作の定義づけです
    /// 動作を示すパラメータはfloat配列によって渡されるためそれを元に何をするかを決定していきます
    /// actionの動作タイプにもケースに分けて既定の動作をするタイプと、物理挙動など小数単位で動作が分類されるものとあるので作るものに沿って考えていく必要があります
    /// また、ここで報酬の定義を行い、その報酬によってリセットなどの処理も行います
    /// </summary>
    /// <param name="action"></param>
    public override void AgentStep(float[] action)
    {
        if (action[0] == 0)
        {
            _mario.position += new Vector3(0.5f, 0f, 0f);
        }
        else if (action[0] == 1)
        {
            _mario.position += new Vector3(-0.5f, 0f, 0f);
        }

        //敵に当たったら報酬をマイナスして、学習をリセットしてみる
        Collider[] hitObjects = Physics.OverlapBox(_mario.position, new Vector3(0.3f, 0.3f, 0.3f));
        if (hitObjects.Where(col => col.gameObject.tag == "enemy").ToArray().Length == 1)
        {
            reward = -1f;
            done = true;
        }
    }

    /// <summary>
    /// 1エピソード分の学習が終わった際にリセットしない場合に呼ばれるメソッドです
    /// 1エピソードの学習終了時に基本はリセット処理を呼びますが、設定で呼ばないようにしている場合こちらの処理を通すことができます(インスペクタで設定できます)
    /// リセット処理を行うとエピソード終了フラグもリセットされますが、こちらは残ったままになります(シーンからAgentを取り除くときなどに使用するとか)
    /// </summary>
    public override void AgentOnDone()
    {

    }

    /// <summary>
    /// Agentのリセット処理です
    /// 1エピソード分の学習が終わった後にAgentを最初の状態に戻します
    /// </summary>
    public override void AgentReset()
    {
        _mario.position = new Vector3();
    }

}

最低限状態の定義であるCollectStateと行動やルール定義を行うAgentStepさえ記述がされていればAgentは機能を果たすことができます
サンプルコードでは記述がされていませんが、正の報酬がないと基本的にはAgentはモチベーションを持って行動することができません
なのでしっかりと報酬を定義し、自分の望んだルールを正確に実装することが強化学習において大切になってきます

Brain

Brainはその名の通り行動を考える部分になります、Agentに行動を指示し制御します
Brainの行動決定には4つのタイプがあります(タイプ自体はインスペクタからいじることができます)
1つ目がExternalです、外部からの制御、いわばTensorflowから制御させるタイプです
2つ目がInternalです、強化学習によって得られた学習データによって制御させるタイプです
3つ目がPlayerです、単純にプレイヤーがコントローラーなどから制御させるタイプです
4つ目がHeuristicです、コーディングによって制御させるタイプです
以上の4タイプを用いてBrainからAgentに行動を指示していきます、基本的にはPlayerやHeuristicで環境の確認を行い、Externalで学習、Internalで学習結果の利用を行っていく流れですね
Brainに関しては特にソースを記述する必要はなく、インスペクタ上の設定で基本的には十分です

Academy

学習における環境を定義する部分です、学習ステップのスピードやエピソード単位での学習量の調整、また学習時のプレビュー設定も可能です
また、ステップごとの環境の処理やエピソードごとに環境をリセットする場合にも処理をします

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;

//簡易的なサンプル
public class MarioAcademy : Academy
{

    [SerializeField]
    private GameObject _mario;

    /// <summary>
    /// Academyの初期化処理です
    /// 環境を立ち上げた際に1回だけ呼ばれます
    /// </summary>
    public override void InitializeAcademy()
    {

    }

    /// <summary>
    /// 1ステップ毎に環境側が行う動作です
    /// 動的にオブジェクトを制御したり、resetParameterを使って環境状態の制御を行ったりします
    /// </summary>
    public override void AcademyStep()
    {

    }

    /// <summary>
    /// Academyのリセット処理です
    /// 1エピソード分の学習が終わった後にAcademyを最初の状態に戻します
    /// 今回は分かりやすくFindで記述しました
    /// </summary>
    public override void AcademyReset()
    {
        //敵をリセットしたり
        var enemys = GameObject.FindGameObjectsWithTag("enemy");
        foreach(GameObject enemy in enemys)
        {
            Destroy(enemy);
        }

        //ステージ情報のリセットなどもここで行うと良いでしょう
    }

}

Academyに関しては記述の必要がないときは何も書かなくても大丈夫です
上記のサンプルコードはかなり簡易的ですが、学習環境が複雑化してくるとなかなかに大変になると思います


まとめ

今回はML-Agentsで実際に強化学習をするためにUnity側が何をすればいいかを解説しました
実際は強化学習の用途などによって必要な実装部など大きく変わってくるため、その都度公式のドキュメントを参考にし実装をしていくことになります
Unityで強化学習が使えるようになることはゲーム開発のみならず、学術的な研究にも応用できるため知っておいて損はないかなと思います
今現在自分もこのML-Agentsを使って自律型AIや自動テスト機構などを開発する試みをしています、成果が出次第、また投稿します
少しでも強化学習に興味が湧いた人は、簡単なサンプルもありますので触ってみてはいかがでしょうか