Karabiner-Elements

Karabiner-Elements を使っていたのだが Visual Studio Code で有効になり困っていたので入れ直す。

で教えていただいたことを元にきれいにインストールした時のメモ。

現状の問題点

macOS 上で Emacs キーバインドが使えるのは良いが、Visual Studio Code で有効になると困る。なぜならば VS Code では別途 Emacs キーバインドが用意されておりそちらを優先したい。

クリーンインストール

  • ~/.config/karabiner を別の場所に退避
  • Karabiner-Elements 設定 - Misc - Uninstall Karabiner-Eelements
  • mac 再起動
  • macOS の素の状態を確認
    • Ctrl-W は効かない。
    • Ctrl-F や Ctrl-B などは効く。これは macOS の機能?
  • Visual Studio Code で Ctrl-X Ctrl-F や Ctrl-X B などが効くことを確認。
  • Karabiner-Elements を公式サイトからインストール

設定

  • 起動
  • Version 13.1.0
  • Complex Modifications - Add Rule - Import more rules from the Internet
  • Emacs key bindings (rev 12) の上4つを有効にする。

f:id:higepon:20201108155433p:plain

追記

number1cruncher.com にある mark region とか ctrl-y を取り込む。 https://github.com/higepon/dotfiles/commit/c912fdd6624354d373426c8dc3d3920658fc4f2e#diff-0dc7a7cc6b2e4051d7e9ecd928e8af90f31115dc774b182451abf6b496ba3762

Kaggle Halite コンペ + 強化学習

Halite by Two Sigma | Kaggle に参加していた。今回はお誘いいただいて @threecourse さん と @Seed57_cash さんとチームで挑んた。

コンペの目的

  • 4人プレイの岩塩(halite)集めゲーム
  • 1プレイヤーが複数の shipyards and ships を操作することが可能
  • 盤面サイズ 21 x 21 のボードゲーム
  • 詳細はここ

前半: チーム結成前

強化学習で解くことを決意。以前学んだ強化学習は記憶から飛んでいるので1から勉強し直すことに。決めた方針は

  • 強化学習は難しいので、とにかくシンプルスタート
  • 時間がたっぷりあるので DQN を自前で実装して理解を深める
  • halite コンペではなく、もっとシンプルな connectx コンペで練習する
  • Colab Pro

結果としてこの方針は良い面も悪い面もあった

良かった点

  • DQN の実装にとても苦労したので強化学習デバッグに強くなった。動いていないのはなぜか?に対して仮説が立てられるように
  • connectx は十分にシンプルな題材で、他にも強化学習で解いている人がいたので参考情報が多かった
  • Colab Pro はすばらしい :)

悪かった点

  • DQN は不安定だったのでかなりの時間を溶かしてしまった。理想的には理論を理解し、サンプルを実行しあとはライブラリを使うのが正解な気がする
  • 時間はたっぷりなかった。強化学習デバッグ・トレーニングに時間がかかる

後半: チーム結成後

@threecourse さんに pfrl を教えてもらい自分の実装を移植した。ライブラリを使うと責任の分担がはっきりするので observation, reward そして model に集中できる。さらに DQN が動くようになると数行の修正でより安定した PPO が動くようになりコストパフォマンスが大変良かった。チームメイトと slack で議論・情報共有しながらできたのはとても良かった。他にも @threecourse さん と @Seed57_cash さんがモデルについて話しているときに、「ああ。このくらいやらないとだめなんだ」というベースラインをしれたのは大変ありがたかった。

