スポンサーリンク

Open AI GymのFrozenLakeを深層強化学習(DeepQ-Network)で解いてみた

Chainer Python ディープラーニング 強化学習
Open AI GymのFrozenLakeを強化学習(Q学習)で解いてみた
Google子会社DeepMind社により開発された、人工知能を搭載したコンピューター囲碁プログラム「AlphaGo(アルファ碁)」の活躍により、深層学習や強化学習の注目度がさらに上がった気がします。

前回、強化学習で解いた問題を、今回は深層強化学習(Deep Q-Network、以降DQN)で解いてみます。

DQNを使うほどの問題ではないのですが、実装の勉強のため、簡単な問題で解いてみました。

Deep Q-Network

前回も少し触れましたが、DQNは、Q学習の行動価値関数 Q(s, a) の近似関数を深層学習で求める手法です。

状態 s_t から行動 a_t を選択して、報酬 r_t を受け取って状態 s_{t+1} に遷移したとします。

この時にニューラルネットワーク Q に状態 s_t を入力した時に出力される行動ベクトル aa_t に当たる確率を下記で更新します。

(DeepMind社の論文を読んでいて、ここが一番理解に苦労しました...)

    \[{\displaystyle}y_t = Q(s_t)\]

    \[{\displaystyle}y_t[a_t] \leftarrow r_t+{\max}Q(s_t)\]

また、学習に使う「状態・行動・報酬」のセットは、互いに相関のないものを使った方が収束が早くなるそうです。

Q学習では、行動をさせるたびに「状態・行動・報酬」を使ってQ関数を更新していましたが、DQNでは「状態・行動・報酬」を一旦保存しておいて、そこからランダムにミニバッチ分取り出し、確率勾配降下法でニューラルネットワークの重みを更新していくようです。

FrozenLakeをDeep Q-Networkで解いてみる

Open AI GymやFrozen Lakeの環境の説明については、前回記事と同様なので省略します。

ニューラルネットワークの実装には、下記らの記事と同様、フレームワークのChainerを使いました。

Chainerでニューラルネットワーク、RNN、CNNを実装してみた
RNNの実装の勉強もしました。 今回は整理と備忘録も込めて、Chainerでニューラルネットワーク、リカレントニューラルネットワーク、畳み込みニューラルネットワークの実装について記します。
Chainerでニューラルネットワーク及び畳み込みニューラルネットワークを実装してみた
今回、Chainerを触ってみまして、MNISTの画像認識アルゴリズムを、ニューラルネットワークと畳み込みニューラルネットワークで実装してみました。 ちなみに、他にもTensorFlowも使ってみたのですが、自分としてはひとまずChainerの方がコード感覚としてしっくりきましたので、こちらを使ってみました。

実装が下記になります。

GitHub: https://github.com/Gin04gh/open_ai_gym/blob/master/DQN_NN_FrozenLake.ipynb

上記、移設しました。

GitHub: https://github.com/Gin04gh/datascience/blob/master/open_ai/dqn_nn_frozenlake.ipynb

import copy, sys
import numpy as np
%matplotlib inline
import matplotlib.pylab as plt
import gym
import chainer
import chainer.links as L
import chainer.functions as F
from chainer import Chain, optimizers, Variable, serializers

class Neuralnet(Chain):
    def __init__(self, n_in, n_out):
        super(Neuralnet, self).__init__(
            l1 = L.Linear(n_in, 100),
            l2 = L.Linear(100, 100),
            l3 = L.Linear(100, 100),
            q_value = L.Linear(100, n_out, initialW=np.zeros((n_out, 100), dtype=np.float32))
        )

    def Q_func(self, x):
        h = F.leaky_relu(self.l1(x))
        h = F.leaky_relu(self.l2(h))
        h = F.leaky_relu(self.l3(h))
        h = self.q_value(h)
        return h

