ユニットテストはあなたの友達です
作家の中には「すべての文章は書き直しである」という言葉があります。つまり、文章の大部分は改訂されています。プログラマー(または少なくともデータサイエンティスト)にとって、この表現は「すべてのコーディングがデバッグしている」と言い換えることができます。
コードを書いているときはいつでも、意図したとおりに機能することを確認する必要があります。正当性を検証するために私が見つけた最良の方法は、コードを小さなセグメントに分割し、各セグメントが機能することを検証することです。これは、セグメントの出力を正しい答えであることがわかっているものと比較することで実行できます。これはユニットテストと呼ばれます。優れたユニットテストを作成することは、優れた統計学者/データ科学者/機械学習の専門家/ニューラルネットワークの実践者になるための重要な要素です。単に代替品はありません。
ネットワークのパフォーマンスを調整する前に、コードにバグがないことを確認する必要があります!それ以外の場合は、RMS Titanicでデッキチェアを再配置することもできます。
ニューラルネットワークには、検証を他のタイプの機械学習や統計モデルよりもさらに重要にする2つの機能があります。
ニューラルネットワークは、ランダムフォレストまたはロジスティック回帰のように「既製の」アルゴリズムではありません。単純なフィードフォワードネットワークの場合でも、ネットワークの構成、接続、初期化、最適化の方法について多数の決定を行うのはユーザーの責任です。これはコードを書くことを意味し、コードを書くことはデバッグを意味します。
例外を発生させずにニューラルネットワークコードを実行した場合でも、ネットワークにバグがある可能性があります。これらのバグは、ネットワークが訓練する潜伏的な種類でさえあるかもしれませんが、次善の解決策に行き詰まっているか、または結果として得られるネットワークに必要なアーキテクチャがありません。(これは、構文エラーと意味エラーの違いの一例です。)
チェイス・ロバーツによるこの中記事「機械学習コードの単体テスト方法」では、機械学習モデルの単体テストについて詳しく説明しています。この記事からバグのあるコードの例を借りました。
def make_convnet(input_image):
net = slim.conv2d(input_image, 32, [11, 11], scope="conv1_11x11")
net = slim.conv2d(input_image, 64, [5, 5], scope="conv2_5x5")
net = slim.max_pool2d(net, [4, 4], stride=4, scope='pool1')
net = slim.conv2d(input_image, 64, [5, 5], scope="conv3_5x5")
net = slim.conv2d(input_image, 128, [3, 3], scope="conv4_3x3")
net = slim.max_pool2d(net, [2, 2], scope='pool2')
net = slim.conv2d(input_image, 128, [3, 3], scope="conv5_3x3")
net = slim.max_pool2d(net, [2, 2], scope='pool3')
net = slim.conv2d(input_image, 32, [1, 1], scope="conv6_1x1")
return net
エラーが表示されますか?以前の結果は新しい変数で上書きされるため、さまざまな操作の多くは実際には使用されません。ネットワークでこのコードブロックを使用すると、トレーニングが行われ、重みが更新され、損失が減少する可能性もありますが、コードは意図したとおりに動作していません。(著者は一重引用符または二重引用符の使用についても一貫性がありませんが、それは純粋に文体的です。)
ニューラルネットワークに関連する最も一般的なプログラミングエラーは次のとおりです。
- 変数は作成されますが、使用されることはありません(通常はコピーアンドペーストエラーのため)。
- 勾配更新の式は正しくありません。
- 重みの更新は適用されません。
- 損失関数は正しいスケールで測定されません(たとえば、クロスエントロピー損失は確率またはロジットの観点から表現できます)
- 損失はタスクに適切ではありません(たとえば、回帰タスクにカテゴリクロスエントロピー損失を使用する)。
歩く前にクロールします。走る前に歩く
広くて深いニューラルネットワーク、およびエキゾチックな配線を備えたニューラルネットワークは、現在の機械学習のホットなものです。しかし、これらのネットワークは完全に存在するようにはなりませんでした。彼らのデザイナーは、より小さなユニットからそれらを構築しました。最初に、単一の非表示層で小さなネットワークを構築し、それが正しく機能することを確認します。次に、モデルの複雑さを段階的に追加し、それぞれが同様に機能することを確認します。
レイヤー内のニューロンが少な すぎると、ネットワークが学習する表現が制限され、適合不足が発生します。ニューロンが多すぎると、ネットワークがトレーニングデータを「記憶」するため、過剰適合を引き起こす可能性があります。
問題をモデル化するのに必要なニューロンの数が数学的に少ないことを証明できたとしても、「もう少し」ニューロンがあると、オプティマイザーが「適切な」構成を見つけやすくなります。(しかし、これがなぜそうなのか、だれも完全に理解しているとは思いません。)ここでXOR問題のコンテキストでこの例を提供します。。
隠れ層の数を選択すると、ネットワークは生データから抽象化を学習できます。ディープラーニングは最近大流行しており、多数の層を持つネットワークは印象的な結果を示しています。ただし、多くの隠れ層を追加すると、リスクが過剰になり、ネットワークの最適化が非常に難しくなります。
賢いネットワーク配線を選択すると、多くの作業を行うことができます。あなたのデータソースは特殊なネットワークアーキテクチャに対応していますか?畳み込みニューラルネットワークは、「構造化された」データソース、画像または音声データで印象的な結果を達成できます。リカレントニューラルネットワークは、自然言語や時系列データなどのシーケンシャルデータタイプでうまく機能します。残留接続は、ディープフィードフォワードネットワークを改善できます。
ニューラルネットワークトレーニングはロックピッキングに似ています
最先端の、あるいは単に良い結果を得るには、連携して動作するように構成されたすべてのパーツをセットアップする必要があります。実際に学習するニューラルネットワーク構成をセットアップすることは、ロックを選択するようなものです。すべてのピースを適切に並べる必要があります。1つのタンブラーを適切な場所に配置するだけでは不十分であるように、アーキテクチャのみ、またはオプティマイザーのみを正しくセットアップするだけでも十分ではありません。
構成の選択のチューニングは、ある種の構成の選択(学習率など)が他の選択肢(例:ユニット数)よりも重要であると言うほど単純ではありません。選択は他の場所でされた別の選択と組み合わせてうまくいくことができます。
これは、構成オプションの完全なリストであり、正規化オプションや数値最適化オプションでもありません。
これらのトピックはすべて、活発な研究分野です。
ネットワークの初期化は、ニューラルネットワークのバグの原因として見過ごされがちです。大きすぎる間隔での初期化では、初期の重みが大きすぎます。つまり、単一のニューロンがネットワークの動作に大きな影響を与えます。
ニューラルネットワークと回帰モデルの主な違いは、ニューラルネットワークが、活性化関数と呼ばれる多くの非線形関数の合成物であることです。(参照:ニューラルネットワークと線形回帰の本質的な違いは何ですか)
シグモイド活性化関数(ロジスティックまたは関数)に焦点を当てた古典的なニューラルネットワークの結果。最近の結果では、ReLU(または同様の)ユニットは勾配が急であるため、より適切に動作する傾向があり、更新を迅速に適用できることがわかりました。(参照:なぜニューラルネットワークでReLUを使用するのか、どのように使用するのか?)ReLUに関する注意点の1つは、学習が困難になる「デッドニューロン」現象です。漏れのあるrelusおよび類似のバリアントは、この問題を回避します。見るタン
他にも多くのオプションがあります。参照:長所/短所を持つニューラルネットワークの活性化関数の包括的なリスト
残留接続は、ニューラルネットワークのトレーニングを容易にすることができるきちんとした開発です。「画像認識のための深い残留学習」
Kaiming He、Xiangyu Zhang、Shaoqing Ren、Jian Sun In:CVPR。(2016)。さらに、残差ブロック内の操作の順序を変更すると、結果のネットワークをさらに改善できます。「深い残余ネットワークにおけるアイデンティティマッピング」、Kaiming He、Xiangyu Zhang、Shaoqing Ren、およびJian Sun著。
非凸最適化は難しい
ニューラルネットワークの目的関数は、隠れユニットがなく、すべての活性化が線形で、設計行列がフルランクの場合にのみ凸型になります-この構成は通常の回帰問題であるためです。
他のすべての場合、最適化の問題は非凸であり、非凸の最適化は困難です。ニューラルネットワークのトレーニングの課題はよく知られています(「ディープニューラルネットワークのトレーニングが難しいのはなぜですか?」を参照)。さらに、ニューラルネットワークには非常に多数のパラメーターがあるため、1次のメソッドのみに制限されます(参照:機械学習でNewtonのメソッドが広く使用されない理由を参照してください)。これは非常に活発な研究分野です。
学習率の設定が大きすぎると、「キャニオン」の一方から他方に跳躍するため、最適化が発散します。この設定が小さすぎると、実際の進行が妨げられ、SGDに固有のノイズが勾配推定を圧倒する可能性があります。
グラデーションクリッピングは、しきい値を超えるとグラデーションのノルムを再スケーリングします。これは、通常1.0の設定忘れパラメータだと思っていましたが、0.25に設定することでLSTM言語モデルを劇的に改善できることがわかりました。なぜだか分かりません。
学習率のスケジューリングは、トレーニング中に学習率を低下させる可能性があります。私の経験では、スケジューリングの使用は正規表現によく似ています。1つの問題(「特定のエポック後に継続する学習を取得する方法」)を2つの問題(「特定のエポック後に継続する学習を取得する方法) 「」と「良いスケジュールを選択するにはどうすればよいですか」)。他の人々は、スケジューリングが不可欠であると主張しています。決めさせてあげます。
適切なミニバッチサイズを選択すると、間接的に学習プロセスに影響を与える可能性があります。これは、大きなミニバッチは小さなミニバッチよりも分散(大数の法則)が小さくなる傾向があるためです。ミニバッチは、勾配の方向を知るのに十分な大きさでありながら、SGDがネットワークを正規化できるほど小さい必要があります。
確率的勾配降下法には、バニラSGDを改善するために、運動量、適応学習率、Nesterovの更新などを使用する多数のバリアントがあります。より良いオプティマイザーの設計は、非常に活発な研究分野です。いくつかの例:
最初に登場したとき、Adamオプティマイザーは多くの関心を集めました。しかし、最近のいくつかの研究では、勢いのあるSGDがニューラルネットワークの適応勾配法よりも優れていることがわかっています。「機械学習における適応勾配法の限界値」by Ashia C. Wilson、Rebecca Roelofs、Mitchell Stern、Nathan Srebro、Benjamin Recht
しかし、他方では、このごく最近の論文は、適応速度法と勢いのあるSGDとの間のギャップを埋めると思われる新しい適応学習速度オプティマイザを提案しています。「深層ニューラルネットワークのトレーニングにおける適応勾配法の一般化ギャップの解消」by Jinghui Chen、Quanquan Gu
学習勾配を自動的に調整するために履歴勾配情報を採用する適応勾配法は、ディープニューラルネットワークのトレーニングの勢いで確率的勾配降下(SGD)よりも悪いことを一般化することが観察されています。これにより、適応勾配法の一般化のギャップをどのように埋めるかが未解決の問題になります。この作業では、Adam、Amsgradなどの適応勾配法が「過剰適応」される場合があることを示します。部分適応運動量推定法(Padam)と呼ばれる新しいアルゴリズムを設計します。このアルゴリズムは、Adam / AmsgradをSGDと統合して、両方の世界から最高の結果を出します。標準ベンチマークの実験では、PadamがAdam / Amsgradとして高速収束率を維持しながら、ディープニューラルネットワークのトレーニングでSGDを一般化できることを示しています。
正規化
データの規模は、トレーニングに大きな違いをもたらします。
ニューラルネットワークにデータを提示する前に、平均と単位の分散が0になるようにデータを標準化するか、ような短い間隔にデータを配置すると、トレーニングを改善できます。これは事前調整に相当し、ユニットの選択がネットワークの重みに与える影響を取り除きます。たとえば、ミリメートル単位の長さとキロメートル単位の長さはどちらも同じ概念を表しますが、スケールは異なります。データを標準化する方法の正確な詳細は、データの外観によって異なります。[ - 0.5 、0.5 ]
層の正規化は、ニューロンの活性化の移動平均と標準偏差を維持することにより、ネットワークトレーニングを改善できます。これがトレーニングに役立つ理由はよく理解されておらず、研究の活発な分野であり続けています。
正則化
ネットワークの正則化の選択と調整は、一般化が良好なモデル(つまり、トレーニングデータに過剰適合しないモデル)を構築する重要な部分です。ただし、ネットワークが学習データの損失を減らすのに苦労しているとき(ネットワークが学習していないとき)、正則化は問題の内容を曖昧にする可能性があります。
ネットワークが学習しない場合、すべての正規化をオフにして、正規化されていないネットワークが正しく機能することを確認します。次に、各正則化ピースを追加し直し、それらのそれぞれが途中で機能することを確認します。
この戦術は、一部の正則化が不十分に設定される可能性がある場所を正確に特定できます。いくつかの例は
実験のログブックを保持する
ニューラルネットワークをセットアップするとき、パラメーター設定をハードコーディングしません。代わりに、実行時にネットワーク構成の詳細を読み込むために読み取られ、使用される構成ファイル(JSONなど)でそれを行います。これらの構成ファイルはすべて保持しています。パラメーターを変更する場合は、新しい構成ファイルを作成します。最後に、トレーニングと検証のためのエポックごとの損失すべてをコメントとして追加します。
私が古い結果を保持することに執着している理由は、以前の実験に戻って確認することが非常に簡単になるためです。また、同じ行き止まりの実験を誤って繰り返すことを防ぎます。心理的には、「振り返ってみると、このプロジェクトは今日の望みどおりではないかもしれませんが、週間前と比べて進歩しています。」k
例として、私はLSTM言語モデルについて学びたかったので、他のTwitterユーザーに応答して新しいツイートを書き込むTwitterボットを作成することにしました。大学院と仕事の間の自由時間にこれに取り組みました。約1年かかり、150を超えるさまざまなモデルを繰り返してから、私がやりたいと思っていた(意味のある)新しい英語のテキストを生成するモデルに到達しました。(重要なポイントの1つであり、非常に多くの試行を行った理由の一部は、初期の低損失モデルがトレーニングデータを記憶することができたため、単純に低サンプル外損失を得るだけでは不十分であったことです。そのため、プロンプトへの応答としてテキストの密接なブロックをそのまま再現していました。モデルをより自然に、そして低損失にするために微調整が必要でした)