LoginSignup
37
36

More than 5 years have passed since last update.

Chainerで機械学習と戯れる: Jumpで穴を飛び越えろゲーム(自作)を強化学習できるか?

Last updated at Posted at 2015-07-19

はじめに

前回は 足し算ゲームをChainerを使って強化学習できるか? で単純な足し算ゲームの強化学習をやってみました。
一応基本はわかってきたつもりなので、ゲームっぽいものに挑戦したいと思います。

そこでゲーム環境を作り、その上でゲームを実装し、そのゲームをChainerにやらせるというアプローチをとることにしました。

ゲーム環境

  • イメージはASCII文字がシンボルで、画面の大きさが横40x縦24 のゲーム環境
  • 入力は「上下左右」と「Aボタン」「Bボタン」。それぞれ独立として、2^6=64パターンの入力がある。
  • ゲーム開発時用として、Terminalからのキー入力(ikjl + xz)でプレイできるデバッグモードがある。
    • でも、押しっぱなしにすると入力溜まってしまって辛い・・・ 同時押しできないから辛い・・・
  • 学習の様子を見るために、TCP/IPで通信して、直前のプレイの様子を表示する クライント・サーバがある。

という感じです。簡単に言うと、学習中にサーバに接続すれば、以下の様なものがTerminal上で見れるというものです。

AWS上などで実行しておいて、様子を見たい時にクライントを接続して眺めることができるので、暑い夏にPCをホッカホカにしたくない人向けです。
ちなみに ttygif などで上記のようなGif Animeも作れます(ttygifすごい!)。

お題: Jumpで穴を飛び越えろゲーム(自作)を強化学習できるか?

上記のGif Animeみたいなゲームです(足し算ゲームよりは進化した・・・)。

ルール

  • 左右のキーで左右に動く
  • AボタンでJumpする。高さ制限あり(3マスくらい)。Jump中でも左右に自由に動ける。
  • P がプレイヤーで 穴に落ちたらゲームオーバー (報酬 -1)
  • ターン毎に 報酬 +0.05
  • 自分の真下が穴の場合 報酬 +0.1

学習用に設けたルール

  • 無効なキーを押したら 報酬 -0.1
    • 例: 上や下、 Bボタン。 左右を同時に押す、等。
    • 一応、何が有効なキーか、くらいは教えてあげてもいいかな、と(収束しないし・・・)。
    • こうしておくと、暫くしたらちゃんと押さなくなりますw
  • e-Greedyで選択するActionは、上記無効なキーを含まないように調整
    • つまり、それは事前知識として知っている、ということになります(それくらいいいだろう・・・?)。

実験

学習モデル

DQNっぽく、 入力が画面の表示データのみ(40*24のでchar codeが 32〜127 まで)。前3フレームと現在のフレームの4フレーム分を入力とします。

ネットワーク構造は何が良いのか全くわからないので、こんな感じになっています。

HISTORY_SIZE = 4
# Convolution2DのOutputサイズは (screen_size - ksize) / stride + 1 となる(たぶん)。
chainer_model = FunctionSet(
    l1=F.Convolution2D(HISTORY_SIZE, 3, ksize=1, stride=1),  # 履歴を3つのW*Hのチャネルにする
    l2=F.Linear(JumpGame.WIDTH*JumpGame.HEIGHT*3, 800),      # ので、ここのINは W*H*3になる
    l3=F.Linear(800, 500),
    l4=F.Linear(500, 300),
    l5=F.Linear(300, 64),
)

Ugo-Nama さんの
DQNをPython + Chainerで書いたよ(コード公開しました)
も参考にさせてもらい、過去の4フレームを F.Convolution2D でまとめたところまでは良いんじゃないかなと思っていますが、その後3channelで良いのかとか、中間層の構成はこれで良いのかとか、まだ完全に手探りです。中間層ちょっと多いような気がするなぁ(ピクセルの処理みたいなのが一切無いわけだし・・)。

実装

実験に使ったソースコードはこちらになります。

今回からは、サーバ機能などもありましたので、機能毎に分割して作りました。

今後ゲームを追加するときは、 jump_game.py の部分だけ作れば良いようになっている、、、つもりです。

ゲームオーバーになる度に、学習状態を保存しているので、途中でBreakしても再開すれば前のModelを自動的にLoadします。ただ、Modelの構造を変更するとLoadでエラーになるので、その時は手動で ~/.game/model/ の下にあるファイルを消すような仕様。。

過去のプレイ結果を取っておいて、バックグラウンドでバッチ学習する仕組み(Experience Replay?)もおいおい作っていきたいと思います。

結果

まだよい結果は出てないです。
単に時間がかかるのか、やり方が間違っているのか。。
非常に単純な足し算ゲームでも最適な行動を学習するまでには100万回以上(学習回数でいうと5000万回以上だった気もする・・)かかったしなぁ。。
前回のような厳密な最適行動は不要だとは思いますが、穴に落ちなくなるようになってくれないかなぁと思っています。

結果が出るまでじっと待つ必要があるというのは、何か本当に生物の実験をしているようですねw

追記:12時間後

初期の頃とモデルを変えて12時間後くらいには上記のようになっていました。
少し賢くなってますねー。

HISTORY_SIZE = 4
PATTERN_SIZE = 100

def calc_output_size(screen_size, ksize, stride):
    return (screen_size - ksize) / stride + 1

nw = calc_output_size(JumpGame.WIDTH, 8, 4)   # 9
nh = calc_output_size(JumpGame.HEIGHT, 8, 4)  # 5
chainer_model = FunctionSet(
    l1=F.Convolution2D(HISTORY_SIZE, PATTERN_SIZE, ksize=8, stride=4),
    l2=F.Linear(nw * nh * PATTERN_SIZE, 800),
    l3=F.Linear(800, 400),
    l4=F.Linear(400, 64),
)

F.Convolution2D(4, 3, ksize=1, stride=1)
から
F.Convolution2D(4, 100, ksize=8, stride=4)
にしています。

さいごに

次はもう少し違うゲームを作ってみようと思います。
Experience Replayはその後くらいにやってみようかな。

次: Chainerで機械学習と戯れる: JumpゲームとTreasureゲームをChainerで強化学習した過程と結果のメモ

37
36
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
36