ボール「Roller」が立方体「Target」の方向に転がることを学ぶ学習環境を用意しています
強化学習の要素
報酬
RollerAgentがTargetの位置に到着時に+1.0(エピソード完了)
RollerAgentがTargetが床から落下時に±0.0(エピソード完了)
Agentオブジェクトに下記を追加していく
学習環境のエージェント(RollerAgent)にBehavior Parameters
コンポーネントがアタッチされているはずです
これは、エージェントの「観察」と「行動」のデータ型を設定するコンポーネントです
BehaviorName
学習設定ファイルのセクションとしての利用
VectorObservation(観察)
「観察」は、特定のエージェントが利用できる環境の「状態」の部分情報です。
「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方向に加える力
Model
利用するモデルファイル(拡張子はonnx)
BehaviorType
実行モード
Default 学習用Pythonスクリプトが実行中の時は「学習モード」 それ以外でModelが設定済みの時は「推論モード」 それ以外は「Heauristicモード」
Heauristic Only
// ヒューリスティックモードの行動決定時に呼ばれる
public override void Heuristic(in ActionBuffers actionBuffers)
{
var actionsOut = actionBuffers.ContinuousActions;
actionsOut[0] = Input.GetAxis("Horizontal");
actionsOut[1] = Input.GetAxis("Vertical");
}
今回のContinuousActionはサイズ2の配列
[0]にはInput.GetAxis("Horizontal")
[1]にはInput.GetAxis("Vertical")
UnityのPlayボタンを押して、方向キーで「RollerAgent」の操作ができる
(in
について)
作成した学習環境のエージェント(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;
TransformとRigidBodyの参照を定義
public class RollerAgent : Agent
{
public Transform target; // TargetのTransform
Rigidbody rBody; // RollerAgentのRigidBody
(Agentクラスのオーバーライドメソッドを追加していく)
ゲームオブジェクト生成時の初期化を行います。
// ゲームオブジェクト生成時に呼ばれる
public override void Initialize()
{
// RollerAgentのRigidBodyの参照の取得
this.rBody = GetComponent<Rigidbody>();
}
エピソード開始時の初期化を行います。
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);
}
エージェントに観察を渡します。
引数「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速度
}
決定された行動に応じて行動実行を行い、その結果に応じて報酬取得とエピソード完了を行います
// 行動決定時に呼ばれる
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章でも取り上げる予定。
Max Step :
エピソードの最大ステップ。エピソードのステップ数がこの値を超えると、エピソード完了となる。0の場合は無制限。
Target :
ドラッグ&ドロップでTargetを設定
下記スクリプトは最終的な全体のスクリプトになります
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");
}
}
「DecisionRequester」は「何ステップごとに1回決定を要求するか」を設定するコンポーネントです
DecisionPeriod : 何ステップごとに1回決定を要求するか —
ここまででUnity側の設定は終わり、次は学習ファイルを設定し、pythonとUnityを走らせます