【深層強化学習、Deep Q Learning】keras-rlでDouble-DQNを試す

今回は、keras-rlを使ってDouble-DQNを試します。
Double-DQNは、DQNの過大評価という課題の緩和のために開発された手法です。

ちなみに私の環境は

  • ubuntu 18.04
  • python 3.6.0

です。

DDQNについて

Double-DQNでは、
行動の過大評価を緩和するために提案された手法です。

ここでは、簡単に説明します。
詳細は参考文献を参照していただければと思います。

DQNでは、ある\(Q(s_t,a_t)\)を更新する際に、
遷移先の最大行動価値\(Q(s_{t+1}, argmax_{a’} Q(s_{t+1},a’ |\theta^{‘}_t))\)を用います。
以下はDQNで用いるTD誤差になります。

$$
\begin{eqnarray}
\newcommand{\argmax}{\mathop{\rm arg~max}\limits} TD &=& r_{t+1} + \gamma Q(s_{t+1}, a | \theta^{‘}_t) – Q(s_{t}, a_t | \theta_t) \\
a &=& \argmax_{a’} Q(s_{t+1},a’ |\theta^{‘}_t)
\end{eqnarray}
$$

ここで、\(\theta_t\) はQ Networkのパラメータ、
\(\theta^{‘}_t\) はTarget Networkのパラメータを表しています。

各状態の最大行動価値はその他すべての行動価値に少なからず影響を与えます。
その最大行動価値がたまたま良かった場合(過大評価)、
その評価はすべての状態行動価値に及びます。

上述した過大評価を解消するために、
単純に遷移先の状態行動価値を用いるのではなく、
もう1つのQ Networkを用意して、その出力を用いて更新します。

Double-DQNのTD誤差は以下のように計算されます。

$$
\begin{eqnarray}
\newcommand{\argmax}{\mathop{\rm arg~max}\limits} TD &=& r_{t+1} + \gamma Q(s_{t+1}, a | \theta^{‘}_t) – Q(s_{t}, a_t | \theta_t) \\
\color{black}{a} &=& \argmax_{a’} Q(s_{t+1},a’ |\color{red}{ \theta_t})
\end{eqnarray}
$$

赤い部分が通常のDQNと異なる点です。
Target Networkによる過剰評価を防ぐために、
Q Networkが用いられます。
Target Networkの最大行動価値を単純に用いるのではなく、
Q Networkにおいての最大行動価値を得られる行動をTarget Networkでとったときの行動価値を用います。
ややこしいですが、Target Networkの過大評価をQ Networkを用いることで緩和しようとしています。

keras-rl ではTarget Networkを2つ目のQ Networkとして扱っています。

keras-rlのインストール

keras-rlを動かすために、
keras-rl以外にもいくつかインストールする必要があります。

pip install numpy
pip install gym
pip install tensorflow
pip install keras

keras-rlのインストール

pip install keras-rl

解く問題

台車の上にポールがあって、 そのポールが倒れないように、うまく台車を制御する問題です。

エージェントはステップ毎に状態(詳しくは参考サイトを参照)と報酬を受け取り、学習します。

以下のサイトのcartpoleについての説明がわかりやすいです。 http://qiita.com/namakemono/items/9b75d1c0c98916b396ba

ソースコード

keras-rl ではDQNAgent の引数enable_double_dqnをTrue変更するだけで、
Double-DQNにできます。

以下にサンプルコードを示します。
以下のコードはコピーしてそのまま実行できます。

import numpy as np
import gym

from keras.models import Sequential
from keras.layers import Dense, Activation, Flatten
from keras.optimizers import Adam

from rl.agents.dqn import DQNAgent
from rl.policy import BoltzmannQPolicy
from rl.memory import SequentialMemory

# 環境(タスク)の設定
ENV_NAME = 'CartPole-v0'
env = gym.make(ENV_NAME)
np.random.seed(123)
env.seed(123)
nb_actions = env.action_space.n

# モデルの作成
model = Sequential()
model.add(Flatten(input_shape=(1,) + env.observation_space.shape))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(16))
model.add(Activation('relu'))
model.add(Dense(nb_actions))
model.add(Activation('linear'))
print(model.summary())

# Experience Bufferの設定
memory = SequentialMemory(limit=50000, window_length=1)
# 行動選択手法の設定。ここでは、ボルツマン選択
policy = BoltzmannQPolicy()

# DDQNエージェントの設定
# DDQNはDQNAgentの引数を変更することによって設定できます。
# enable_double_dqnをTrueにすることでDDQNとなります
dqn = DQNAgent(model=model, nb_actions=nb_actions, memory=memory, nb_steps_warmup=10,
               target_model_update=1e-2, policy=policy, enable_double_dqn=True)
dqn.compile(Adam(lr=1e-3), metrics=['mae'])

# 学習
history = dqn.fit(env, nb_steps=50000, visualize=False, verbose=2)
history = np.array(history.history)
np.save("history_dqn.npy", history)

# 学習曲線のプロット
plt.plot(np.arange(len(history["episode_reward"])),history["episode_reward"],label="ddqn")
plt.show()

# 学習したモデルのパラメータの保存
dqn.save_weights('ddqn_{}_weights.h5f'.format(ENV_NAME), overwrite=True)

# 学習したモデルの読み込み
# agent.load_weights('ddpg_{}_weights.h5f'.format(ENV_NAME))

# テスト
dqn.test(env, nb_episodes=5, visualize=False)

結果の出力

上述したコードを実行すると、 下記のような学習曲線をプロットします。

f:id:ttt242242:20190423191539p:plain

横軸はエピソード、縦軸が報酬になります。
エピソードが進むにつれて得られる報酬が高くなっていきます。

DQNと比較したかったのですが、
過大評価してしまう例をすぐには用意できなかったので、
とりあえず、DDQGを動かしてみました。

参考文献

https://www.slideshare.net/ssuser07aa33/introduction-to-double-deep-qlearning

https://arxiv.org/pdf/1509.06461.pdf

コメント

タイトルとURLをコピーしました