2022年7月24日日曜日

もう1つ修正を施したらちゃんと育つようになった

 前回回帰ネットワークの実装ミスの話をしたが、もう1つだけミスしていたかもしれないところがあった。今までの実装では zero_grad() メソッドをエピソードの終了時にだけ呼び出していたのだが、learn() メソッドが呼び出される度に呼び出されるように変更した。

どちらが正解かははっきりしなかったのだが、裏で回していたKangarooで、これを毎回呼び出した方が安定して成長することがわかったのでこのような変更を加えた。これでバッチサイズが小さくなっても学習が途中で止まってしまうことがなくなった。

AIガオガエンの方でもそうなっているようで、Lv3を何度か攻略してみたところバッチサイズが小さくても過去に見られたようなパフォーマンスの低下が見られなくなった。やっと実装が完了した、ということになるだろうか。まだ問題が潜在している可能性はあるものの、今のところ不審な挙動は見られない。

今は学習率周辺のベンチマークをとっている。もしうまくいくようであれば過去に一度試したGRU、未来予測型、色の見えるエージェントなども順次試していきたい。それぞれ攻略するのに数日単位でかかるので気長な作業にはなりそうだが…。

2022年7月10日日曜日

回帰型ネットワークの実装ミス

うまく成長しない原因もわからないし、Switch本体のバッテリーも壊れてしまっていたこともあり、しばらく配信を停止していた。数日前にまた再開して再度以前の成長していた時の環境を取り戻そうとするもうまくいかず停滞していた。

何か間違いが見つからないかとまたコードを眺めたり、デバッガで値を確認したりしていたところ、一つ予期しない挙動をしている部分を見つけた。

以前回帰型のネットワークのバグ取り(トレーニング中に同じネットワークを2回使ってしまい、状態を変えてしまっていたバグ)をした時、もう1つ同じネットワークを用意することで解決したかのように見えた。ここに盲点があった。

トレーニングがうまくいくためにはこの2つのネットワークは全く同じアウトプットを、時間差をつけて出さなければならない。1つのエピソードを学習する際、2つ目のネットワークに最初のnステップの状態をあらかじめ入力して、nステップ分だけ進めておく。このようにセットアップして、以後1つ目のネットワークが変更される度に2つ目のネットワークにウェイトをコピーすれば、同じアウトプットを出し続けるだろうという算段だった。

何が問題だったかというと、この1つ目のネットワークと2つ目のネットワークが受ける入力はnステップ分だけずれているので、ネットワークの内部状態の変化が必ずしも同期しないということだった。すなわち、毎回ウェイトがコピーされるとは言え、2つ目のネットワークはずれているnステップ分の状態をウェイトが更新される前のネットワークで処理することになるため1つ目のネットワークと全く同じことをするわけではない、ということになる。これによって内部状態にわずかなずれが生まれ、そのずれは蓄積されてエピソードの後半に向けて増えていく。

このバグの存在とバッチサイズを大きくすると挙動がよくなるという現象は無矛盾である。

解決方法は明快で、ウェイトをコピーするタイミングで内部状態も同時にコピーすることで対応できる。(内部状態を更新せずにネットワークを使用する、という解決方法もあり、こちらの方がリソースの節約にもなるが、コードの更新箇所が多くなるので見送った。) これが最後の実装ミスだといいのだが…。

今後は、まず今まで走らせていたLv5クリア前のモデルがパフォーマンスを改善するか確認し、それが確認されたらバッチサイズを小さな値に戻してまたLv3スタートで最初から学習をさせることにする。大幅な学習期間の短縮が見られる可能性もあるが、あまり期待しないようにする。