この記事は弊社のMamunがMediumに投稿した以下の記事を日本語訳したものです。
ディープラーニングやAIの時代にあって、何百万ものパラメータを持つモデルをトレーニングすることは比喩的、文字通りに「安い」作業ではありません。そして、そのための環境を所有していない場合、クラウドベースのリソースでモデルをトレーニングすると、莫大な費用がかかるでしょう。
CUDA対応のGPUを搭載した計算エンジンを提供するサービスはたくさんあります。しかし、それらのサービスはどれもほぼ同じように高価です。そして、もしあなたのコードがうまく最適化されていなければ、それはあなたにとって災厄となるでしょう。
ディープラーニングの愛好家の多くは、コードの最適化を重視せず、全体的なパフォーマンスをわずかに向上させるために過剰な数のリソースを割り当てようとしています。しかし、少しの最適化で、同じことを達成し、多くの時間と費用を節約することができます。
そこで、ここではディープラーニングモデルのトレーニングコストを削減するためのベストプラクティスをいくつか挙げてみたいと思います。
コードのプロファイリング
コードのプロファイリングはディープラーニング愛好家の間ではあまり知られていませんが、コードの中で何が起こっているのかをよりよく知ることができる方法です。コードのプロファイリングには様々な意味がありますが、今のところ最も重要なのは、どの関数やメソッドが最も時間を割いているかを知ることです。
すべてのディープラーニングフレームワークは、行列操作タスクを行うために高度に最適化されています。しかし、モデルには他にも多くのことがあります。例えば、畳み込み操作は通常、単純な行列変換よりもはるかに多くの時間がかかります。別のケースでは、パラメータを複数回反復するループを含む新しい手法を書いたかもしれません。あるいは、コンテンツを意識したデータの前処理をその場で大量に適用している場合もあるでしょう。
コードをプロファイリングしていると、これらのボトルネックを簡単に見つけることができます。もしかしたら、いくつかのブロッカーは避けられないかもしれませんが、簡単に避けられるものも間違いなく見つかります。
良いデータローダを書く
モデルをトレーニングするとき、モデルがどれだけうまく学習しているかに関心を払うべきであり、それが最優先事項です。ゆえにデータローダはモデルが必要とするときはいつでも必要なデータを提供しなければならず、モデルを待たせてはいけません。良いデータローダを書くことに少しの時間を投資することで最終的には多くの利益をもたらします。
バッチを高速に処理するために複数のワーカーを割り当てることができます。データのプリフェッチは、トレーニング中の非アクティブな時間を短縮するための良いテクニックでもあります。
トレーニングの方法が許すなら、HDF5/Parquet/Featherコンテナにデータを保存しておいて、後でトレーニングの過程で読み返すこともできます。HDF5形式であれば、データを異なるグループに分けて保存することで、さらに高速にデータを取得することができます。
システムのリソース使用状況の監視
システム監視を行うことで、リソースの活用状況をよりよく理解することができます。
例えば、上の図を見ると、CPUがデータの受け渡しが間に合わないために、GPUがその能力をフルに発揮できないことがわかります。この場合、別のGPUを追加したり、GPUをより多くのメモリとパワーに交換しても問題は解決しません。
一方で強力なCPU(GPUよりも安価)を追加した後、結果は次のようになります。
CPU追加後にGPUがほぼフルで使えている状況
プロットを見ると、GPUの利用率は大幅に向上していますが、CPUの負荷は低下していることがわかります。これは一例に過ぎませんが、リソースの無駄を減らすために、より良いトレードオフができることを示しています。
Mixed Precision Training
Mixed-precision training ¹は、ディープラーニングモデルを高速にトレーニングし、計算の複雑さを軽減するための技術として、現在では認知されています。PoCを行う場合や、精度と速度のトレードオフを余儀なくされるような状況では、この手法を活用することができます。
浮動小数点の精度を下げることで学習時間が大幅に改善され、メモリ量も少なくて済みます。さらに、このようなモデルをオンボードコンピュータで展開すれば、推論時間も大幅に短縮されます。
後からGPUを割り当てる
開発の第一段階では、GPUは全く必要ないかもしれません。CPUモードからGPUモードにコードを変更するのはほんの数秒で可能です。だから、使われていないGPUにお金を無駄にしないようにしましょう。
データクリーニング、前処理、サニティーチェックはすべてCPUモードで行い、最後に自分のコードに自信が持てたら、予算と要求に応じてGPUを割り当てましょう。
可能な限り多くの情報を記録する
意味のない実験を減らすようにしましょう。初心者はこのミスをより頻繁に行ってしまいます。学習に関する情報を記録しないで、結局同じ実験を何度もやってしまうのです。
少なくとも、そのようなイベントを排除するために、学習の戦略のハイパーパラメータと主要な変更をログに記録します。
累積勾配の計算(Accumulated Gradient)
大きなバッチサイズを試しているときにGPUメモリが足りなくなってきた場合、Accumulated Gradientは救世主になるかもしれません。例えば、4つのサンプルのバッチしかGPUのRAMの制約で実行できない環境で、16のサンプルのバッチを実行させたい場合、4つのフォワードパスを行い、それぞれのパスのロスを蓄積します。そして、その4つのバッチに対して1回だけバックワードパスを実行します。
残念ながら、すべてのディープラーニングフレームワークがこのAccumulated Gradientを提供しているわけではないので、これを実装するには追加の関数を書く必要があるかもしれません。
PyBind を使って高速化する
プログラミング言語への忠誠心が、それに値する以上の犠牲を払わないようにしましょう。Pythonは多くのオプションと柔軟性を与えてくれますが、速度の面ではC/C++や他の多くのプログラミング言語よりもかなり遅いです。
そこで、C++でいくつかのコードを書くことを覚えて、トレーニングの全体的なプロセスを高速化しましょう。PyBind11はC++11とPythonを組み合わせるための素晴らしいツールです。ぜひ試してみてください。
ハイパーパラメータチューニングツールを使う
ハイパーパラメータチューニングツール⁴に時間を投資しましょう。信じてください、それは後で多くの時間を節約するでしょう。
デフォルトのハイパーパラメータ設定で不適切なデータ初期化を行うと、目的のゴールに到達するまでにさらに10~数百エポックのコストがかかることがあります。そのため、すべての実験で時間を無駄にするのではなく、最初に一度だけ時間を無駄にするようにしてください。
自動化するための有名なフレームワークを使用する
上で述べた多くのことを自動化するための素晴らしいライブラリやフレームワークがあります。例えば、Pytorch を使っているのであれば、Pytorch Ignite や Pytorch-lightning のようなライブラリがあり、多くの面倒なタスクを自動化してくれます。
Pytorch-lightning³は、より整理された方法で実験を行うための強力な基盤を与えることができる多くの便利な機能を組み合わせたことで、最近多くの人気を集めています。サードパーティのライブラリをサポートした様々な便利なフックにより、Pytorch-lightning³は最高のMLライブラリの一つとなっています。
一方、Optunaは、計算量の多いモデルでも高度なハイパーパラメータ探索アルゴリズムを実行できるハイパーパラメータチューニング専用フレームワークです。一般的なDeep Learningフレームワークとの統合は非常に簡単で直感的です。
全体像を見る
最後にクリシェ(決まり文句)!
いくつかの標準的なメトリクスとスコアで進捗状況を可視化してみてください。そうすれば、モデルの振る舞いについてより広い視野が得られます。おそらくほとんどの場合、最初からオーバーフィットしているモデルをトレーニングし続けることはなくなるでしょう。
この投稿が、ディープラーニングの実験をより効率的に行うためのお役に立てれば幸いです。この記事についてご質問やご意見がありましたら、お気軽にコメントを残してください。良い一日を。