Unityで機械学習(その3)

Unity

前回はサンプルシーンを用いて基本的な流れを確認したが、今回は1からプロジェクトを作成して機械学習を行ってみよう。(この記事は公式に上がっているチュートリアルをもとにしている)

目指す状態

まずは今回の目指す状態を確認しよう。以下のように床の上にキューブとボールがある。ボールを操作しランダムに現れるキューブにぶつけるというのが今回の目的だ。
このボール操作を学習させる。

準備

この記事は以下の2つを終えていることが前提なのでまだの人はそちらから行ってほしい。
○環境構築編
-Windowsはこちら(準備中)
Macはこちら
その2

作成

ではさっそく作っていこう。

Unityプロジェクト作成

1.UnityHumから新規3Dプロジェクトを作成する。今回プロジェクト名はRollerBallとした。

パッケージのインポート

WindowメニューのPackageMangerを選択して、Unity2019は左上、Unity2018は下にある+ボタンを押す。

Import from diskを選択して、GitHubからクローンしたml-agentsフォルダにある。
com.uniyt.ml-agnets->package.json
を選択する。

シーンの作成

シーンを作成していこう。

○新規3DオブジェクトからPlaneを選択してFloorとリネーム

○新規3DオブジェクトからCubeを選択してTargetとリネーム,トランスフォームを以下のように調整する。

○新規3DオブジェクトからSphereを作成しRollerAgentとリネーム、トランスフォームを以下のように調整

○RollerAgentにRigidbodyをAddComponentする。

○オブジェクトをまとめておこう。CreateEmptyをして(この際、トランスフォームがリセットされていることを確認すること)TrainingAreaとリネーム。先程作成した3つのオブジェクトを子要素にする。

スクリプトの作成

○新規にRollerAgentスクリプトを作成し、RollerAgentにアタッチする。

○RollerAgentをエディターで開いて以下のように記述する

using UnityEngine;
using Unity.MLAgents;
using Unity.MLAgents.Sensors;
using Unity.MLAgents.Actuators;
public class RollerAgent : Agent {
    Rigidbody rBody;
    void Start() {
        rBody = GetComponent<Rigidbody>();
    }

    public Transform Target;

    //エピソード開始時に呼ばれる
    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, 0.5f, 0);
        }

        // ターゲットをランダムな場所に移動
        Target.localPosition = new Vector3(Random.value * 8 - 4,
                                           0.5f,
                                           Random.value * 8 - 4);
    }
    //観測値の設定を行う(引数のsensorに観測したい値を登録する)
    public override void CollectObservations(VectorSensor sensor) {
        // ターゲット(Cube)の位置
        sensor.AddObservation(Target.localPosition);
        //球の位置
        sensor.AddObservation(this.transform.localPosition);

        //球の速度のx成分
        sensor.AddObservation(rBody.velocity.x);
        //球の速度のz成分
        sensor.AddObservation(rBody.velocity.z);
    }
    public float forceMultiplier = 10;

    //行動ポリシーに基づき決定したアクションに対して報酬とエピソードの終了を行う
    //決定されたアクションが引数に入る
    public override void OnActionReceived(ActionBuffers actionBuffers) {
        
        Vector3 controlSignal = Vector3.zero;
        //actionBuffersに2つの値がfloat値で渡される
        controlSignal.x = actionBuffers.ContinuousActions[0];
        controlSignal.z = actionBuffers.ContinuousActions[1];
        //渡された値をもとにAddForce(球を動かす)
        rBody.AddForce(controlSignal * forceMultiplier);

        /***Rewards(報酬)と終了****/
        //球とターゲットの距離を計測して
        float distanceToTarget = Vector3.Distance(this.transform.localPosition, Target.localPosition);

        // もし接触していたら
        if (distanceToTarget < 1.42f) {
            //1.0の報酬を獲得
            SetReward(1.0f);
            //エピソード終了
            EndEpisode();
        }

        // もし球が床から落ちてしまったら
        else if (this.transform.localPosition.y < 0) {
            //エピソード終了
            EndEpisode();
        }
    }
    //このモードを設定しておくと手動での検証が可能となる
    public override void Heuristic(in ActionBuffers actionsOut) {
        var continuousActionsOut = actionsOut.ContinuousActions;
        continuousActionsOut[0] = Input.GetAxis("Horizontal");
        continuousActionsOut[1] = Input.GetAxis("Vertical");
    }
}

コンポーネントのアタッチと設定

○RollerAgentを選択しBehavierParameterコンポーネントを追加する(エージェントの観察と行動のデータ型を指定するコンポーネント)
注)すでについてた場合は不要
○同じくDecisionRequesterコンポーネントを追加する(何ステップ毎に決定を要求するかを決めるコンポーネント)
○パラメータを以下のように設定する

実験

先程、スクリプトで手動による検証を可能にしておいたのでとりあえず手動で試してみよう。

○カメラのトランスフォームを以下のように設定する

○実行してみよう。矢印キーでボールに力を加えることができるのでターゲットにぶつかることを目標にして動かしてみよう。触れるとランダムな場所にターゲットが移動する。床から落ちてしまったらやり直しだ。

割と簡単。。。

機械学習の実践

今あなたはターゲットとボールの位置を判断して、矢印キーの操作を行ったはずだ。
この操作を機械に学習させてみよう。

ハイパーパラメータ設定ファイルの作成

強化学習の際に必要となる細かな設定をまとめたファイルを作成しよう。

○GitHubからクローンしたml-agentsフォルダに行き以下のコマンドを入力しyamlファイルを作成する。

$ vi config/rollerball_config.yaml

記述内容は以下をコピペ(このファイルの詳細は次回以降)

behaviors:
  RollerBall:
    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ファイルをもとにして学習を開始しよう。
任意のID(ここではMyRollerBall)を付与して以下のコマンドを実行する。

$ mlagents-learn config/rollerball_config.yaml --run-id=MyRollerBall

画面が立ち上がりUnityを開始せよ!と言っているのでUnityに戻って実行しよう。

このとき、先程手動で実行したときのままになっていたりするとうまく開始出来ずに終了してしまう。そのときには以下のコマンドで最初から行う。(–forceオプションをつける)

$ mlagents-learn config/rollerball_config.yaml --run-id=MyRollerBall --force

学習が始まった!

途中で終了

50万ステップ眺めていても良いが、平均報酬が1に近づいたら終わりにしてしまってよいだろう。その場合はUnityに戻って再生を停止する。

途中で学習が終了し、Modelファイルが保存された。

作成されたファイルはresults->起動ID名フォルダの中にonnxファイルとして作成されているのでそれをUnityのプロジェクトにドラックで配置

エージェントにアタッチ

エージェント(球)を選択して、model部分にアタッチする

学習の成果を見よう!

実行してみよう、学習の成果によって無駄なく、落ちることなくターゲットに接触することができるようになった。

スムーズ・・・

終わりに

以上で今回の内容は終了だ。1からプロジェクトを作成し学習することに成功した。
次回は今回やった学習をより高速に行う方法を学習する。

Unity
スポンサーリンク
シェアする
mjpurinをフォローする
ジョイタスネット

コメント

タイトルとURLをコピーしました