Unity_Lesson

サンプル

ボール「Roller」が立方体「Target」の方向に転がることを学ぶ学習環境を用意しています


強化学習について(「9_強化学習.ipynb」を参照)

強化学習の要素

サンプル



追加するコンポーネントや手順

Agentオブジェクトに下記を追加していく

  1. Behavior Parametersの追加
  2. Agentクラスのスクリプトの追加
  3. DecisionRequesterの追加


1.Behavior Parametersの追加

学習環境のエージェント(RollerAgent)にBehavior Parametersコンポーネントがアタッチされているはずです
これは、エージェントの「観察」と「行動」のデータ型を設定するコンポーネントです

「観察」は、特定のエージェントが利用できる環境の「状態」の部分情報です。
「VectorObservation」は「観察」のデータ型の1つで、配列で問題解決に必要な情報を格納します。 今回は、観察の配列として、下記のように格納しています

        観察の配列[0] = TargetのX座標     
        観察の配列[1] = TargetのZ座標  
        観察の配列[2] = RollerAgentのX座標  
        観察の配列[3] = RollerAgentのZ座標  
        観察の配列[4] = RollerAgentのX速度  
        観察の配列[5] = RollerAgentのZ速度  


「行動」は、エージェントが実行するポリシーからの指示。 「Continuous」は「行動」のデータ型の1つで、連続値(-1.0~+1.0)を格納します。 今回は、行動の配列として、下記のように格納しています。

        行動の配列[0] = RollerAgentのX方向に加える力  
        行動の配列[1] = RollerAgentのZ方向に加える力  


2.Agentクラスのスクリプトの追加

作成した学習環境のエージェント(RollerAgent)に、「Agentクラスのスクリプト」を追加します。

「Agentクラスのスクリプト」では、「エピソード開始時の初期化」「観察取得」「行動実行と報酬取得」などを実装します。

具体的には下記メソッドをオーバーライドします。

・ void Initialize():ゲームオブジェクト生成時に呼ばれる

・ void OnEpisodeBegin():エピソード開始時に呼ばれる

・ void CollectObservations():観察取得時に呼ばれる

・ void OnActionReceived:行動決定時に呼ばれる


スクリプトを書いていく

①パッケージのインポート

using System.Collections.Generic;
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Policies;


②RollerAgent

TransformとRigidBodyの参照を定義

public class RollerAgent : Agent
{
    public Transform target; // TargetのTransform
    Rigidbody rBody; // RollerAgentのRigidBody

    (Agentクラスのオーバーライドメソッドを追加していく)


③Initialize()

ゲームオブジェクト生成時の初期化を行います。

    // ゲームオブジェクト生成時に呼ばれる
    public override void Initialize()
    {
        // RollerAgentのRigidBodyの参照の取得
        this.rBody = GetComponent<Rigidbody>();
    }


④OnEpisodeBegin()

エピソード開始時の初期化を行います。
RollerAgentが床から落下している時は、RollerAgentの位置と速度をリセットし、Targetの位置は常にリセットします

    // エピソード開始時に呼ばれる
    public override void OnEpisodeBegin()
    {
        // RollerAgentが床から落下している時
        if (this.transform.localPosition.y < 0)
        {
            // RollerAgentの位置と速度をリセット
            this.rBody.angularVelocity = Vector3.zero;
            this.rBody.velocity = Vector3.zero;
            this.transform.localPosition = new Vector3(0.0f, 0.5f, 0.0f);
        }

        // Targetの位置のリセット
        target.localPosition = new Vector3(
            Random.value*8-4, 0.5f, Random.value*8-4);
    }


⑤CollectObservations()

エージェントに観察を渡します。
引数「VectorSensor」のAddObservation()で観察の値を渡します

