EnsekiTT Blog

EnsekiTTが書くブログです。

Unityで強化学習するけど脱サンプルするためにサンプル以下の物を作った話

こんにちは、えんせきです。
時差ボケじゃないとは思うんですが、正月と異なる稼働時間の長さなのか
1万歩歩くために帳尻合わせでやってる散歩のせいかとても眠いです。

つまりなにしたの?

前回はUnityが用意してくれたサンプルを動かしたけど、今回はしょぼくなってもいいから自分のシーンで強化学習してみた。
f:id:ensekitt:20180112023741j:plain

用意するObject

  • Agent

各エージェントは、それぞれ状態と観測結果を持っていて別々に動いてくれる。ただBrain(脳)は1対1ではなく用意する。
今回のAgentは棒。箒を倒れないようにバランス取る的なやつで、探せば当然出てくるレベルのしょぼいやつ。
f:id:ensekitt:20180112020319p:plain
これにScriptを追加する。スクリプトについては前回のを参考に作ってみた。
ensekitt.hatenablog.com

  • Brain

Agentにアクションを渡したり動作結果を受け取ってAcademyに渡すところみたい。動かし方は4種類ある。
ExternalはPythonとかと繋ぐためのコミュニケータ(API)を持っている。
Internalは学習済のモデルを使って自律動作させる。
Playerはキーボードとかジョイスティックをアサインしておくことで人が動かせる。(デバッグの時に使う)
Heuristicはコードによって動きを定義することができる。(テストだと思ってるけど、他の用途があるかもしれない)

  • Academy

Brainを子オブジェクトとして持つ。これはシーンに対して1つ作れば良いみたい。
Engine Configurationはトレーニングモードと推論モード(動かすモード)でレンダリングの品質を指定する。トレーニングの時は凄い小さい画面で時間スケールを大きくして、フレームレートは可能な限り早くするみたいなことをする。
グローバルエピソードの長さ。各エージェントがDone(終了)にならなくてもシーンの長さが設定値になったらDoneにする。

シーンの構成

Main Camera 
Directional Light
EventSystem
Academy
  +Brain
Agent
  +Base
  +Stick

エージェントは出来上がってからいっぱいコピペして増やしてる。

用意するスクリプトは2つ

Academy

今回の使い方だけだったらクラス名だけ更新してAcademyを継承すれば良い。

Agent

受け取った値をどう反映させるかを決める。入力値はぶっ飛んだ値が来ることもあるので値域の制限とかも入れている。
報酬系もここで定義して今回であれば、倒れたら報酬-1にしたりしている。

Decision

Heuristicで実行する時はDecisionを継承したもので作るけど今回はPlayerとInternalとExternalなのでいらない。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FurikoAgent : Agent {
	[Header("Specific to Furiko")]
	public GameObject stick;
	private Rigidbody rb;

	public override List<float> CollectState()
	{
		List<float> state = new List<float> ();
		state.Add (gameObject.transform.localPosition.x);
		state.Add (stick.transform.localRotation.z);
		state.Add (stick.GetComponent<Rigidbody> ().angularVelocity.z);
		return state;
	}

	public override void InitializeAgent(){
		rb = GetComponent<Rigidbody> ();
	}

	public override void AgentStep(float[] act)
	{
                // アクションを受け取って値の範囲を決める。
		if (brain.brainParameters.actionSpaceType == StateType.continuous) {
			float action_x = act [0];
			if (action_x > 1f) {
				action_x = 1f;
			}
			if (action_x < -1f) {
				action_x = -1f;
			}
			if ((gameObject.transform.localPosition.x < 10f && action_x > 0f) ||
			    (gameObject.transform.localPosition.x > -10f && action_x < 0f)) {
         // ベースを左右に動かすところ
				gameObject.transform.Translate (action_x, 0, 0);
			}
		}

                // 報酬を決める。継続していたら+0.01、一定角度以上になったら-1にして停止
		if (done == false) {
			reward = 0.01f;
		}

		float angle_z;
		angle_z = stick.transform.localRotation.eulerAngles.z;
		if ((angle_z > 60f) && (angle_z < 300)) {
			done = true;
			reward = -1f;
		}
	}

	public override void AgentReset()
	{
                //エージェントの状態をリセットする
		gameObject.transform.localPosition = new Vector3 (0f, -3f, 0f);
		stick.transform.localPosition = new Vector3(0f, 0f, 0f);
		stick.transform.localRotation = Quaternion.Euler(0f, 0f, 0f);
		stick.GetComponent<Rigidbody>().velocity = new Vector3(0f, 0f, 0f);
		stick.GetComponent<Rigidbody> ().angularVelocity = new Vector3 (0f, 0f, Random.Range (-0.5f, 0.5f));
	}
}

15Agentで50万回学習した結果がこちら

f:id:ensekitt:20180112022538g:plain
あれ、お互いにあたってね…?

環境はこちら
ensekitt.hatenablog.com

使い方とかはこちらと同じ
ensekitt.hatenablog.com

クリエイティブ・コモンズ・ライセンス
この 作品 は クリエイティブ・コモンズ 表示 4.0 国際 ライセンスの下に提供されています。