KerasとCartPoleでDQN (Deep Q-Network)

つりながら学ぶ!深層強化学習 PyTorchによる実践プログラミング 良い

 マイナビ出版からPyTorchを使って深層強化学習を作りながら学ぶという本が出てて、発売日にすぐ買って、今日もまだ読んでる途中なんだけれど、いかんせんディープラーニング関係はKerasと時々生TensorFlowぐらいしか弄ってないから、PyTorchが今ひとつ分からない。

 そこで勉強ついでに、ネットの記事を調べつつ、本のソースコードを元にDQNをKerasを使って実装するってのが今日のお話。

使うライブラリ

  • OpenAi Gym
  • TensorFlow 1.9.0
  • Keras (tf.keras)

まず前提として、DQNの実装で重要なのは以下の通り。

DQN で重要な4つのポイント

  • Experience Replay
  • Fixed Target Q-Network
  • Reward Clipping
  • Huber Loss

Huber Loss は TensorFlowの損失関数の中に実装されているのでそれを使うことにします。

10回連続して棒を立たせられたら終了するコード。

# coding: utf-8

import random
import gym
import numpy as np
from collections import deque
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, InputLayer
from tensorflow.keras.optimizers import Adam
from tensorflow.losses import huber_loss

EPISODES = 500
ENV = "CartPole-v0"
GAMMA = 0.99
MAX_STEPS = 200
NUM_EPISODES = 500


CAPACITY = 10000
BATCH_SIZE = 32


class ExperienceMemory:
    def __init__(self, capacity):
        self.capacity = capacity
        self.memory = deque(maxlen=capacity)

    def push(self, state, action, next_state, reward):
        # if len(self.memory) < self.capacity:
        #    self.memory.append(None)

        self.memory.append((state, action, next_state, reward))

    def sample(self, batch_size):
        return random.sample(self.memory, batch_size)

    def __len__(self):
        return len(self.memory)


class Q_network:
    def __init__(self, num_state, num_action):
        self.memory = ExperienceMemory(CAPACITY)
        self.num_state, self.num_action = num_state, num_action

        self.model = Sequential()
        self.model.add(InputLayer(input_shape=(num_state, )))
        self.model.add(Dense(32, activation="relu"))
        self.model.add(Dense(32, activation="relu"))
        self.model.add(Dense(num_action, activation="linear"))
        self.optimizer = Adam(lr=0.00001)
        self.model.compile(loss=huber_loss, optimizer=self.optimizer)

    def replay(self, target_q_n):
        if len(self.memory) < BATCH_SIZE:
            return
        inputs = np.zeros((BATCH_SIZE, self.num_state))
        targets = np.zeros((BATCH_SIZE, self.num_action))
        transitions = self.memory.sample(BATCH_SIZE)

        for i, (state_batch, action_batch, next_state_batch, reward_batch) in enumerate(transitions):
            inputs[i:i+1] = state_batch
            target = reward_batch

            if not (next_state_batch == np.zeros(state_batch.shape)).all(axis=1):
                mainQ = self.model.predict(state_batch)[0]
                next_action = np.argmax(mainQ)
                target = reward_batch + GAMMA * target_q_n.model.predict(next_state_batch)[0][next_action]

            targets[i] = self.model.predict(state_batch)
            targets[i][action_batch] = target
            self.model.fit(inputs, targets, epochs=1, verbose=0)

    def decide_action(self, state, episode, target_q_n):
        epsilon = 0.5 * (1 / (episode + 1))

        if epsilon <= np.random.uniform(0, 1):
            action = np.argmax(target_q_n.model.predict(state)[0])
        else:
            action = np.random.choice(self.num_action)

        return action


