忙しい人のためのTuning Playbook
January 26, 2023 | 11 min read | 2,811 views
先日、Google ResearchとHarvard大学のメンバーが『Deep Learning Tuning Playbook』を公開しました。この資料は、性能の良い深層学習モデルを作る方法(特に、ハイパーパラメータの決め方)を解説したものです。ハイパーパラメータは深層学習の成否を支配する重要な要素の一つです。しかし、ハイパーパラメータの決め方に関する知識は論文や教科書では取り上げられず、「達人たち」に属人化していました。Tuning Playbookは、これを言語化して資料にまとめようという重要な試みです 1。
Tuning Playbookは素晴らしい資料ですが、他人に気軽に薦めるには長すぎると感じました。そこで、自分の勉強も兼ねて、日本語で要点をまとめました。簡潔さを優先して色々と省略しているので、詳しい説明は元資料を当たってください。
新しくプロジェクトを始めるとき
この節では以下を仮定します:
- 問題設定やデータクリーニングは完了している
- 問題に即した適切な評価指標が選択できている
- いろいろな設定を試すための訓練と評価のパイプラインができている
新しくプロジェクトを始めるときのベースラインは以下の手順で作成します:
- よく使われるモデルアーキテクチャと最適化器を選ぶ
- メモリが許す最大のバッチサイズを選ぶ
- その他のハイパーパラメータはミニマルにする
この手順について、詳しく見ていきます。
モデルアーキテクチャ
既にうまくいくことがわかっているモデルから始めましょう。
- ベースラインによく使われる、「枯れた」モデルを使いましょう
- 可能であれば類似した問題設定の論文を見つけて、それを再現実装してみましょう
最適化器
その問題設定でよく使われる最適化器から始めましょう。
- あらゆる問題に使える「最強の」最適化器はありません
- そもそも、最適化器の性能を比較すること自体が難しい問題です
- その問題設定でよく使われる最適化器から始めましょう
- 例:SGD(モメンタム付き)、Adam、NAdam
- 最適化器のすべてのハイパーパラメータに注意を払いましょう
- チューニングは後段で行うので、ここでは頭の片隅に入れておくだけで構いません(詳しくは「その他のTips」)
- 悪い例:モデルの最適な層数を決めるときに、最適化器のハイパーパラメータをどの層数でも同じにする
バッチサイズ
バッチサイズは訓練が進む速度を定めるハイパーパラメータであり、検証セットでの性能によって直接チューニングすべきではありません。メモリに収まる最大値で固定しておくのがよいでしょう。
- バッチサイズをなるべく大きく取ることで、訓練にかかる時間を最小化できます
- 「訓練にかかる時間」は「1ステップあたりの時間」×「収束までのステップ数」です
- 前者は、理想的にはバッチサイズによらず一定です
- 後者は、バッチサイズと概ね反比例します
- バッチサイズを2倍にすると、収束までのステップ数が半分になることが期待できますが、この効果はバッチサイズに対して逓減していきます
- したがって、バッチサイズを大きく取るほど、訓練にかかる時間は短くなります
- バッチサイズを検証セットでの性能によって直接チューニングすべきではありません
- 他のハイパーパラメータ(学習率やステップ数)を適切に選べば、任意のバッチサイズで同じ結果を達成できるはずです
- バッチサイズを決めるには、何度か訓練を実行してみる必要があります
- OOMになるまでバッチサイズを2倍にし続けます
- OOMになる直前のバッチサイズを選び、その値で固定します
- スループットが一定のバッチサイズから低下するのであれば、そこで止めます
- バッチサイズを大きくする目的は時間短縮だからです
- 同様の理由で、勾配累積(gradient accumulation)は時間がかかるので避けましょう
- モデルや最適化器によって許容できるバッチサイズが変わるので、これらを変更したらバッチサイズを決め直す必要があります
- 他のハイパーパラメータ、特に最適化器(学習率やモメンタム)と正則化はすべてバッチサイズに依存するので、バッチサイズは最初に決定しましょう
- もし変更した場合は再度チューニングが必要です
初期設定
初期段階のハイパーパラメータは、「そこそこの結果」にミニマルな実装で到達するという方針で決定しましょう。
- 初期段階で定めなければならないハイパーパラメータ(のうち、ここまでで決めていないもの)は以下の3種類です
- モデルに関するもの
- 例:層数
- 最適化器に関するもの
- 例:学習率
- 訓練ステップ数
- モデルに関するもの
- 初期段階では、「そこそこの結果」にミニマルな実装で到達することを目指します
- 例:小さいモデル
- 例:一定の学習率
- 訓練ステップ数は、性能と改善サイクルの短さのバランスから決めます
改善サイクルを回すとき
この節では以下を仮定します:
- 前節のプロセスによって「そこそこの結果」に到達するベースラインが得られている
- 実験を並列で実行するのに十分な計算リソースがある
ここからは、ベースラインの改善を行います。そのときの心構えは、
- 問題に対する洞察を得ることを意識しながら、確実な改善を少しずつ加える
- 不必要な複雑さを避けるため、強い根拠がない限り変更を加えない
です。
改善の手続きは自動的なチューニングアルゴリズム(Bayes最適化など)で代替できるようにも思えますが、探索空間をうまく設計しないと有限の時間でいい結果を得ることはできません。効率的に良い結果を得るためには、「自動チューニングは各実験(改善サイクル)の内部で利用し、そこで得られた洞察を次の実験設計に利用する」という方針を採るのが良いでしょう。実験の設計を「探索」、自動チューニングを「活用」と捉えることもできます。「探索」を疎かにすると局所解に陥ってしまうので、「活用」よりも「探索」を重視するほうが長期的には良い結果にたどり着くでしょう。
改善サイクルでは、以下のような手順を繰り返すことになります:
- 次の実験の目標を決める
- 目標を達成するための実験を設計し、実施する
- 結果から洞察を導く
- 満足の行く洞察が得られたら、繰り返しを終了する
この手順について、詳しく見ていきます。
次の目標を定める
各実験には明確な唯一の目標を持たせましょう。
- 実験の目標は、適切なスコープを持った明確なものであるべきです
- 良い例:
- データの前処理方法が与える影響を調べる
- 活性化関数の選び方が与える影響を調べる
- 検証誤差を最小化する
次の実験を設計・実施する
scientificなハイパーパラメータを公平に比較するために、nuisanceなハイパーパラメータを最適化することでその影響を排除しましょう。
- ハイパーパラメータは以下の3種類に分けることができます
- scientific: 影響を調べたいもの
- nuisance: scientificを比較するためにチューニングする必要があるもの(参考:局外パラメータ)
- fixed: スコープを狭めるために仮定するもの。これを変えると、実験から得られる洞察が保障されなくなります
- 例:モデルの層数が性能に与える影響を調べたい場合
- 層数はscientificです
- 学習率はnuisanceです
- 活性化関数はfixedです
- ハイパーパラメータの分類は実験の目標によって変わりますが、概ね以下のような方針で決めることができます
- 最初にscientificを決め、次にリソース制約からnuisanceを決めます。残りをfixedとします。
- 最適化器のハイパーパラメータは大抵nuisanceです。一方、最適化器の選択は大抵scientificかfixedです。
- scientificなハイパーパラメータを公平に比較するために、nuisanceなハイパーパラメータを最適化することでその影響を排除しましょう
- ここでBayes最適化を使っても構いませんが、探索段階では準乱数に基づく方法を勧めます(詳しくは「その他のTips」)
- リソース制約などの理由で複数のscientificなハイパーパラメータを独立に実験できない場合は、やむを得ず混ぜて実験することもあります
- その場合は特に準乱数に基づく探索をするべきです
- 実験を実施するときには、以下の項目を(できれば自動で)記録しましょう
- 実験の名前
- 設定ファイルへのリンク
- 試行回数
- 最高スコア
- 再現するためのコマンド
結果から洞察を導く
結果が出て洞察を導く際には、実験が正しく実施されたか振り返るのを忘れないようにしましょう。プロットはなるべく自動化しましょう。モデルや訓練パイプラインを変更する意思決定をするときには、確率的な変動と追加される複雑さに注意しましょう。
- 実験が正しく実施されたかどうか、以下のような観点から確認しましょう
- 探索空間の広さは十分だったか?
- 最適解が境界付近が見つかっているならば、探索空間を広げて実験を続けましょう
- 探索空間を埋めるのに十分なサンプルを得られたか?
- 不足している領域があれば、そこからサンプルを取りましょう
- 一様に不足しているなら、実験を続けましょう
- ただし、サンプルが十分かどうかは非自明です
- ランタイムエラーや勾配消失が起きる試行が多数あったか?
- そうであれば探索空間を再設計してやり直しましょう
- 訓練曲線の形は正常か?
- 最終的な検証誤差だけを見ていては大事な情報を見落としてしまうので、最善の数試行だけでも訓練曲線を見るようにするにしましょう
- 過学習しているようなら、正則化(ドロップアウト、ラベル平滑化、重み減衰など)を足してみましょう
- 訓練の後半で誤差がステップごとに振動しているなら、安定化(バッチサイズを上げる、学習率を下げるなど)を検討しましょう
- 探索空間の広さは十分だったか?
- 洞察を導くには各種プロットが役に立ちます
- 例:縦軸に検証誤差、縦軸に各ハイパーパラメータをとった散布図
- 考察に時間を割くため、プロットの手続きは自動化しましょう
- 入れようとしている変更を反映させるかどうかの意思決定は、「改善の蓋然性」と「追加される複雑さ」のトレードオフに基づいて行います
- 良い結果が出たとしても、それは確率的な変動による偶然かもしれません
- 同条件で複数回試すと、意思決定の精度を上げることができます
- それでも絶対的な確実さは得られないので、最終的には複雑さとのトレードオフから判断します
最後のひと押し
一連の実験によって良い探索空間が求まったら、Bayes最適化で性能をブーストしましょう。
- 十分な洞察が得て探索空間が決まったら、目的が「問題に対する洞察を得る」から「最高性能を達成する設定を見つける」に変わります
- ここからはBayes最適化が強力なツールになります
- 最適化が終わったら、テストセットでの評価もしてみましょう
その他のTips
マルチホストの注意点
- ログとチェックポイントは1つのホストだけで実行しましょう
- 評価やチェックポイントの際、バッチ正規化のパラメータがホスト間で同期されていることを確認しましょう
- モデルの初期化に使う乱数シードはホスト間で統一し、データのシャッフルや前処理に使う乱数シードはホストごとに異なるものを使いましょう
訓練ステップ数の決め方
- データ拡張やドロップアウトを追加すると、勾配の分散が増えるので、必要なステップ数が増加します
- ステップ数は大きめにとっておき、学習が終わってから最適なチェックポイントを選びましょう
- early stoppingを入れる必要はありません
準乱数に基づく探索のメリット
- 準乱数に基づく探索には以下のようなメリットがあります
- ランダム探索よりもグリッドに近いため一様性が期待できる
- 検証誤差だけでなく訓練誤差も見たくなったときなど、事後分析ではBayes最適化よりも使いやすい
- Bayes最適化よりも再現性が高い
学習率のスケジューリング
- いろいろな学習率を試すことで最適な学習率に当たりやすくなるので、何かしらのスケジューリングは入れた方が良いでしょう
- 最適なスケジューリング方法を決めるのは難しいですが、よく使うのは線形減衰とコサイン減衰です
- 論文で稀に見かける複雑なスケジューリングは、Human-in-the-loopの結果であることもあります
Adamのハイパーパラメータのチューニング
- チューニング対象は試行回数を目安に決めましょう
- 10回未満で済ませるなら、学習率のみ
- 25回までなら、β1も
- それ以上なら、εも
- 更に増やせるなら、β2も
訓練を安定化させる方法
- 学習率のウォームアップ
- この方法は、訓練初期の安定化に効果があります
- 訓練が不安定になる閾値を求め、学習率をゼロから閾値の10倍程度までゆっくりと上げていきます
- ウォームアップにかけるステップ数は10倍ずつくらいの粒度でチューニングしましょう
- ウォームアップ後の学習率は一定で構いません
- 勾配クリッピング
- この方法は、訓練中盤から終盤の安定化に効果があります
- L2ノルムを監視し、異常値が原因になっているようなら90パーセンタイルなどでクリップしましょう
- 勾配クリップがステップの半数以上で発生するなら、諦めて学習率を下げましょう
- 学習率を下げる
- 上記の方法がうまく行かなければ、学習率を下げましょう(訓練にかかる時間が長くなります)
-
↩
Kaggleで公開されているコンペの解法を見ていると、同じような動機を読み取れることがあります。
Written by Shion Honda. If you like this, please share!