まずターミナルを起動し、ML-Agents Toolkitを任意の場所にクローンします。
次にUnityの新しい2Dプロジェクトを作成します。プロジェクトのバージョンは2021.3以降を選択してください。
プロジェクトが開いたら、Window→Package Managerをクリックし「+」からAdd package from disk...を選択します。
クローンしたml-agents→com.unity.ml-agents→package.jsonを選択します。
まずMain CameraのCameraコンポーネントのSizeを7に変更し、下の画像のようにオブジェクトを追加します。
①壁を追加
・ Hierarchy ウィンドウで右クリックし、2D Object→Sprites→Squareを選択します。
・ Squareを選択しInspectorで名前を「UpWall」に変更します。
・ Sprite RendererコンポーネントのColorを黒色に変更します。
・ Transformの値をPosition(0, 6, 0)、Rotation(0, 0, 0)、Scale(11, 1, 1)に変更します。
同じように、Hierarchy ウィンドウから2D Object→Sprites→Squareを選択し、Sprite RendererコンポーネントのColorを黒色に変更して、下と左右の壁を作成していきます。
下の壁
・名前を「DownWall」
・Transformの値をPosition(0, -6, 0)、Rotation(0, 0, 0)、Scale(11, 1, 1)に変更します。
右の壁
・名前を「RightWall」
・Transformの値をPosition(6, 0, 0)、Rotation(0, 0, 0)、Scale(1, 13, 1)に変更します。
左の壁
・名前を「LeftWall」
・Transformの値をPosition(-6, 0, 0)、Rotation(0, 0, 0)、Scale(1, 13, 1)に変更します。
②Circleを追加
・ Hierarchy ウィンドウで右クリックし、2D Object→Sprites→Circleを選択します。
・ Circleを選択しInspectorで名前を「Target」に変更します。
・ Sprite RendererコンポーネントのColorを赤色に変更します。
・Transformの値をPosition(0, 3, 0)、Rotation(0, 0, 0)、Scale(1, 1, 1)に変更します。
③Squareを追加
・ Hierarchy ウィンドウで右クリックし、2D Object→Sprites→Squareを選択します。
・ Squareを選択しInspectorで名前を「SquareAgent」に変更します。
・Transformの値をPosition(0, 0, 0)、Rotation(0, 0, 0)、Scale(1, 1, 1)に変更します。
④オブジェクトをグループ化
・Hierarchyウィンドウで右クリックし、Create Emptyを選択します。
・GameObjectを選択しInspectorで名前を「TrainingArea」に変更します。
・Transformの値をPosition(0,0,0)、Rotation(0,0,0)、Scale(1,1,1)に設定します。
・UpWall、DownWall、RightWall、LeftWall、SquareAgent、Target、を選択してTrainingAreaにドラッグします。
①SquareAgent→Add Componentで、Rigidbody2Dコンポーネントを追加し、Gravity Scaleを0に変更します。
②SquareAgent→Add Componentで適当な名前のスクリプトを追加します。
ここではSquareAgentという名前のスクリプトを作成しました。
作成したスクリプトに下のコードを貼り付けます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//MLAgentsを使えるように名前空間を追加
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using Unity.MLAgents.Actuators;
//MonoBehaviourをAgentに変更
public class SquareAgent : Agent
{
public Transform Target;
public Transform upWall;
public Transform downWall;
public Transform rightWall;
public Transform leftWall;
Rigidbody2D rb;
bool initialPosition;
public float moveForce = 10f;
public override void Initialize()
{
initialPosition = false;
rb = GetComponent<Rigidbody2D>();
}
//エピソードの開始
public override void OnEpisodeBegin()
{
//Agentが壁に近づいた時の処理
if (initialPosition == true) {
initialPosition = false;
this.rb.velocity = Vector2.zero;
this.transform.localPosition = Vector2.zero;
}
//Targetの新しい位置
Target.localPosition = new Vector2(Random.value * 10 - 5,Random.value * 10 - 5);
}
//環境の情報を収集
public override void CollectObservations(VectorSensor sensor)
{
//TargetとAgentの位置
sensor.AddObservation(Target.localPosition);
sensor.AddObservation(this.transform.localPosition);
//Agentの速度
sensor.AddObservation(rb.velocity.x);
sensor.AddObservation(rb.velocity.y);
}
//Agentの移動と報酬
public override void OnActionReceived(ActionBuffers actionBuffers)
{
//Agentを移動させる
Vector2 conSig = Vector2.zero;
conSig.x = actionBuffers.ContinuousActions[0];
conSig.y = actionBuffers.ContinuousActions[1];
rb.AddForce(conSig * moveForce);
//TargetとAgentの距離を取得
float distanceToTarget = Vector2.Distance(this.transform.localPosition, Target.localPosition);
//TargetとAgentの距離が1.42より小さい場合、1.0の報酬を与えてエピソードを終了する
if (distanceToTarget < 1.42f)
{
SetReward(1.0f);
EndEpisode();
}
//Agentが壁に近づいたらエピソードを終了する
else if (rightWall.localPosition.x - 1f < this.transform.localPosition.x || leftWall.localPosition.x + 1f > this.transform.localPosition.x || upWall.localPosition.y - 1f < this.transform.localPosition.y || downWall.localPosition.y + 1f > this.transform.localPosition.y) {
initialPosition = true;
EndEpisode();
}
}
//人が操作するときの処理
public override void Heuristic(in ActionBuffers actionsOut)
{
var continuousActionsOut = actionsOut.ContinuousActions;
continuousActionsOut[0] = Input.GetAxis("Horizontal");
continuousActionsOut[1] = Input.GetAxis("Vertical");
}
}
③SquareAgentのTargetにHierarchyのTarget、upWallにHierarchyのUpWall、dwonWallにHierarchyのDwonWall、rightWallにHierarchyのRightWall、leftWallにHierarchyのLeftWallをそれぞれドラッグします。
④Add ComponentからDecision Requesterを追加し、Decision Periodを10に変更します
⑤Add ComponentからBehavior Parametersを追加して、下のように設定します。
・Behavior NameをCollectorSquareに変更します。
・Vector ObservationのSpace Sizeを8に変更します。
・ActionsのContinuous Actionsを2に変更します。
・Discrete Branchesを0に変更します。
・ModelのInference DeviceをCPUに変更します。
SquareAgentのBehavior ParametersのBehavior TypeをHeuristic Onlyに変更して、Unityエディターを再生したら、SquareAgentを矢印キーで操作できるのでSquareAgentが壁に接近した時、Targetに接近した時の動作を確認します。
正しく動作していることが確認できたら、Behavior TypeをDefaultに戻しておきます。
トレーニングを高速化するためHierarchyウィンドウのTrainingAreaをプレハブ化して、シーン内に重ならないように複数配置します。
クローンしたml-agentsのファイル→configファイルにCollectorSquare.yamlファイルを作成します
CollectorSquare.yamlファイルに下のコードを貼り付けます。(GitHubのMl-Agentsのサンプルのものをそのまま使用しています。)
behaviors:
CollectorSquare:
trainer_type: ppo
hyperparameters:
batch_size: 10
buffer_size: 100
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
yamlファイルの詳細は、下のGitHubのページで確認することができます。
①ターミナルでPythonのバージョンが3.8.13以降であることを確認します。
②サンプルを学習させるために仮想環境を使うのでターミナルを立ち上げて任意の場所にディレクトリを作ってください。今回はSampleという名前のディレクトリを作りました。
③cdコマンドでSampleに移動した後、今回使う新しい環境を作成します。ここでは、learning-envという名前の仮想環境を作成しました。
④仮想環境が作成できたら、作成した仮想環境を有効化します。
⑤仮想環境を有効化している状態でpipとsetuptoolsをアップグレードします。
⑥mlagentsPythonパッケージをインストールします。
⑦仮想環境を有効化している状態でprotobufのバージョンを3.19.6に変更します。
仮想環境は$ deactivateで終了することができます。
仮想環境を有効化している状態でターミナルでcdコマンドを使い、クローンしたml-agentsのファイルに移動します。移動できたら下のコマンドを入力します。
(--run-id=の「CollectorSquare」は好きに変えてもらっても大丈夫です。)
ターミナルにUnityのロゴが表示されたら、Unityエディターでシーンを再生します。
問題なく動作すれば学習が開始されます。
学習はターミナルでCtrl+Cを実行することで終了させることができます。
学習させたモデルはクローンしたml-agentsのresultsの中の保存されます。今回は、resultsにCollectorSquareというディレクトリが作成されているはずなので、そのディレクトリの中のCollectorSquare.onnxをUnityのProjectウィンドウのAssetsにドラッグアンドドロップします。
次に、SquareAgentのBehaviorParametersのModelにCollectorSquare.onnxをドラッグします。
その状態でUnityエディターを再生すると、学習させたモデルを使うことができます。