実装の詳細

  • ship の convert はハードコーディング
  • 1 ship が 1 agent
  • self play (自己強化学習
  • 上記2つから 4人対戦の場合 4 * ship の数の agent がいることになるので training 負荷が高かった
  • reward
    • 衝突回避が難しかった。結局大きな goal (= 最終的にゲームに勝つ)では衝突回避は学習できなかった。衝突を事後検知して失われたスコアに応じて負のrewardを与えた
    • ship は4方向の移動と動かないという選択肢があるのだが、compound 命令として deposit を定義した。これのおかげで deposit という行動を取ると reward を貰えることを学んでくれた
  • observation
    • 6チャンネルの 2D array (自分の位置、味方の船、shipyard の位置、時間経過、halite の位置、spawn予定地)

どこまで行けたか

  • 1人プレイ & 1 ship で halite をあつめて deposit => OK
  • 2人プレイ& 1 ship for each で haliteをあつめて deposit => OK
  • 2人プレイ & 2 ship for each => カオス

結局複数プレイヤー複数 ship は正しく動くところまで持っていけなかった。pfrl を正しくない方法で使っているのでそれが原因かもしれない。

所感

  • 強化学習は安定させるのが難しい。ただしデバッグ方法を理解すれば、かなりましになる。
  • チームでやるほうが学びがある。本当にありがたい。
  • 実世界で役に立つ強化学習もやってみたい。

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 にしたら全て動くようになった。

Reinforcement Learning の self play についてのまとめ

強化学習の self play について知りたいことがあるので、ざっくりと有名な論文を読んでいく。熟読はしない。

知りたいこと

  • self play は同一インスタンス、別インスタンスどちらか?
  • 直感的にはどこかで stuck しそうな感じがするけど?
  • 学習が進んでいることをどのように評価するか?

論文

Jupyter/Colab における pyplot リアルタイム描画

強化学習の様子を visualize するために pyplot でグラフをリアルタイム描画していたが遅くて筋が悪いのでやめた。inline で表示すると plot 数が増えると極端に遅くなりボトルネックになる。inline リアルタイム描画をやめて、画像ファイルとして出力して inline 表示も試したが同様。もともと気軽に見られるようにとの意図だったが完全に裏目に出ているのでやめる。tensorboard のように log からグラフ描画のほうが筋が良さそう。

強化学習/RL/Reinforcement Learning のデバッグ方法

RL のデバッグは難しい。RLアルゴリズムの選択、適切な reward の設定、Deep RLの場合モデルの選定、実装の正しさ、適切なパラメータ、そもそも学習できる問題なのか。切り分けが難しい。世の中には同じように思っている人がたくさんいるようだ。情報元から適当にまとめる。

情報元

チェックリスト

  • 低次元の state をもつシンプルな environment で問題をかんたんにする
  • reward を simple にしてみる。すぐに効く形の feedback も良い。
  • random policy に解かせてみる。random でも時々問題を解けそうなら有望。
  • 自分の目で observation を見て、自分でも解けるか確認
  • Observation mean 0 std 1 に scaling する
  • Reward も可能なら scale する
  • Observation/Reward に outlier がいないことを確認する
  • 動くかな。やってみよう。はうまくいかない。たくさんのことを正しくやらないと学習は進まない。
  • agent を正しく実装することがとても重要。他のコードよりもテストが大事。
    • 可能な限り unit tests を書く
    • ありとあらゆる場所に asserts を。matrix dimension, input, output, action の range
  • 長い時間実行する前に、1行1行目を皿にしてコードを読む。
  • 可能な限り見える化、すべてを log する
  • いくつかの states をピックアップして q-value を見てみる。徐々に変わって stablize されるはず。
  • すべての input/output/state を記録する
  • Neural Net がからむと 10 倍難しくなる。最初は Neural Net でやらず。うまく動いたら swap する
  • seed を固定する
  • optimizer の選択にsensitiveなので注意
  • すべてを normalize せよ
  • 良い結果が出ているものは100-1000 の reward 使ってる
  • 参照実装が使っている hyper parameters 使う
  • 大きい replay buffer, bigger batch size
  • 常に simple version から試して動くことを確認
  • そもそも agent はときどき正しいことをやっているの?
  • DQN は収束がおそいよ

RL での batch size

Reinforcement LearningWelcome to Spinning Up in Deep RL! — Spinning Up documentation で勉強しながら実装している。とある実装で batch size = 5000 となっていて「値が大きすぎる」と思い、何気なく小さな値に変更した。それをすっかり忘れて試行錯誤しているうちに policy gradient (logprob) が 0.0 になってしまい学習が進まない減少に悩まされた。ログを見て、よくよく考えてみたら logprob が 0 ってことは選択された action の確率が 1 ってことだ。つまり policy はどんな状態でも1つのアクションしか取りようがない状態をモデルが学習してしまっていた。さらに観察すると、これはある episode で agent が右に移動し続けただけで大きな報酬を得てしまったのを学習したのだとわかった。batch size が小さかったせいでこれが大きく聞きすぎてしまったようだ。