ML agentsの健忘録


# MlAgentsを使用して玉を追いかける学習をした


**参照記事**
前半

https://qiita.com/ref3000/items/40a0915fbc4478b5cadb

後半

https://gametukurikata.com/machinelearning/chasingmlagentscharacter

# 学習方法 Unity内コンポーネント
必要なコンポーネント3つ
1 :Agentクラスを引き継いだコンポーネント
2 :DecisionRequester
3 :BehaviorParameters
をオブジェクトに着ける　(this.transformができるオブジェクトの方が楽かも)
1
状態のエピソードを開始(観測、報酬与える、行動決定など)するために使用
2
観察→決定→行決定の間にアクションを取り
観察→決定→アクション→報酬　というステップを繰り返すために使用
Decision Requesterコンポーネントを取り付けていない場合はスクリプトでAgent.RequestDicisionメソッドを呼び出して決定をする必要がある。
3
観測値のパラメータ数、学習データのパラメータ数を設定するのに使用(学習系全般の設定に使用)

![mlagetns玉追いかけ必要最低限コンポーネント.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1982114/aab3e9f5-c29b-52f9-3d94-e69bc35c885a.png)


# 学習方法 コマンドライン


MLagents ml-agents-release8branch -release8branch の中にProject(unityのプロジェクト)がありそれが　デモ、サンプルなど基本的なことが一通り入っている。
Projectをコピーして別の名前で保存 → unity からリストに追加する
新しい学習のプロジェクトができる。
ml-release8branch　にunityproject(project)とresults(学習結果のモデル入るやつ)とconfig(学習時のパラメータを設定するやつ)が入っている。


anacondaprompt から
PC / Windows /MLagents /ml-agents-release8branch /ml-release8branch に行く

その後以下を打ち　mlagents起動できるようにする
```
conda activate mlagents
```
以下を押しunityを実行することで学習開始する
```
mlagents-learn .\config\ppo\RollerBall.yaml --run-id=n
```
作成したNNファイル（学習の重みの結果）はml-release8branch内のresultsの中に入っている。
run-id ごとに生成したNNデータを保存するファイルが生成される

学習の続きを実行する場合
```
mlagents-learn .\config\RollerBall.yaml --run-id=RollerBall --resume
```
学習を破棄して新しく実行する場合
```
mlagents-learn .\config\RollerBall.yaml --run-id=RollerBall --force
```
:::note warn
警告
.\config\RollerBall.yaml でなく.\config\ppo\RollerBall.yamlかもしれないので階層構造を見ること
:::


意味　
mlagents-learn :学習を指定
.\config\RollerBall.yaml :今のフォルダ内のconfig\RollerBall.yamlで学習する
(config/rollerball_config.yaml は学習のハイパーパラメータ情報　2行目にUnityと対応付ける名前 RollerBallが入っている)

:::note warn
警告
Unity内ScriptのBehaviorParametersのBehaviorNameとRollerBall.ymal内2行目RollerBollを同じ名前にする
:::


configの中にパラメータを入れたrollerball_config.yamlファイルを作成しておく(名前に対応した学習がされる)

# 学習法解説
```RollerAgents.cs
using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Actuators;
using Unity.MLAgents.Sensors;
using Random = UnityEngine.Random;

public class RollerAgent : Agent
{
    Rigidbody rBody;
    public Transform target;

    //前回の距離を保持
    float previousDistance;

    // 初期化
    public override void Initialize()
    {
        rBody = GetComponent<Rigidbody>();
    }

    // エピソード開始時
    public override void OnEpisodeBegin()
    {
        // 落ちたらリセット
        if (this.transform.localPosition.y < 0)
        {
            this.rBody.angularVelocity = Vector3.zero;
            this.rBody.velocity = Vector3.zero;
            this.transform.localPosition = new Vector3(0.0f, 0.5f, 0.0f);
        }

        // 箱をランダムに配置
        target.localPosition = new Vector3(Random.value * 8 - 4, 0.5f, Random.value * 8 - 4);

        //前回の距離を記録
        previousDistance = Vector3.Distance(this.transform.localPosition, target.localPosition);
    }


    // 観測値取得時
    public override void CollectObservations(VectorSensor sensor)
    {
        sensor.AddObservation(target.localPosition); //TargetのXYZ座標
        sensor.AddObservation(this.transform.localPosition); //RollerAgentのXYZ座標
        sensor.AddObservation(rBody.velocity.x); // RollerAgentのX速度
        sensor.AddObservation(rBody.velocity.z); // RollerAgentのZ速度
    }

    // 行動実行時
    public override void OnActionReceived(ActionBuffers actionBuffers)
    {
        // ボールに力を加える
        rBody.AddForce(actionBuffers.ContinuousActions[0] * 10, 0, actionBuffers.ContinuousActions[1] * 10);

        // 前回より近づいた分報酬。離れた分お叱り
        var nowDistance = Vector3.Distance(this.transform.localPosition, target.localPosition);
        AddReward((previousDistance - nowDistance) * 0.1f);
        previousDistance = nowDistance;

        //時間の経過とともにお叱り もともと短時間で出来るだけ多くの報酬を得るように成長するのでいらない
        //AddReward(-0.01f);

        // 箱に接触した場合は報酬を与えてリセット。
        if (Vector3.Distance(this.transform.localPosition, target.localPosition) < 1.4f)
        {
            AddReward(10.0f);
            EndEpisode();
        }

        // 落下した時場合はお叱りを与えてリセット。
        if (this.transform.localPosition.y < 0)
        {
            AddReward(-13.0f);
            EndEpisode();
        }
    }

    // キー入力をパラメータに変換
    public override void Heuristic(in ActionBuffers actionsOut)
    {
        var actions = actionsOut.ContinuousActions;
        actions[0] = Input.GetAxis("Horizontal");
        actions[1] = Input.GetAxis("Vertical");
    }
}
```

