いろんなサイトをランダムに読んでいたけど、仕様というかどういう流れで実装するか(論文によくかいてあるやつ)が大切ということがわかってきた。
目次
Spinning Up以外の実装
ただ、Spinning Upの実装をみていてもそこまで理解できなかった。
ので、別のサイトをいくつかみて理解を深めようということで私の中で勝手になった。
この広大なネットの海を探していると(攻殻風)いっぱい参考になる記事があった。
このお方が書いているのが非常に参考になった。
曖昧な部分がなく、本当にこの人は理解しているんだ。
Q learning
main netのtarget net役割もコード見れば非常にわかりやすかった。
main netとtarget netの差を近似していくんだとわかる。
Q networkは
elix-tech
のサイトに詳しく書かれています!
コードやペーパーを読むときも何か紙にメモをしながら読んだほうがよいかもしれない。(とふと思った。)
これホントに参考になるんですが、説明に無駄が全くないというか、理解しやすい。
Q learningはざっくりと、今の状態(s)と行動(a)は予めわかっているから、今回はgymの環境(つまりgymひいてはmujocoがかなり重要なのではないかと思っています)からそれぞれのaの報酬(r)を計算して、その中で最も高い価値の行動を取ったときの状態(s_t)を取得する。
その時に最も高い価値の選択方法が問題となると思うのですが、1回目は適当に選びます。
env.action_space.sample()
を使え。
2回目以降はNNで計算して選択します。
そして、Qテーブルにsとaの組み合わせから得られる報酬をどんどんアップデートもしながら書き込んでいきます。
DQN, DDQN
QiitaのDQNの実装と、elix-techのDQNの実装を比較してみるのも一興。
実装自体は化物級な難しさではありません!(簡単に書いてくれているのだと思いますが)
ただ、正確に理解して正確に実装する。
それが求められている。
いきなりここのQiitaのコードを読むのは難しいので、まずはelix-techがおすすめ。
tenserflowとkerasの組み合わせで実装する方が簡潔らしいです。
そして、deque
というのはよく使われているので使っていくべきだろう。
むしろ知らない方がアレなのかもしれないですが。
Q learningとDQNの違い
かなり丁寧にまとめてくれています。
1つ目の工夫Experience Replayは、学習内容をメモリに保存して、ランダムにとりだして学習します。
2つ目の工夫Fixed Target Q-Networkは、1step分ずつ学習するのでなく、複数ステップ分をまとめて学習(バッチ学習)します。
3つ目の工夫報酬のclippingは、各ステップでの報酬を-1から1の間にします。今回は各ステップで立っていたら報酬0、こけたら報酬-1、195 step以上立って終了したら報酬+1とクリップしました。
4つ目の誤差関数の工夫は、誤差が1以上では二乗誤差でなく絶対値誤差を使用するHuber関数を実装します。
少し時間はかかりましたが、コードを読めば理解できました。
強化学習の理解に苦しんでいるのですが、大きな分類としては行動の価値を予測する実装
と実施した行動から報酬を計算する実装
に別れます。
行動の価値を予測する実装
Experience Replay
行動予測のNNの学習をする時にランダムに選んで学習していました。
QNetworkクラス内のreplayメソッドにありました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# 重みの学習 def replay(self, memory, batch_size, gamma, targetQN): inputs = np.zeros((batch_size, 4)) targets = np.zeros((batch_size, 2)) mini_batch = memory.sample(batch_size) for i, (state_b, action_b, reward_b, next_state_b) in enumerate(mini_batch): inputs[i:i + 1] = state_b target = reward_b if not (next_state_b == np.zeros(state_b.shape)).all(axis=1): # 価値計算(DDQNにも対応できるように、行動決定のQネットワークと価値観数のQネットワークは分離) retmainQs = self.model.predict(next_state_b)[0] next_action = np.argmax(retmainQs) # 最大の報酬を返す行動を選択する target = reward_b + gamma * targetQN.model.predict(next_state_b)[0][next_action] targets[i] = self.model.predict(state_b) # Qネットワークの出力 targets[i][action_b] = target # 教師信号 # shiglayさんよりアドバイスいただき、for文の外へ修正しました self.model.fit(inputs, targets, epochs=1, verbose=0) # epochsは訓練データの反復回数、verbose=0は表示なしの設定 |
ここで、
1 2 |
mini_batch = memory.sample(batch_size) |
というのはMemoryクラスのメソッドなんですが、np.ramdom.choice
でミニバッチ長の配列を返しています。
Fixed Target Q-Network
本実装では32回でモデルを更新するようになっています。
1 |
batch_size = 32 # Q-networkを更新するバッチの大記載 |
ただ、32回に一度更新するのではなく、32実行分memoryに溜まったら、その後行動を1回実行するごとに溜まったmemoryの中から32個選んで更新をかけるというような流れです。
なんか無駄が多そうな気がするが…
ここに関してはペーパーで確認した方がよさそうですね。
1 2 3 |
# Qネットワークの重みを学習・更新する replay if (memory.len() > batch_size) and not islearned: mainQN.replay(memory, batch_size, gamma, targetQN) |
誤差関数
誤差関数にHuber関数を使う。
1 2 3 4 5 6 7 8 9 |
# [1]損失関数の定義 # 損失関数にhuber関数を使用します 参考https://github.com/jaara/AI-blog/blob/master/CartPole-DQN.py def huberloss(y_true, y_pred): err = y_true - y_pred cond = K.abs(err) < 1.0 L2 = 0.5 * K.square(err) L1 = (K.abs(err) - 0.5) loss = tf.where(cond, L2, L1) # Keras does not cover where function in tensorflow :-( return K.mean(loss) |
実施した行動から報酬を計算する実装
clipping
強化学習では比較的多い気がするclipping。
1 2 3 4 5 6 7 8 9 10 11 |
# 報酬を設定し、与える if done: next_state = np.zeros(state.shape) # 次の状態s_{t+1}はない if t < 195: reward = -1 # 報酬クリッピング、報酬は1, 0, -1に固定 else: reward = 1 # 立ったまま195step超えて終了時は報酬 else: reward = 0 # 各ステップで立ってたら報酬追加(はじめからrewardに1が入っているが、明示的に表す) episode_reward += 1 # reward # 合計報酬を更新 |
DQNとDDQNの違い
DDQN(Double DQN)は行動価値関数Qを、価値と行動を計算するメインのQmainと、MAX[Q(s_{t+1}, a_{t+1})]を評価するQtargetに分ける方法です。
分けることで、Q関数の誤差が増大するのを防ぎます。
Qnetwork classのここの部分ですね。
1 2 3 4 5 |
if not (next_state_b == np.zeros(state_b.shape)).all(axis=1): # 価値計算(DDQNにも対応できるように、行動決定のQネットワークと価値観数のQネットワークは分離) retmainQs = self.model.predict(next_state_b)[0] next_action = np.argmax(retmainQs) # 最大の報酬を返す行動を選択する target = reward_b + gamma * targetQN.model.predict(next_state_b)[0][next_action] |
価値(retmainQs)と行動(next_action)は、self.model
、つまりはmainQN
で、MAX[Q(s_{t+1}, a_{t+1})]を評価するのはQtarget
となっていますね。
DQNの場合はmainとtargetの重みを同じにしますが、DDQNだとmainとtargetを完全に分けます。
1 2 3 4 5 6 |
if DQN_MODE: # 2018.06.12 # shiglayさんさんより間違いを修正いただきました # targetQN = mainQN # 行動決定と価値計算のQネットワークをおなじにする # ↓ targetQN.model.set_weights(mainQN.model.get_weights()) |
次回
脱線は続く。
paperから実際に実装しようと思う。
それが一番のチュートリアルとなる。
すごくまとまっていないメモみたいになってしまった…
書きながらまとめるのは難しいですね…