ゼロから創る chainerrl を使ったディープラーニングもどき

注:今回の記事は完全にプログラマ向けの解説記事です

ソースコードの閲覧、ダウンロードは此方からどうぞ
GitHub - qhapaq-49/chainerrl_test: chainerrlを使ったスタンドアロンな強化学習のサンプルです

【前回の記事(tensorflow版)】
ゼロから創る tensorflow + reinforcement learningを使ったディープラーニングもどき - qhapaq’s diary

【今回の記事と合わせてオススメしたい記事】
ChainerRLで三目並べを深層強化学習(Double DQN)してみた - Qiita
# 正直、本稿よりも此方の記事のほうが良く出来ています。

【今回作りたいもの】
前回に引き続き、今度はchainerrlでニューラルネット+強化学習を組んでみました。
今回も200行程度で動くスタンドアロンな強化学習のサンプルを公開、紹介していきます。ゲームAI開発者の必須スキル(?)である
ディープラーニング+強化学習の雰囲気と簡単な実装を本稿で感じていただければ幸いです。

強化学習の詳しい導入や解説は前回の記事をご覧ください。

【本稿で扱うゲームのルール(再掲載)】
本稿ではニューラルネットで動く競りゲームのAIを作ります。競りゲームとは

・初期所持金10のプレイヤー2人が4回の競りを行う
・各競りでは1〜4の勝利点がランダムで出てくる。同じ勝利点は二度出ない。
・競りで買値を宣言できるのは一度だけ。高値を宣言した側が勝利点を得られる。引き分けの場合の再試合はなし
・4回の競りの後で勝利点が高いほうが勝利

というゲームです。私が風呂に入りながら勝手に考えたゲームです。

【使い方】

step.0 学習/対戦のモード選び

# 学習 or 対戦
Learning = False

の部分を編集して学習させるか対戦させるかを決めます

step.1 読み込むモデルを決める

# 学習させる場合の初期値(になってるか怪しい
sagent.loadAgent("")

のダブルクオテーションの中に所定のモデルファイル(初期状態では無いはずなので空文字にしてください)
を書き入れることで、エージェントの初期値を決定します。


step.2 対戦相手のモデルを決める

env = seriEnv()
# 学習させる場合の対戦相手
env.loadEnemy("random")

のダブルクオテーションの中に所定のモデルファイル(初期状態では空文字かrandom(全ての手をランダムにする)を選んでください)
を書き入れることで、対戦相手の初期値を決定します。以下の学習(対戦)ではこのモデル相手の勝率を最適化(測定)します


step.3 学習の場合、保存するモデル名を決める

if Learning == True:
    # この名前で保存する
sagent.agent.save('model2')

のクオテーションの中に学習結果を保存する際の名前を書き入れてください。

以上の準備を済ませたら

python3 serichainer.py

とでもすれば動きます(本コードはpython3での動作は確認してますが、他の環境での動作は解りません)


【chainerで強化学習するときにハマりやすいポイント】

ChainerRLで三目並べを深層強化学習(Double DQN)してみた - Qiita
を合わせて読むことを強くオススメします。

PFNのサイトに17/08/22時点に書かれていない大事なポイント(かつ最もハマり易いポイント)はexplolerの独自設定です。サイトではお約束のgymの流用がなされており、最低限どんな機能を含んでいればよいのかがハッキリしません。が、必要な実装自体はシンプルです。以下、公開したコードより抜粋

# step1 ランダムムーブ用のクラスを創る
# explorer用のランダム関数オブジェクト
class RandomActor:
    def __init__(self):
        pass
    def random_action_func(self):
        # 所持金を最大値にしたランダムを返すだけ
        return random.randint(0,10)

# 本当はrandom_action_func(self, money)と所持金を引数にしたかった
ra = RandomActor()


# step2 メンバ関数を引数としたexplolerを定義する
self.explorer = chainerrl.explorers.ConstantEpsilonGreedy(epsilon=0.05, random_action_func = ra.random_action_func)

# step3 dqnagentにぶち込む
# explolerの自作以外はだいたいコピペでも動く。パラメタや設定がベストか否かは...知らぬ
self.agent = chainerrl.agents.DoubleDQN(self.q_func, self.optimizer, self.replay_buffer, self.gamma, self.explorer,replay_start_size=500, phi=self.phi)

このとき、random_action_funcに引数がつくと、どうもエラーが出て動かないようです。また、紹介したqiitaのコードはdoubledqnの定義の際に不要な引数がついているため、そのままだと動きません。
# コード中の update_frequency=1, target_update_frequency=100 を消さねばならぬ。
この辺の処理をしないとうまく動かない理由はいまいちわからないのですが、取り敢えずtensorflowよりも短い行数で曲がりなりにも学習するサンプルを創ることが出来ました。

【あとがき:なんでこんなものを態々公開したか】
chainerも布教されるべきだと思ったからです。三目並べを見つけた時には「私の記事いらなくね」と思いましたが、此方は不完全情報ゲームの学習機だったり、三目並べの補足情報もあったほうが良い(事実、三目並べのコピペだけでは機能しなかったわけですし)するので存在意義は見いだせたかなと思います。

チュートリアルよりも高度な実装をtensorflow, chainerと有名なDNNパッケージで作り比べた感想は「tensorflowよりchainerの方が楽とか、kerasの方がもっと楽とか言うけど、実装で最も工数を喰うのはチュートリアルをはみ出た、解説記事を見つけ難い状態での謎解きであり、中級者的な使いやすさは教えを請える相手が何を使っているか(教えを請う人が居ないならどれだけまともなデバッグログを吐くか)でほぼ決まる」というものです。三目並べの記事のお陰でchainerはtensorflowに比べ地力で書くコードの数は少なかったですが、引数やらなんやらの問題を自力で解読した結果、tensorflowと使った工数には大差はありませんでした。

chainerはtensorflowに比べればコミュニティーが小さいですが、chainerにしか出来ないことも沢山あります。tensorflowに慣れ親しんだ人も、chainerの練習はしておくと将来的に得なのでは....ないでしょうか。