class Agent:
    def __init__(self, num_states, num_actions):
        self.brain = Q_network(num_states, num_actions)
        self.target_q_n = Q_network(num_states, num_actions)

    def update_q_function(self):
        self.brain.replay(self.target_q_n)

    def get_action(self, state, episode):
        return self.brain.decide_action(state, episode, self.target_q_n)

    def memorize(self, state, action, state_next, reward):
        self.brain.memory.push(state, action, state_next, reward)


class Environment:
    def __init__(self):
        self.env = gym.make(ENV)
        self.num_states = self.env.observation_space.shape[0]
        self.num_actions = self.env.action_space.n

        self.agent = Agent(self.num_states, self.num_actions)

    def run(self):
        complete_episodes = 0
        episode_final = False

        for episode in range(NUM_EPISODES):
            observation = self.env.reset()
            state = observation
            state = np.reshape(state, [1, self.num_states])

            self.agent.target_q_n = self.agent.brain

            for step in range(MAX_STEPS):
                action = self.agent.get_action(state, episode)
                next_state, _, done, _ = self.env.step(action)
                next_state = np.reshape(next_state, [1, self.num_states])

                if done:
                    next_state = np.zeros(state.shape)
                    if step < 195:
                        reward = -1
                        complete_episodes = 0
                    else:
                        reward = 1
                        complete_episodes += 1
                else:
                    reward = 0

                self.agent.memorize(state, action, next_state, reward)
                self.agent.update_q_function()

                state = next_state

                if done:
                    print("{} Episode: Finished after {} steps: complete_episodes: {}".format(
                        episode, step+1, complete_episodes))
                    break

                if episode_final:
                    self.env.render()
                    break

                if complete_episodes >= 10:
                    print("10回成功")
                    episode_final = True


cartpole_env = Environment()
cartpole_env.run()

実行結果

f:id:Hiro2201:20180817235600g:plain
120エピソードくらい回して10回連続で立ち続けた後のやつ

 上手くいけば100 ~ 150ステップぐらい回せば終わるんだけれど、途中で成功回数が9回ぐらいまでガクッと落ちて、なかなか学習がうまくいかないことが多々あるので、学習が上手くいってないな と思ったら再度実行。  これがなぜなのかが分からない。乱数お祈りゲーなのかな??原因を探しつつDueling DQN書く。

おまけ

 自分で集めたデータを使ってKeras用のデータセットを簡単に作れるライブラリ作ったので、使って見てね github.com

インストール方法は、  pip install tartare

からかい上手の高木さん 2期はよ来い。

参考にした本

「つりながら学ぶ!深層強化学習 PyTorchによる実践プログラミング」は強化学習関係の本で一番簡単で分かりやすいと思うけど、O'Reillyの「実践Deep Learning」の強化学習のページと「速習 強化学習」を片手に調べるとさらに良いと思う。

 あと、「つくりながら(ry」はJupyter Notebookを使って進んでいくけど、個人的には .py形式で話を進めてくれたほうが、PyCharmみたいなIDEで調べながら学べて値の動きとかが分かれただろうからちょっと残念。あと、PyTorch慣れないから分かりにくい()。

つくりながら学ぶ! 深層強化学習 ~PyTorchによる実践プログラミング~

つくりながら学ぶ! 深層強化学習 ~PyTorchによる実践プログラミング~

実践 Deep Learning ―PythonとTensorFlowで学ぶ次世代の機械学習アルゴリズム (オライリー・ジャパン)

実践 Deep Learning ―PythonとTensorFlowで学ぶ次世代の機械学習アルゴリズム (オライリー・ジャパン)

速習 強化学習 ―基礎理論とアルゴリズム―

速習 強化学習 ―基礎理論とアルゴリズム―

  • 作者: Csaba Szepesvari,小山田創哲,前田新一,小山雅典,池田春之介,大渡勝己,芝慎太朗,関根嵩之,高山晃一,田中一樹,西村直樹,藤田康博,望月駿一
  • 出版社/メーカー: 共立出版
  • 発売日: 2017/09/21
  • メディア: 単行本
  • この商品を含むブログを見る