0.はじめに
この記事はLife is Tech ! Kansai Advent Calendar の記事です。
ML-Agentsについて色々調べていくと2Dを使った記事が全然なくて困った過去がありました。
なので今回は2DでRay Perception Sensor 2Dを用いて、ML-Agentsの公式チュートリアル的なものを作ってみました!
2Dを久しぶりに触る人やML-Agentsを触ったばっかりの人に見ていただけると幸いです。
以下完成動画です
1.使用環境
- Unity 2021.3.9f1
- ML-Agent Release 20
2.学習環境の作成
UnityHubを開き新規2Dプロジェクトから「2DMLRaycast」という名前のプロジェクトを作成します。
2-1 必要なPacakageのインストール
次に作成したプロジェクトにML-Agentsを追加していきましょう!
まず、WindowからPacakage Managerを開きます。次に+ボタンからadd package from disk
を選択します。
そしてGithubからインストールしてきたml-Agentのフォルダの「com.unity.ml-agents/package.json」を選択してインポートをします。
2-2 Stageの作成
つづいて、Hierarchy ウィンドウから GameObjectを作成していきましょう。
(作成の方法はHierarchy ウィンドウ下の+ボタンをクリックして下図のように選択してください)
今回必要な機能はStageとAgentTargetの3つなので順番に作っていきます。
- 1.Stage
2D Object > Sprites > Square
を作成します。今回はStage
という名前にしました。
Position(0,0,0) , Rotation(0,0,0) , Scale(20,10,1)にしましょう。 - 2.Agent
2D Object > Sprites > Circle
を作成します。今回はPlayer
という名前にしました。
Position(0,0,0) , Rotation(0,0,0) , Scale(1,1,1)にしましょう。
そしてAdd ComponentでCircle Collider 2D
とRigidbody 2D
を追加しておきます。 - 3.Target
2D Object > Sprites > Square
を作成します。今回はTarget
という名前にしました。
Position(2,2,0) , Rotation(0,0,0) , Scale(1,1,1)にしましょう。
そしてAdd ComponentでBox Collider 2D
を追加しておきます。
更に後々使うので、TargetのタグをTarget
に設定します。
また、この際にPlayer
とTarget
のSprite RendererにおけるAdditional SettingsのOrder inLayer を 1
に設定しておきましょう
すべての設置が終了したらこんな感じになります。(なお視認性をあげるために任意の色の設定とPlayerに簡易的な目をつけています)
2-3 Agentに必要な設定
次にAgentであるPlayer
に機械学習ができるように設定をしていきましょう !
Playerを選択したあとに Add Component を選択しBehavior Parameters
を追加して以下のように設定します。
「Behavior Parameters」 は Agentに必ず追加する必要があります
- Behavior Name:訓練設定ファイルの名前で使用します
- Vector Observation
- Space Size : Vector Observationの大きさ
- Continuous
- Space Size : 行動の大きさ
また、Decision Requester
もPlayerに追加し、Decision Period
を 10 に設定します。
2-4 Ray Perception Sensor 2D の設定
次にPlayerにRay Perception Sensor 2D
を追加し、以下の設定をします。
これはPlayerからRayを飛ばし、ヒットした情報を観測データとして自動でエージェントに送る便利なコンポーネントです。
上から順番に軽く説明していきます。
- Detectable Tags:観測対象になるタグを選択します
- Rays Per Direction: 左右に飛ばすRayの数を設定します。今回の場合値が「3」なので正面に1本、左右にそれぞれ3本の計7本Rayが飛ぶことになります。
- Max Ray Degrees: 一番外側のRayから正面のRayまでの角度
- Ray Length : Rayの長さ
このままだと、Player自体にRayが干渉してしまうのでPlayerのLayerを2: Ignore Raycast
に選択して干渉しないようにしましょう!
Playerのオブジェクトを選択肢して、以下のようにRayが飛んでいるのを確認出来たら準備OKです!
2-5 Playerのスクリプトの作成
それではPlayerにアタッチするスクリプトを書いていきましょう!
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using Unity.MLAgents.Actuators;
public class PlayerAgent : Agent
{
Rigidbody2D rBody;
public Transform Target;
void Start()
{
rBody = this.gameObject.GetComponent<Rigidbody2D>();
}
//初期化
public override void OnEpisodeBegin()
{
//プレイヤーの初期化
this.rBody.angularVelocity = 0;
this.rBody.gravityScale = 0;
this.rBody.velocity = Vector3.zero;
this.transform.localPosition = new Vector3(0, 0.5f, 0);
// ターゲットを生成
Target.localPosition = new Vector3(Random.value * 10 - 2, Random.value * 5 - 2, 0);
}
//環境情報の収集
public override void CollectObservations(VectorSensor sensor)
{
//自身の座標
sensor.AddObservation(this.transform.localPosition);
// 加えた力
sensor.AddObservation(rBody.velocity.x);
sensor.AddObservation(rBody.velocity.y);
}
private float moveSpeed = 5;
private float rotateSpeed = 10.0f;
//行った行動による報酬の決定
public override void OnActionReceived(ActionBuffers actionBuffers)
{
// Actions, size = 2
Vector2 controlSignal = Vector2.zero;
controlSignal.x = actionBuffers.ContinuousActions[0];
controlSignal.y = actionBuffers.ContinuousActions[1];
//回転させる角度
float angle = controlSignal.x * rotateSpeed;
transform.Rotate(new Vector3(0, 0, -angle));
float angleDir = (transform.eulerAngles.z + 90) * Mathf.Deg2Rad;
Vector3 dir = new Vector3(Mathf.Cos(angleDir), Mathf.Sin(angleDir), 0.0f);
rBody.velocity = dir.normalized * moveSpeed * controlSignal.y;
// 報酬
float distanceToTarget = Vector3.Distance(this.transform.localPosition, Target.localPosition);
if (distanceToTarget < 1.3f)
{
SetReward(1.0f);
EndEpisode();
}
else if (Mathf.Abs(this.transform.localPosition.x) > 9.5f || Mathf.Abs(this.transform.localPosition.y) > 4.5f)
{
EndEpisode();
}
}
public override void Heuristic(in ActionBuffers actionsOut)
{
var continuousActionsOut = actionsOut.ContinuousActions;
continuousActionsOut[0] = Input.GetAxis("Horizontal");
continuousActionsOut[1] = Input.GetAxis("Vertical");
}
}
このコードをPlayerにアタッチしたあとに、Target の GameObject をドラッグ & ドロップで PlayerAgentに設定します。
2-6 Heuristicでの動作テスト
ここまで出来たのなら一度Heuristicモードで操作できるか確認してみましょう!
HeuristicモードはBehavior Parameters
の「Behavior Type」をHeuristic Onlyにして再生をすると操作することが出来ます。
3 訓練ファイルの作成
続いて訓練ファイルを作成していきましょう!
予めGitHubからクローンしたml-agentフォルダに移動し、config/ppo下に2DBall.yaml
ファイルを作成し、以下を書いていきます。
behaviors:
2DBall: # Behavior Name
trainer_type: ppo # トレーナー種別
# 学習アルゴリズムの設定
hyperparameters:
batch_size: 128
buffer_size: 256
learning_rate: 3.0e-4
beta: 5.0e-4
epsilon: 0.2
lambd: 0.99
num_epoch: 3
learning_rate_schedule: linear
# ニューラルネットワークの設定
network_settings:
normalize: false
hidden_units: 128
num_layers: 2
# 報酬の設定
reward_signals:
extrinsic:
gamma: 0.99
strength: 1.0
max_steps: 500000
time_horizon: 64
summary_freq: 10000
4 強化学習の実施
すべて完了したらPythonのml-agentフォルダにて以下のコマンドを実行します。
このときの「run-id」は学習する際のIDで出力先のフォルダ名になります。
仮にIDを上書きで実行したい場合は末尾に --force
をつけてあげてください
$ mlagents-learn config/ppo/2DBall.yaml --run-id=2DBall
上記のコマンドを実行したあとにUnity EditorのPlayボタンを押すと学習が始まります。
ある程度学習させたあとにUnity Editorの再生を停止するとml-agentフォルダの「results/2DBall」に「2DBall.onnx」というモデルが生成されています。
そのモデルをUnityのAssets下に持ってきたあとに、Behavior Parameters のModelにドラッグ&ドロップしてモデルの設定をします。
その後シーンを実行すると学習モデルを実行することが出来ます!
5 最後に
いかがでしたでしょうか。かなり端折った説明にはなっていますが、2DでML-Agentsを使用する際の参考になれば幸いです。
また余談にはなりますが、私自身この分野について勉強し始めたばかりなのとQiitaで執筆するのが初めてなので、間違いや不明点があればお気軽に知らせてください。
参考記事