DQN デバッグメモ

デバッグの過程をまとめる。ほぼ自分用。

自分用のまとめ

  • PyTorch でも tensoarboard を使ってたくさんログをとる。ログをとればおかしいところはみつかる。
  • ログすべきもの
    • observation を tensorboard で visualize する。
    • 次の最善手が分かっている observation を手動で作る。policy network が出力する action values を tensorboard に出力する。その action value が最善手に converge するか見守る
    • loss
    • benchmark agent との対戦結果。win rate ではなくて win/draw/lost 数をプロットする。
    • 1 episode の長さをプロットする。self play では徐々に長くなることが想定される
    • self play でなければ average reward。
    • eps decay curve 。これをプロットして学習が進む前に random move をやめてしまうのを防ぐ。
  • 特に注意すべき点
    • agent に渡されている observation を人の目で観察する。与えられた情報で自分が勝てるようになるか考える。
    • observation を neural network 用に加工するときにミスをしていないか確認する。
    • reward が clipping されているか。
  • Target network の更新頻度を調節せよ
  • policy network の更新式を理解してデバッグする
  • 同様の試みの learning rate / モデルサイズ / learning が始まるまでの steps などを参考にする

以下は自分がデバッグしていたときの生ログで未整理です。

可能な限りログを取る

reward, action, observation, average_reward などを logger でログを取る。

Visualize

Observation を人の目で見られるように visualize した。もともとの DQN 実装が current_state - prev_state = observation としていたが、これではどうやっても Connect X では勝てない。差分だと置かれた1つのコマとそのポジションしかわからない。

epsilon decay

DQN では agent に exploration させるために一定の確率で action を random sample する。この確率は episode が進むにつれて小さくなるように設計されている(=学習が後半で進むと仮定しているから)。この確率をログしてみると。どうも学習が全然進んでない段階で小さくなりすぎているように見えたので調整した。

loss

reward ばかりに気を取られて loss をグラフに描いてみたら様子がおかしい。初期にガクッと下がって、その後ぐんぐん上がっていく。これはおかしいのだろうか。まずは loss の定義から見直そう。 To minimise this error, we will use the Huber loss. The Huber loss acts like the mean squared error when the error is small, but like the mean absolute error when the error is large - this makes it more robust to outliers when the estimates of Q are very noisy. とある。そもそも error は以下ののものを比べている。

  • state=s, action=a に対する Q value. Q(s, a)。
    • memory から取り出した s と a に一致する Q value をNN から取り出したもの
  • 次の time step における最大の Q value * γ + r。
    • memory から取り出した next_state に対して最大のQ value とそれを与える action を特定

このエラーが徐々に大きくなるのはいくつかのシナリオが考えられるが、まずは詳細なログを取ってみよう。 f:id:higepon:20200720172017p:plain

100 episodes ごとに Q value の絶対値が大きくなっていることが観察された。それにともない loss が相対的に大きくなっているように見える。Q value が大きくなっているのはなぜか。ある state に対する報酬全体が上がるとは、初期状態に Q function が見積もっていたよりも実際の報酬が高いということ?reward をログしてみた。reward は 1, -1, 0 のどれかである。reward を non_final_mask で絞るとすべて 0 だ。あれこれでは学習進まない? と思ったけど正しそう。他のDQN実装とも見比べた。あと loss が increase するのはありっぽい。

python - Cartpole-v0 loss increasing using DQN - Stack Overflow

状況を整理

  • 観察されていること
    • loss が下がってそのうち上がる
    • 同時に random agent との対決での勝率が下がる
  • 仮説またはアイデア
    • そもそもどれくらいで Converge するのかを調べる
    • 他の実装を動かしてみて観察する
    • loss が下がりきったときの勝率を見てみる。Eearly stopping するの?
      • episode 200 あたりで下がりきって勝率もそこから下がる
        • 普通の NN なら下がり続けるので上がる理由が理解できない。
    • learning rate が大きすぎるのでは
      • learning rate 1e-3 にして実験中
        • qvalues が stable で値も爆発していない。良いかも!
        • 50000 episodes は少なかったかも。
        • Pytorch DQN - ConnectX | Kaggle だと 5e-4
        • 200000 episodes, average reward も print しよう eps が急激に小さくなっていた。

うまく動き出したような気がする

eps をグラフに描いたら明らかにおかしかった。学習の初期に一気に最小値まで下がってしまっていた。EPS_DECAY = 200000 ほどにするとゆっくり下がってよい。この変更により明らかに avg reward が変わった。 f:id:higepon:20200723082543p:plain