**解説**
強化学習を行うゲームオブジェクトにはMonoBehaviourクラスを継承してクラスを作成するのではなくAgentクラスを継承しoverrideして作成する。
(public class RollerAgent : Agent)

**Agentクラスのメソッド紹介**

public override void Initialize() :エージェント初期化時
public override void OnEpisodeBegin() :エピソード開始時
public override void CollectObservations() :エージェントが観察するデータの収集時
public override void OnActionReceived(ActionBuffers actionBuffers) :観察によって収集した情報を決定した後にアクションを起こす時
public override void Heuristic(in ActionBuffers actionsOut) :学習状態でない時にテストプレイヤーがキャラクターを操作したりを切り替える時に使用

**1エピソードとは？**
→　MaxStepで指定したステップ回数に達した(目的達成できなかった)またはEndEpisode関数を呼び出した(目的達成できた)時に学習が終了する。その区切りのこと

**このプログラムの流れ**
Initialize → OnEpisodeBegin → CollectObservations → OnActionReceived

**OnEpisodeBegin**



**CollectObservations**
学習に用いるデータ収集に使用する　どのタイミングで呼ばれているのかは未検証
データは学習に必要なもので構成し外れ値、欠損値、正規化する方が良い。
追加するデータ数の合計をBehavior ParametersコンポーネントのVector ObservationのSpaceSizeに設定する。
→　ActionReceived関数の引数の次元が設定される

sensor.AddObservation(this.transform.localPositionならx,y,zの3次元)

```ActionReceived.cs
public override void CollectObservations(VectorSensor sensor) {
    sensor.AddObservation(/*追加したい学習データ*/);
}
```

**OnActionReceived**
観測値から得られたデータを元に行動を決め、報酬を与える。また、条件を満たした場合エピソードを終わらせる

報酬はAgentクラス内のAddReward(/*報酬値*/);
エピソード終了はAgentクラス内のEndEpisode();

学習の仕組み
状態観測 → 重み付け → 行動 → 報酬 → 重み更新（学習）
この繰り返しによりRewardが大きくなる重みを求めることができる。
ターゲットと自身の位置が近いときに報酬もらえる(学習) → ターゲットと自身の座標記録(観測)　→　ターゲット方向に力加える(行動)　

actionBuffersについて
観測値に重みを着けた学習データのこと
VectorAction - SpaceSizeから変更できる　今回は2(X,Z)移動のみを行動とするため。

与える報酬について
-1~1の間が良い

```ActionReceived.cs
public override void OnActionReceived(ActionBuffers actionBuffers){
        // ボールに力を加える
        rBody.AddForce(actionBuffers.ContinuousActions[0] * 10, 0, actionBuffers.ContinuousActions[1] * 10);
        // 前回より近づいた分報酬。離れた分お叱り
        var nowDistance = Vector3.Distance(this.transform.localPosition, target.localPosition);
        AddReward((previousDistance - nowDistance) * 0.1f);
        previousDistance = nowDistance;
}

```

# Behavior Parametersの設定
Vector Observationは観察するデータに関しての設定です。

VectorObservation - Space Size
CollectObservationsメソッドで追加した観察データの数　(例)this.transform.localPositionならx,y,zの3つ

Stacked Vectors
観察データを過去の何ステップ分を保持しておくかのデータです。
今回は過去の3ステップ分を保持する。

Vector Action
行動のVectorデータに関しての設定

Space Type
Discreteは離散データで-1、0、1等の整数データでContinuousは浮動小数点データです。
今回はSpace TypeはContinuous
Space SizeはXとZ方向の入力データの2にする

Modelには実際に学習させたデータを設定しますが、まだ学習していないので現時点では設定しません。

Inference DeviceはCPUとGPUどちらを使って学習するか。今回はCPU

Behavior Type
動作のタイプでInferenceは学習データを使って推論でエージェントを動かす。
HeuristicはHeuristicメソッドで設定したとおり入力してエージェントを動かす
Defaultだと学習中はInferenceにし、学習をしていない時にプレイボタンを押したらHeuristicにする
Use Child Sensorsは敵キャラクターの子要素にあるセンサーコンポーネント（RayPerceptionSensor3D等）を使用するかどうか

Observable Attribute Handlingは観察可能な属性の取り扱いの設定
Ignoreは無視
Exclude Inheritedは継承を除外、自身のクラスのみ調べる
Examine Allは全てを調べる

# 学習環境の複製
作成した学習環境をコピペして10個くらいに複製しておくだけでOK


# 学習設定ファイル
behaviorの下(2行目)とUnityのBehavior ParametersのBehavior Nameと一致させる。

max_steps
学習ステージで行う全てのステップ数。
summary_freq
コマンドプロンプトに表示される経過をsummary_freqステップ毎にする
他の設定は以下参照

https://github.com/Unity-Technologies/ml-agents/blob/main/docs/Training-Configuration-File.md