連載
» 2019年03月05日 05時00分 公開

Web解析にも使える! 強化学習超入門(2):【3目並べで学ぶ強化学習】Q-LearningとDQNを徹底解説 (2/2)

[蓑田和麻,リクルートテクノロジーズ]
前のページへ 1|2       

ChainerRLの環境構築の仕方と基本的な使い方

 ChainerRLは、Pythonから利用します。2019年2月現在、Pythonのバージョンは2.7以上、もしくは3.5.1以上で動作が保障されています。今回はPython3.6.5を使います。Pythonの環境設定が完了したら、下記でChainerRLをインストールします。

pip install chainerrl==5.2.0

 これにより、他に必要な「NumPy」や「Chainer」などのライブラリを自動でインストールできます。これだけで準備は完了です。

 では、DQNを実装していきましょう。まずはDeep Learningのネットワーク構成は、今回は「中間層2層」「レイヤー数は81」「活性化関数を“leaky_relu”」としています。ここはタスクによってさまざまで、ゲームの場合は画像を入力としたCNN(Convolutional Neural Network)を使用することもあります。

dqn.py
import chainer
import chainer.functions as F
import chainer.links as L
import chainerrl
class QFunctions(chainer.Chain):
   def __init__(self, obs_size, n_actions, n_hidden_channels=81):
       super().__init__(
           l0=L.Linear(obs_size, n_hidden_channels),
           l1=L.Linear(n_hidden_channels,n_hidden_channels),
           l2=L.Linear(n_hidden_channels,n_hidden_channels),
           l3=L.Linear(n_hidden_channels,n_actions)
       )
   def __call__(self, x, test=False):
       h = F.leaky_relu(self.l0(x))
       h = F.leaky_relu(self.l1(h))
       h = F.leaky_relu(self.l2(h))
       return chainerrl.action_value.DiscreteActionValue(self.l3(h))

 18行目の「chainerrl.action_value.DiscreteActionValue」でQ値の推定値を出力できます。

 次に、強化学習をするに当たって、エージェント(学習するオブジェクト自身)を作成します。

q_func = QFunctions(obs_size,n_actions)
optimizer = chainer.optimizers.Adam(eps=1e-2)
optimizer.setup(q_func)
# 報酬の割引率
gamma = 0.95
# epsilon-greedyを使ってたまに冒険
explorer = chainerrl.explorers.LinearDecayEpsilonGreedy(
   start_epsilon=1.0, end_epsilon=0.3, decay_steps=50000, random_action_func=ra.random_action
)
replay_buffer = chainerrl.replay_buffer.ReplayBuffer(capacity=10**6)
agent = chainerrl.agents.DQN(
   q_func, optimizer, replay_buffer, gamma, explorer, replay_start_size=500, update_interval=1,
   target_update_interval=100
)

 「dqn.py」で作成した「QFunctions」などでエージェントを作成する際に必要な変数を定義した後、11行目の「chainerrl.agents.DQN」でエージェントを定義できます。

 5行目で定義した「gamma」は割引率と呼ばれ、報酬につながったがあまりにも報酬から遠い行動を少し低めの価値に設定するためのパラメーターです。また7行目で定義した「explorer」とは探索に関する方針を定義するものです。今回、最初の方はデータがなく学習できないため、ランダムに行動する探索の割合を高く設定しています。ランダムの行動をする内に良い行動、悪い行動を学んでいくので、徐々にランダムの行動の割合を減らし、Q値を活用した行動を多くする方針にしています。

 さらに10行目で定義した「replay_buffer」は、今までの状態と行動をためておいて、Q値を更新して使う際に、ここからサンプリングして使用するもので、DQNの学習を高い精度で行うための工夫です。

DQNを使って3目並べを学習

 これら用いて実際に3目並べを学習していきます。下記は今回の3目並べの盤面やランダムに行動する関数を定義したものです。ここはやりたいことに合わせて適宜ルールを設定します。

