Q-NetworkでOpenAI Gymに挑戦!
Q-Networkに挑戦してみる
強化学習のQ-NetworkでOpenAI Gymのフローズンレイクに挑戦します。 目標は前回までのQラーニングよりさらにゲームが上手いAIを作ることです。
Q-Networkについて簡単に説明しておきます。
ステートと重みをかけ合わせてQ値を求めます。 Qテーブルの代わりに重みを用いることで、 メモリの節約や計算の高速化ができます。
Q値の目標値と推定値の二乗和誤差を最小にする重みを計算する手法です。
損失関数は下の通りです。
Loss = Σ(Q_target - Q)2
ひとまずコーディングしてみます。 Pythonでサクサク書いていきましょう。
import gym import numpy as np import random import tensorflow as tf env = gym.make("FrozenLake-v0") #tensorflowの初期化 tf.reset_default_graph #変数を設定する inputs1 = tf.placeholder(shape=[1,16], dtype=tf.float32) #重みを正規分布で用意する。分散は0.01で設定 W = tf.Variable(tf.random_uniform([16,4],0,0.01)) #かけ算を行う Qout = tf.matmul(inputs1, W) #Q値の推定値を最大にする値を一つ取り出して予測値に代入する predict = tf.argmax(Qout, 1) #Q値の推定値を入れる変数 nextQ = tf.placeholder(shape=[1,4], dtype=tf.float32) #損失関数 loss = tf.reduce_sum(tf.square(nextQ - Qout)) #勾配降下法。パラメータを更新する頻度は0.1に設定する trainer = tf.train.GradientDescentOptimizer(learning_rate=0.1) #2乗誤差を最小にするように計算する updateModel = trainer.minimize(loss)
ここまでをまず説明します。 input1は1×16のベクトルです。 15個の0と1個の1が格納されています。 ゲームのフィールドのどこにいるかの情報を入れておく変数です。
Wは16×4のベクトルでinputs1と掛け算できる形に作ります。 こいつが重みです。
inputs1とWをかけ合わせるとQ値が1×4のベクトルで得られます。 このベクトルの中身で最大の値をとるindexを1の値にして取り出します。
選んだQ値のアクション(上下左右の入力)を行い、 ステータス、報酬、穴に落ちてないか、次の報酬を予測して、 アクションした後のQ値と比較します。
今選んだQ値とその結果得られるQ値の差が最小になる重みWを求めていくことでAIが賢くなっていきます。
では、学習させるコードを一気に書いていきましょう。
#トレーニングをする。まずは初期化する。 init = tf.global_variables_initializer() #学習用のパラメータ #割引率 y = 0.99 #選択するアクションを決めるパラメータ e = 0.01 #ゲームをプレイする回数 num_episodes = 20000 #トータルの報酬を格納する rList = [] #ネットワークの学習を進めるためのコード with tf.Session() as sess: sess.run(init) #学習させる回数 for i in range(num_episodes): #変数をリセットする s = env.reset() rAll = 0 d = False #何回アクションするか j = 0 while j < 100: j += 1 #これまでのQ値から次のアクションを決める a, allQ = sess.run([predict, Qout], feed_dict = {inputs1:np.identity(16)[s:s+1]}) # ε-グリーティー法、10%以下の確率でランダムにアクションをする if np.random.rand(1) < e: a[0] = env.action_space.sample() s1, r, d, _ = env.step(a[0]) Q1 = sess.run(Qout, feed_dict = {inputs1:np.identity(16)[s1:s1+1]}) maxQ1 = np.max(Q1) targetQ = allQ #ベルマン方程式 targetQ[0, a[0]] = r + y * maxQ1 _, W1 = sess.run([updateModel,W], feed_dict={inputs1:np.identity(16)[s:s+1], nextQ:targetQ}) rAll += r s = s1 #穴に落ちたら閾値を更新して次のエピソードに移る if d == True: e = 1/((i/50)+10) break rList.append(rAll) print("全ゲーム中、どれくらいゴールできたか:" + str(sum(rList)/num_episodes*100) + "%")
全ゲーム中、どれくらいゴールできたか:67.50500000000001%
前回よりかなり賢くなってますね。
ゴールもちゃんとできています。 ゴールするまでにかかる手数も短くなっていて、 Qラーニングより今回挑戦したQ-Networkの方がゲーム上手なAIを作ることができました!