うまく行っていると思う理由 - avg reward が徐々に上がって正の数になっていること。つまり勝ちが多い。 - 同様の指標だが実際の戦闘で勝率 0.5 を超えていること - とある state に対する qvalue が converge している など。loss は必ずしも下がるのが正解とは言えないということらしい。 うまく動いたものを保存するために別ディレクトリにグラフと script を隔離。

問題点

  • reward stats の計算に間違いがある
  • 最大限 debug しているので遅い。1 episode の時間を計測しよう。
  • loss の振れ幅が大きいので learning rate を小さくしてみよう
  • Colab でうごかしてみよう

Colab で動かしてみた lr=1e-2

f:id:higepon:20200723152751p:plain

qvalues と loss は全然収束してないのに Avg reward と Win rate は上がっている。

Colab で動かしてみた lr=1e-3

f:id:higepon:20200723153203p:plain

qvalues と loss は収束しているが Avg reward は下がっている。

速度改善・クラッシュ修正

pyplot がメモリを使いすぎてクラッシュ。描画点が増えるにつれて pyplot が急激に遅くなり学習時間よりも長くかかってしまう。Tensorboard in Pytorch の乗り換えてすべて解決。ちなみにグラフだけではなく observation を画像として Tensorboard に表示することも可能。こうやって苦労すると Tensorboard のすばらしさが分かる。

lr=1e-2

f:id:higepon:20200725080505p:plain

  • 9万エピソード
  • loss, qvalue ともに収束してないように見える
  • 勝率は 0.5 をほぼつねに超えている
  • 勝率は高めだが分散が大きい

以上の観察から lr が大きすぎてうまい local optimal を見つけられないのではと推測。

lr=1e-3

f:id:higepon:20200725080952p:plain

  • 7万エピソード
  • loss, qvalue ともにどこかに収束しつつあるように見える。自信なし。
  • avg reward > 0 である

1e-3 よりはまともにみえる。しかしやはり他の記事を見ると loss の大きさが気になる。

DQN debug ブログを読み解く

Solving Open AI gym Cartpole using DDQN - The intersection of energy and machine learning

  • batch size
  • model parameter 可視化
  • target copy 頻度
    • 10 episode ごとではなくて 10000 steps ごとにした
  • target copy うごいてる???
  • next q も表示してみる

target net の update の更新頻度

上記ブログを読んでみると target net の更新頻度の目安が書いてあり 10K - 100K step ごとらしい。頻繁すぎたかも。

lr=1e-3 10000 steps で更新

loss の scale が自分が思っていた範囲に収まっている。qvalue もしかり。 f:id:higepon:20200725202037p:plain

ところで以下も visualize してみたが policy net と target net の weights がおかしくない?5000 step 離れるとこんなに変わるものか?いや変わるか。5000 steps ごとのはずなのに全然表示されない部分がある。値がゼロに集中するわけではないし。これはなんだろうか。その後 debug したが target update は正常に行われていることを確認した。全然表示されないのは頻度が少なすぎるのかもしれない。 f:id:higepon:20200725202222p:plain

lr=1e-2 10000 steps で更新

f:id:higepon:20200725203003p:plain f:id:higepon:20200725203028p:plain

submission

lr=1e-2 10000 steps で流して reward を観察する。まずうまく動いているかを確認する。 うまく動いているならば early stopping が stability を試みる。local で random agent に対して 0.81 ののものを submit した。Score は 580.7。弱いが。最初の1歩。

negamax との対戦

random 相手に学習しても強くならないのは明らかなので、もう少し強い negamax と戦わせてみる。途中で時間切れになったが少しずつ reward が上がっているのが見える。ただし negamax はアルゴリズムとして遅いのであまり学習に向かないのでそこまで。 f:id:higepon:20200728081012p:plain

self play

self play を試みているが、まだコードが正しいか自信が持てない。気づいた点を片っ端から直していく。

episode time が定期的におそくなっている 

f:id:higepon:20200810130557p:plain

これはかなり怪しい。学習速度の足を引っ張っている可能性も。play 自体にかかっている時間かどうかを切り分けよう。結論 negamax との対戦が遅い。これはどうしようか。とりあえず頻度を下げた。

かかる時間

10000 episodes にかかる時間は ~4000 secs。

観察1

  • negamax との win rate がとてもゆるやかに上がっている
  • qvalue が収束していない
  • eps decay が速く下がりすぎている

f:id:higepon:20200811083751p:plain

