はじめに
こんにちは。プロダクト開発部兼UGC事業部の長野です。
気づけば3年半振りの投稿です。
本業ではphpやJavaScriptを触っていますが、このブログでは引き続きunityネタで行きます。
今回はunityに今流行のDeepLearningを組み合わせたlisというものを使ってみます。
lisとは
2016年にドワンゴ人工知能研究所で産まれた超人工生命です。
詳しくはこちらの記事を参照ください。
公式のリポジトリはこちらです。
環境
以下の2台構成です。
クライアント(unity担当)
MacBook Air(13-inch, Early 2014)
unity 2017.1.1f1 personal
サーバー(python,chainer,dqn担当)
OS Ubuntu 14.04 LTS
GPU Geforce GTX 980 4GB
python 2.7
chainer 1.9
やりたいこと
某横スクロールアクションのように壁をジャンプで飛び越えて進むエージェントを作ってみます。
以下のルールを設けました。
・一本道をひたすら前進する
・壁をランダムな間隔で配置し、壁にぶつかると-5点
・ジャンプで壁を飛び越えることができる(但し無駄にジャンプはさせたくないのでジャンプすると-1点)
やってみる
githubから落としたunityのプロジェクトには4つのサンプル用のsceneが入っています。
この内の「sample」を改造して作ります。
githubからダウンロードしたフォルダ内の「unity-sample-environment」をunityで開き、Assets➔Scenes➔sampleを開きます。
サーバー情報の設定
まずはサーバーの設定をします。
ローカルマシンだけで全て行う場合は恐らくデフォルトで大丈夫です。
SceneControllerのdomainとportを任意の値に変更してください。
地面
下に敷かれている地面用のオブジェクトの大きさを変えます。1方向に進めばいいので細長くします。
これは必ずしも変える必要ありません。
planeの以下の値を変更してください。
scaleのzを10➔0.5
positionのx➔-45
エージェント
このままだとあらぬ方向に動いて落ちていくので向きを変えます。
agentのrotateのy➔270
障害物
触れるとマイナスになる障害物です。
すでにあるオブジェクトの中ではPlusRewordItemを変更すれば簡単に作れるのでそうします。
PrefabsのPlusRewordItemを選択し、
Rewordを1➔-5
さらに飛び越えやすいように薄くします。
ScaleのXを1.5➔1、Zを1.5➔5
障害物の初期配置の処理を変更する
ここからはソースコードの変更です。
まずはScriptsのEnvironmentをダブルクリックして下さい。
Environment.cs
クラス内を以下のように書き換えます。
一定の間隔を保ちつつ、ランダムな距離で障害物を1直線に配置するような処理です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
int itemCount = 10; [SerializeField] List<GameObject> itemPrefabs; void Start () { } void Update () { } public void OnReset() { foreach(Transform i in transform) { Destroy (i.gameObject); } int prePos = 0; for (int i = 0; i < itemCount; i++) { prePos = prePos - UnityEngine.Random.Range(6, 10); Vector3 pos = new Vector3 ( prePos, 0.8f, 0); pos += transform.position; int itemId = UnityEngine.Random.Range(0, itemPrefabs.Count); Debug.Log("itemId = " + itemId); GameObject obj = (GameObject)GameObject.Instantiate (itemPrefabs[itemId], pos, Quaternion.identity); obj.transform.parent = transform; } } |
エージェントの動き部分を変更する
action.cs
以下のように変更します。
元々は回転などの動きもエージェントにさせていますが、
今回はジャンプだけなので余分なcaseを削除しています。
〜変更前〜
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
switch (command) { case "0": rotate = 1; break; case "1": rotate = -1; break; case "2": forward = 1; break; case "3": jump = true; break; } |
〜変更後〜
1 2 3 4 5 6 7 8 |
forward = 1; switch (command) { case "0": break; case "1": jump = true; break; } |
MyFirstPersonController.cs
private void FixedUpdate()
のジャンプ部分の処理にマイナス-1点の処理を追加します
〜変更前〜
1 2 3 4 5 6 7 |
if (m_Jump) { m_MoveDir.y = m_JumpSpeed; PlayJumpSound(); m_Jump = false; m_Jumping = true; } |
〜変更後〜
1 2 3 4 5 6 7 8 |
if (m_Jump) { m_MoveDir.y = m_JumpSpeed; PlayJumpSound(); m_Jump = false; m_Jumping = true; m_agent.AddReward(-1); } |
unity側はこれで終わりです。
サーバー側の変更
サーバー側も少し変更します。
※まずサーバー上でgitからソースをダウンロードして、README.mdの手順でセットアップをしてください。
cnn_dqn_agent.py
今回は「何もしない」「ジャンプ」の2アクションなので
1 |
actions = [0, 1, 2] |
を
1 |
actions = [0, 1] |
に変えます。
学習させてみる
サーバーで以下のコマンドを叩きます。
python ~/lis/python-agent/server.py -i=0.0.0.0 -g=0
unityの再生ボタンを押します。
うまく接続されると、コンソール上に以下のような文字がつらつら表示されます。
また、unity上でエージェントが動き出します。(下の画像が動かない場合はクリックしてください)
あとは待つのみです。
3時間後・・・
始めの頃に比べて動きが良くなっている気がします。実際にどれくらい学習されているかを確認するにはサーバー上の「reward.log」を確認します。
さらに可視化するにはサーバー上でpython plot_reward_log.pyを叩くと良いようです。
これで出力される画像を見ると、時間の経過と共にスコアが徐々に良くなっていることがわかります!
まとめ
今回は簡単な動きしかしていませんが、複雑な動きも学習させたら楽しそうです。
また本格的にやる場合はchainer側の知識も必要なのでゆくゆくはそちらも勉強してみようと思います。
アライドアーキテクツではエンジニアを募集しています。
興味のあるかたはこちらからご応募下さい!
サービス開発や社内インフラ等を担当しています。 このブログではJavaScript周りのネタを中心に書いていきたいです。