強化学習
Keras
OpenAIGym
関数近似

KerasでOpenAI GymのFrozenLake8x8-v0を解きたかった...

FrozenLake-v0は盤面サイズが4x4でしたが,こちらは8x8.
https://gym.openai.com/envs/FrozenLake8x8-v0

4x4での解法はこちらに記載しています.
http://qiita.com/namakemono/items/989f94fe788532a335f2

盤面が大きくなるので,テーブルで持つことは難しく関数近似が必要になります.

クリア条件

  • 直近100回の試行の報酬の平均値が0.99以上.
  • 報酬はクリアできたら+1,落とし穴に落ちたら0とする.

考察

今回のマップは

S.......
........
...H....
.....H..
...H....
.HH...H.
.H..H.H.
...H...G
  • S: ゲーム開始地点
  • G: クリア地点(移動できれば成功)
  • H: 落とし穴(落ちると失敗)
  • F: 移動できる場所(Fだと見づらいので, "."で表示しています.)

  • 開始地点(S)から右に行って,下に行くのが正解となる.

  • 2/3の確率で意図した方向とは異なる方向(直角)に進む(下記リンク参照)ので,最初0.99は無理でしょと思ったが,最後の2マスは右押して移動することで,100%クリアは可能ということに気づく.
    https://github.com/openai/gym/blob/master/gym/envs/toy_text/frozen_lake.py#L99

  • 左下からの1/3の確率で意図した方向と違う方向に進むために,安定してゴールすることはできない.(つまり,こちらのコースは引っ掛けであることをコンピュータは学習する必要がある)

コード

# -*- coding: utf-8 -*-
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
import numpy as np
import gym
from gym import wrappers
from keras.models import Sequential
from keras.layers import Dense

def build_model(input_dim, output_dim):
    model = Sequential()
    model.add(Dense(input_dim, kernel_initializer="he_normal", activation="relu", input_shape=(input_dim, )))
    model.add(Dense(output_dim, kernel_initializer="he_normal", activation="softmax"))
    return model

def run():
    env = gym.make("FrozenLake8x8-v0")
    env = wrappers.Monitor(env, directory="/tmp/frozenlake8x8-v0", force=True)
    logger.info("Observation Space: %d, Action Space: %d" % (env.observation_space.n, env.action_space.n))
    max_episode = 100000
    Gs = [] # Revenues
    best = -1
    model = build_model(env.observation_space.n, env.action_space.n)
    model.compile(loss="categorical_crossentropy", optimizer="nadam", metrics=["mae", "mse"])
    for episode in range(max_episode):
        x = env.reset()
        X = [] # States
        A = [] # Actions
        Q = [] # Action Values
        done = False
        alpha, gamma = 0.03, 0.9
        while not done:
            v = np.zeros(env.observation_space.n)
            v[x] = 1
            q = model.predict(np.asarray([v]))[0]
            a = np.argmax(q)
            X.append(v)
            A.append(a)
            Q.append(q)
            x, r, done, info = env.step(a)
            if done:
                r = +1 if r > 0 else -1
                break
        T = len(X)
        for t in range(T-1):
            a = A[t]
            Q[t][a] = (1-alpha) * Q[t][a] + alpha * r
        model.fit(np.asarray(X), np.asarray(Q), verbose=0, batch_size=T)
        Gs.append(int(r > 0))
        avg = np.mean(Gs[-100:])
        best = max(best, avg)
        logger.info("Episode: %d, End of turn: %d, Revenue: %.2f, Average: %.2f, Best: %.2f" % (episode, len(X), r, avg, best))

if __name__ == "__main__":
    run()

スコア

  • 0.96±0.02