今回は、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)
結果の出力
上述したコードを実行すると、 下記のような学習曲線をプロットします。
横軸はエピソード、縦軸が報酬になります。
エピソードが進むにつれて得られる報酬が高くなっていきます。
DQNと比較したかったのですが、
過大評価してしまう例をすぐには用意できなかったので、
とりあえず、DDQGを動かしてみました。
参考文献
https://www.slideshare.net/ssuser07aa33/introduction-to-double-deep-qlearning
https://arxiv.org/pdf/1509.06461.pdf
コメント