def main():
    sys.setrecursionlimit(10000)

    env = gym.make("FrozenLake-v0")

    n_obs = env.observation_space.n
    n_act = env.action_space.n

    q = Neuralnet(n_obs, n_act)
    target_q = copy.deepcopy(q)

    optimizer = optimizers.Adam()
    optimizer.setup(q)

    loss = 0
    total_step = 0
    gamma = 0.99
    memory = []
    memory_size = 1000
    batch_size = 100
    epsilon = 1
    epsilon_decrease = 0.005
    epsilon_min = 0
    start_reduce_epsilon = 1000
    train_freq = 10
    update_target_q_freq = 20
    n_epoch = 1000
    n_max_steps = 200
    last_rewards = np.zeros(n_epoch)

    for epoch in range(n_epoch):
        pobs = env.reset()
        pobs = np.identity(n_obs, dtype=np.float32)[pobs,:].reshape((1,n_obs))
        done = False

        for step in range(n_max_steps):
            # select action by epsilon-greedy
            pact = env.action_space.sample()
            if np.random.rand() > epsilon:
                a = q.Q_func(Variable(pobs))
                pact = np.argmax(a.data[0])
            # step
            obs, reward, done, _ = env.step(pact)
            obs = np.identity(n_obs, dtype=np.float32)[obs,:].reshape((1,n_obs))

            # stock experience
            memory.append([pobs, pact, reward, obs, done])
            if len(memory) > memory_size:
                memory.pop(0)

            # train
            if len(memory) >= memory_size:
                if total_step % train_freq == 0:
                    # replay experience
                    np.random.shuffle(memory)
                    memory_idx = range(len(memory))

                    for idx in memory_idx[::batch_size]:
                        batch = memory[idx:idx+batch_size]
                        pobss, pacts, rewards, obss, dones = [], [], [], [], []

                        for b in batch:
                            pobss.append(b[0].tolist())
                            pacts.append(b[1])
                            rewards.append(b[2])
                            obss.append(b[3].tolist())
                            dones.append(b[4])

                        pobss = np.array(pobss, dtype=np.float32)
                        pacts = np.array(pacts, dtype=np.int8)
                        rewards = np.array(rewards, dtype=np.float32)
                        obss = np.array(obss, dtype=np.float32)
                        dones = np.array(dones, dtype=np.bool)

                        Q = q.Q_func(Variable(pobss))
                        tmp = target_q.Q_func(Variable(obss))
                        tmp = list(map(np.max, tmp.data))
                        max_Q_dash = np.asanyarray(tmp, dtype=np.float32)
                        target = np.asanyarray(copy.deepcopy(Q.data), dtype=np.float32)
                        for i in range(batch_size):
                            target[i, pacts[i]] = rewards[i]+gamma*max_Q_dash[i]*(not dones[i])
                        q.zerograds()
                        loss = F.mean_squared_error(Q, Variable(target))
                        loss.backward()
                        optimizer.update()

                if total_step % update_target_q_freq == 0:
                    target_q = copy.deepcopy(q)

            # reduce epsilon
            if epsilon > epsilon_min and total_step > start_reduce_epsilon:
                epsilon -= epsilon_decrease

            # update last reward
            last_rewards[epoch] = reward
            total_step += 1
            pobs = obs

            if done:
                break

        #print("\t".join(map(str,[epoch, epsilon, loss, reward, total_step])))

    rates = np.average(last_rewards.reshape([n_epoch//10, 10]), axis=1)
    plt.plot(rates)
    plt.savefig("result.png")

if __name__=="__main__":
    main()

無事、学習しているようです。

問題が離散だからまだ処理が出来ましたが、これが例えばDeepMind社のようにAtariのゲームとなって、状態をゲーム画面(画像)として入力する(畳み込みニューラルネットワーク)となると、いよいよマシンスペックの問題になってきます。

さて、どうしたものか...。

追記(2017-09-07)

上記はニューラルネットワークでしたが、Atariの論文ではCNNでしたので、同じ問題ですが、無理やりCNNにしてみて実装してみました。

下記がコードになります。

GitHub: https://github.com/Gin04gh/open_ai_gym/blob/master/DQN_CNN_FrozenLake.ipynb

上記移設しました。

GitHub: https://github.com/Gin04gh/datascience/blob/master/open_ai/ddqn_cnn_frozenlake.ipynb

問題なく学習していそうです。

追記(2017-10-15)

PyTorchによる実装になりますが、連続値のゲームに対してもDQNを実装してみました。

DQNからさらに改善された研究についても少し勉強してみました。

PyTorchで深層強化学習(DQN、DoubleDQN)を実装してみた
以前に勉強したDeep Q-Network(DQN)を、やっぱり離散的な状態を返す簡単なゲームでなく、連続的な状態のゲームにも適用してみたいと思い、久しぶりにまた勉強しました。 最近の深層強化学習の研究を見てみたところ、DQNからさらに進化していて、A3Cなるものまで登場していましたので、少しばかりそちらについても触れてみます。

コメント