    // 観察取得時に呼ばれる
    public override void CollectObservations(VectorSensor sensor)
    {
        sensor.AddObservation(target.localPosition.x); //TargetのX座標
        sensor.AddObservation(target.localPosition.z); //TargetのZ座標
        sensor.AddObservation(this.transform.localPosition.x); //RollerAgentのX座標
        sensor.AddObservation(this.transform.localPosition.z); //RollerAgentのZ座標
        sensor.AddObservation(rBody.velocity.x); // RollerAgentのX速度
        sensor.AddObservation(rBody.velocity.z); // RollerAgentのZ速度
    }


⑥OnActionReceived()

決定された行動に応じて行動実行を行い、その結果に応じて報酬取得とエピソード完了を行います

// 行動決定時に呼ばれる
    public override void OnActionReceived(ActionBuffers actionBuffers)
    {
        // RollerAgentに力を加える
        Vector3 controlSignal = Vector3.zero;
        controlSignal.x = actionBuffers.ContinuousActions[0];
        controlSignal.z = actionBuffers.ContinuousActions[1];
        rBody.AddForce(controlSignal * 10);

        // RollerAgentがTargetの位置にたどりついた時
        float distanceToTarget = Vector3.Distance(
            this.transform.localPosition, target.localPosition);
        if (distanceToTarget < 1.42f)
        {
            AddReward(1.0f);
            EndEpisode();
        }

        // RollerAgentが床から落下した時
        if (this.transform.localPosition.y < 0)
        {
            EndEpisode();
        }
    }

決定された行動の取得は引数「ActionBuffers」のContinuousActions/DiscreteActionsを使います。今回のContinuousActionsはサイズ2の浮動小数配列で、ContinuousActions[0]はX方向に加える力(-1.0 ~ +1.0)、ContinuousActions[1]はZ方向に加える力(-1.0 ~ +1.0)が渡されます。

報酬の加算は、AgentクラスのAddReward(float increment)、エピソード完了はEndEpisode()を使います。

「報酬」と「エピソード完了」については3章でも取り上げる予定。

ContinuousActionsについて


Agentクラスコンポーネントのインスペクターでの設定


下記スクリプトは最終的な全体のスクリプトになります

using System.Collections.Generic;
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Policies;

// RollerAgent
public class RollerAgent : Agent
{
    public Transform target; // TargetのTransform
    Rigidbody rBody; // RollerAgentのRigidBody

    // ゲームオブジェクト生成時に呼ばれる
    public override void Initialize()
    {
        // RollerAgentのRigidBodyの参照の取得
        this.rBody = GetComponent<Rigidbody>();
    }

    // エピソード開始時に呼ばれる
    public override void OnEpisodeBegin()
    {
        // RollerAgentが床から落下している時
        if (this.transform.localPosition.y < 0)
        {
            // RollerAgentの位置と速度をリセット
            this.rBody.angularVelocity = Vector3.zero;
            this.rBody.velocity = Vector3.zero;
            this.transform.localPosition = new Vector3(0.0f, 0.5f, 0.0f);
        }

        // Targetの位置のリセット
        target.localPosition = new Vector3(
            Random.value*8-4, 0.5f, Random.value*8-4);
    }

    // 観察取得時に呼ばれる
    public override void CollectObservations(VectorSensor sensor)
    {
        sensor.AddObservation(target.localPosition.x); //TargetのX座標
        sensor.AddObservation(target.localPosition.z); //TargetのZ座標
        sensor.AddObservation(this.transform.localPosition.x); //RollerAgentのX座標
        sensor.AddObservation(this.transform.localPosition.z); //RollerAgentのZ座標
        sensor.AddObservation(rBody.velocity.x); // RollerAgentのX速度
        sensor.AddObservation(rBody.velocity.z); // RollerAgentのZ速度
    }

    // 行動決定時に呼ばれる
    public override void OnActionReceived(ActionBuffers actionBuffers)
    {
        // RollerAgentに力を加える
        Vector3 controlSignal = Vector3.zero;
        controlSignal.x = actionBuffers.ContinuousActions[0];
        controlSignal.z = actionBuffers.ContinuousActions[1];
        rBody.AddForce(controlSignal * 10);

        // RollerAgentがTargetの位置にたどりついた時
        float distanceToTarget = Vector3.Distance(
            this.transform.localPosition, target.localPosition);
        if (distanceToTarget < 1.42f)
        {
            AddReward(1.0f);
            EndEpisode();
        }

        // RollerAgentが床から落下した時
        if (this.transform.localPosition.y < 0)
        {
            EndEpisode();
        }
    }

    // ヒューリスティックモードの行動決定時に呼ばれる
    public override void Heuristic(in ActionBuffers actionBuffers)
    {
        var actionsOut = actionBuffers.ContinuousActions;
        actionsOut[0] = Input.GetAxis("Horizontal");
        actionsOut[1] = Input.GetAxis("Vertical");
    }
}



3.DecisionRequesterの追加

「DecisionRequester」は「何ステップごとに1回決定を要求するか」を設定するコンポーネントです

DecisionPeriod : 何ステップごとに1回決定を要求するか   —

ここまででUnity側の設定は終わり、次は学習ファイルを設定し、pythonとUnityを走らせます