次のステップ

  • この学習をそのまま走らせておく
  • eps decay を緩やかにしたものも走らせる
    • 結果:同様に win rate が下降気味。ただし qvalue は収束しつつある。
  • eps decay を緩やかかつ lr=1e-3 にしてみる。
    • 結果:同様に win rate が下降気味。ただし qvalue は収束しつつある。
    • 仮説:常に自分自身と戦うと成長しないのではないか?過去の自分のスナップショットと戦わせよう。
  • 過去の自分と対決するコードを書き始める

過去の自分と対決

self play を実装した。 f:id:higepon:20200813091617p:plain

時間をかけても強くなっていない。一方で他のグラフは特に矛盾がない。このことから agent が本当に強くなる前に世代交代しているのではないかという仮説を立てた。そのため Alpha Zero の論文のようにパラメータを改める。 - 100 episode ごとに世代評価を 1000 に。 - 対戦数を 11 -> 100 に。11 だと偶然6勝してしまうことがありそう。

あと気になるのは avg_reward が 0 以下なのに世代交代が起きていること。これは episode の reward の平均をとっていたからだ。勝負がつかないのは -0.05 が積まれるから。avg_reward は勝負がついたときの reward の平均としよう。

self play negamax, random を evaluation として使う

f:id:higepon:20200814175109p:plain f:id:higepon:20200814175133p:plain

  • reward が上がっているのは良い。
  • level は右肩上がりで良い
  • random 相手でも勝率 0.5 はだめすぎる。
    • 仮説1: random 相手に勝てるようになるにも時間がかかる
      • 検証する方法。
    • ConnectX の DQN 実装他に探す。収束までの episodes 調べる。
  • 仮説2: 実装に間違いがある
  • 仮説3: 1000 steps あたりでは vs random で勝率 0.85 だった。そのあと忘れた?

試すこと

  • Observation mean 0 std 1 に scaling する
    • 20200815_experiment_standardize_pixels.ipynb で試している
      • 特に変化なし。
      • f:id:higepon:20200816084342p:plain
  • 大きい replay buffer
    • 100000
      • 20200815_exp_buffer_100000.ipynb
      • loss の order が小さいままなのはよいのか?右肩下がりに random との戦績が下がっている。
      • f:id:higepon:20200816084700p:plain
    • 1000000
      • 20200815_exp_buffer_1000000.ipynb
      • 上記とあまり変わらず
      • f:id:higepon:20200816084820p:plain
  • Look at sensitivity of EVERY hyper parameter
    • lr 1e-3 をためそう 100000 と比較すること
      • random 相手に 0.5に収束。
      • eps length 11 あたりに収束
      • f:id:higepon:20200817082855p:plain
      • f:id:higepon:20200817082913p:plain
    • 勝率0.55 ではなくて 0.60/0.70 でせだいこうたいはどうか?
      • f:id:higepon:20200817084640p:plain
      • f:id:higepon:20200817084701p:plain
  • Look at episode length (sometimes more informative than episode reward).
    • episode length がきれいに長くなっている。これは良い傾向か?勝負がながびく reward は -0.05 にしているけども。あとやっぱり長引くにつれて random に負けるのはなぜ。もっとよく考えろ。多分良い傾向。お互い強くなってミスをしなくなっている。でもなぜ random 相手に勝てないのか?
    • f:id:higepon:20200816084949p:plain
    • f:id:higepon:20200816085004p:plain
    • f:id:higepon:20200816085049p:plain
  • You might need a huge buffer, so adapt code accordingly
  • 仮説: 勝率が 0.5 に近づく = random に近づくということから model が学びをリセットし続けている。モデルサイズが小さいという仮説。

他の実装をみる - https://github.com/kirarpit/connect4 を見ると 6x7 の board size だと死ぬほど時間がかかると書いてある。4x5だとすぐに収束するようだ。 - 試してみたが全然だめ。 - f:id:higepon:20200819184431p:plain - board_size 変えられるかな? https://codebox.net/pages/connect4 ここはネットワークサイズが分かる。

debug

  • Q-Learning 復習。今回は完全に理解した!
  • だめな点
    • 先手後手どちらも学ばせないといけない。next_state があるから。
    • 先手・後手の色は変えてはいけない。=> これはまちがい。OKです。
    • 盤面にある数で自分の手番はわかる。
    • scaling で色は変えてはいけない。
  • loss が 1000 steps を超えたら急激に上がる
  • target update 頻度を上げたら loss のけたがあがりすぎた?
    • そう。target に近づいて loss 下げる前にまた自分で更新されたら辛い。
  • よくよくself play reward を log してみたら 0 と 1 しかなかった。self play だと自分の手番で負けることがないから!! reward を 0 -> -1 にしたら全て動くようになった。