train.py
from bord import Board
from randomAct import RandomActor
from dqn import QFunctions
import chainer
import chainerrl
import numpy as np
if __name__ == "__main__":
   b = Board()
   ra = RandomActor(b)

 今回両プレイヤーともコンピュータということで2つ目のエージェントを用意します。また、3目並べの場合は盤面が3×3なので、「obs_size」(2行目)を状態の、「n_actions」(4行目)を行動の次元数とし、両方とも9に設定しています。

   # 環境の次元数
   obs_size = 9
   # 行動の次元数
   n_actions = 9
   q_func = QFunctions(obs_size,n_actions)
   optimizer = chainer.optimizers.Adam(eps=1e-2)
   optimizer.setup(q_func)
   # 報酬の割引率
   gamma = 0.95
   # epsilon-greedyを使ってたまに冒険
   explorer = chainerrl.explorers.LinearDecayEpsilonGreedy(
       start_epsilon=1.0, end_epsilon=0.3, decay_steps=50000, random_action_func=ra.random_action
   )
   replay_buffer = chainerrl.replay_buffer.ReplayBuffer(capacity=10**6)
   # agentの生成
   agent_p1 = chainerrl.agents.DQN(
       q_func, optimizer, replay_buffer, gamma, explorer, replay_start_size=500, update_interval=1,
       target_update_interval=100
   )
   agent_p2 = chainerrl.agents.DQN(
       q_func, optimizer, replay_buffer, gamma, explorer,
       replay_start_size=500, update_interval=1,
       target_update_interval=100)

 ここから学習スタートです。今回は2万回学習(2万回試合)させます。今回は報酬として勝った場合「+1」、負けた場合「−1」と設計しました。そして、1ゲームが終わったら、「stop_episode_and_train」で報酬が与えられ(27行目)、それ以前に行った状態と行動のセットの価値推定に用いられます(29行目)。

  # 学習ゲーム回数
  n_episodes = 20000
  miss = 0
  win = 0
  draw = 0
  # 繰り返し実行
  for i in range(1, n_episodes+1):
       b.reset()
       reward = 0
       agents = [agent_p1,agent_p2]
       turn = np.random.choice([0,1])
       last_state = None
       while not b.done:
           action = agents[turn].act_and_train(b.board.copy(),reward)
           b.move(action,1)
           #ゲームが終わった場合
           if b.done == True:
               if b.winner == 1:
                   reward = 1
                   win += 1
               elif b.winner == 0:
                   draw += 1
               else:
                   reward = -1
               if b.missed is True:
                   miss += 1
               agents[turn].stop_episode_and_train(b.board.copy(), reward, True)
               if agents[1 if turn == 0 else 0].last_state is not None and b.missed is False:
                   agents[1 if turn == 0 else 0].stop_episode_and_train(last_state, reward*-1, True)
           else:
               last_state = b.board.copy()
               b.board = b.board * -1
               turn = 1 if turn == 0 else 0

 下記コマンドで学習が走ります。

python train.py
学習が走った結果

 最初は探索によるランダムでの行動が多く学習できていないので、ミスの回数(置けないところに置いてしまうこと)が多くなります。しかし、学習が進んでくると、「勝たないと報酬がもらえない」と学習するため、勝利するよう動き、ミスの回数が減っていきます。さらに先手/後手両方とも学習するため、学習回数が増えるにつれ引き分けの回数が増えていくことが観察できました。

 次に、3目並べを2万回試合させたエージェントと私が対戦したところ、私自身も最適解が分かっているので毎回引き分けになりました。故に強化学習がうまくいっていることが分かります。

まとめ

 連載第2回目では、強化学習アルゴリズムの一つであるQ-Learning、DQNについて解説しました。さらに3目並べを例にしてChainerRLを活用し、実際にDQNを実装してみました。

 第3回目では、これまで学んだ仕組みを利用して、リクルートのメインビジネスであるWebサービスへの適用について解説します。問題設定は3目並べのときと同様ですが、Webサービスの場合、ゲームと異なり「シミュレーションで学習できない」「状態の定義が曖昧になる」といった問題が起こります。それに対してどのように対処したかを解説していきます。

著者プロフィール

蓑田和麻(みのだかずま)

大学4年から修士2年まで統計学を専攻し、2016年リクルートホールディングスに新卒入社。リクルートテクノロジーズ配属後、データテクノロジーラボ部でスタッフ業務効率化のためのプロダクト開発や原稿校閲のプロダクトの導入、推進業務を行いながら、機械学習技術の研究開発に従事。趣味は麻雀と人狼。


前のページへ 1|2       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。