OpenAI Gymで強化学習!
ベルマン方程式
前回の続きです。 OpenAI GymのFrozenLake-v0を攻略して行きます。 Qテーブルを更新するのにベルマン方程式を使うので、 まずはベルマン方程式についてお話しします。
Q(s,a) = r + γ(max(Q(s',a')))
Q:行動価値関数 s:state a,:action
r:報酬(reward) γ:割引率
さて、数式は上記のように書きます。 どういうことかというと、 ゲームのある時点でQ値は次に行うアクションで得られる値の最大値を使います。 この時に得られる値には割引率をかけて、 古いゲームプレイの報酬の影響を減らしています。
ゲームのキー入力に相当するアクションをどう選ぶかがポイントになって来るのですが、 今回はε-グリーディー法という、何度かプレイしてうまくいったアクションを選択し、時々ランダムでアクションする手法を使います。
では早速、Qテーブルを更新していくプログラムを実装しましょう。
実装
#Qテーブルを更新するコード import gym import numpy as np env = gym.make("FrozenLake-v0") #Qテーブルの初期化 Q = np.zeros([env.observation_space.n, env.action_space.n])
ここまででQテープルの準備はできました。 今Qテーブルの中は全部0が入っているはずです。
array([[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]])
Qテーブルを初期化したのでベルマン方程式のパラメータを今度は設定しましょう。
#パラメータを更新するための値 lr = 0.8 #割引率 y = 0.95 #何回ゲームをプレイするか num_episodes = 2000 #得られる報酬の一覧を格納するリスト rList = []
ここまでできたらあとはゲームをプレイさせながらQテーブルを更新するだけです。 一気に行きます。
for i in range(num_episodes): #ステートの初期化 s = env.reset() #報酬の累積和の初期化 rAll = 0 #穴に落ちたか d = False #スタートからゴールか穴に落ちるまで何回アクションするか j = 0 while j < 100: j += 1 #ε-グリーディー法(結果が良かった方法を取り出す) a = np.argmax(Q[s,:] + np.random.randn(1, env.action_space.n)*(1/(i+1))) #アクションに対して新しいステータスと報酬と穴に落ちてるか更新する s1, r, d, _ = env.step(a) #ベルマン方程式を使って更新する Q[s,a] = Q[s,a] + lr * (r + y * np.max(Q[s1,:]) - Q[s,a]) #新しい報酬の総和を更新する rAll += r #新しいステータスに更新する s = s1 #まだ穴に落ちていなければ次の計算を始める if d == True: break #最終的に得られた報酬を報酬のリストに入れる rList.append(rAll)
rListの中には1と0が格納されています。 学習する過程で1の時はスタートからゴールまでたどり着いた時を表していて、 0の時は途中で穴に落ちたかゴールにたどり着けなくてゲームオーバーになったことを表しています。
とりあえず、QテーブルをアップデートしながらゲームをAIにプレイさせたわけですが、 2000回今回はプレイさせましたが、どの程度ゲームクリアできたか見て見ます。
print("回数ごとの結果:" + str(sum(rList)/num_episodes))
回数ごとの結果:0.4325 だいたい43%くらいクリアできてますね。 へたっぴ状態から練習させたからこんなものでしょう。
せっかくなので完成したQテーブルを見て見ます。
#最終的なQテーブルの値 print(Q)
[[3.30403805e-03 1.50916310e-01 4.67828897e-03 4.22277711e-03] [2.87994417e-04 2.36482442e-04 1.14266941e-04 5.24967471e-02] [7.66652742e-04 1.22894047e-03 9.88034171e-04 1.06186835e-02] [1.92752453e-04 6.67114284e-04 3.10096833e-04 4.74819685e-03] [1.70613675e-01 4.20748780e-03 9.26752005e-04 1.33450131e-04] [0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] [4.08413509e-03 4.59097357e-05 5.87518554e-05 3.83861167e-05] [0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] [3.02812052e-03 0.00000000e+00 3.16699706e-03 4.82855578e-01] [4.83901672e-03 7.84723753e-01 0.00000000e+00 0.00000000e+00] [6.29977810e-01 1.39727419e-03 0.00000000e+00 0.00000000e+00] [0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] [0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00] [0.00000000e+00 1.41025980e-03 9.22835605e-01 3.71121000e-04] [0.00000000e+00 9.98525704e-01 0.00000000e+00 0.00000000e+00] [0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00]]
各列は上下左右のキー入力だったのは覚えていますよね。 各行は今いるゲームのフィールド位置ですよ。 値が大きいほどそのコマンドを入力した時に効果的だったことを表しています。
じゃあ、実際にこのQテーブルを使ってゲームさせてみましょう。
#学習したQテーブルを使ってゲームをプレイする s = env.reset() d = False k = 0 while k < 100: k += 1 a = np.argmax(Q[s,:]) s1, r, d, _ = env.step(a) s = s1 env.render() if d == True: break
ゴールにたどり着けてますね。 AIはちゃんと玄人プレイヤーになってくれたようです。
今のQテーブルで100回プレイしたら何回クリアできるかみてみましょう。
#100回プレイして何回ゲームクリアできるか clear_num = 0 for _ in range(100): s = env.reset() d = False k = 0 while k < 100: k += 1 a = np.argmax(Q[s,:]) s1, r, d, _ = env.step(a) s = s1 if s == 15: clear_num += 1 if d == True: break print("100回ゲームをプレイして{}回ゴールまでたどり着けた!".format(clear_num))
100回ゲームをプレイして52回ゴールまでたどり着けた!
この結果は実行するたびに変化しますが50%はゲームがクリアできる子にAIは育ってくれたようです。 Qラーニングについてはここで一つの区切りとします。
次回はQ Networkについて書いてみたいと思います。