この記事は「AIエンジニアが落とし穴に陥った記録」シリーズの第4回目です。トレーニングは順調に進みましたが、数百ステップ後に損失が突然 NaN になりました。再スタート後、位置を変えて再び墜落した。この記事では、実機を使用して 2 つの古典的な NaN シーンを再現し、次にフォワード フックを使用して「どのステップ、どの層、どの値が最初にオーバーフローするか」を特定する方法を説明します。
この記事をサポートする nan_doctor 位置決めスクリプト (フック層ごとの監視 + 2 つのクラッシュ ケース) は、公開番号 Z に従っています。
損失が NaN になることの特徴は、何の説明も与えないことです。
トレーニング カーブはずっと正常ですが、ある瞬間に NaN に直接ジャンプします。再起動して再度実行しても、クラッシュの場所はまだ異なります。インターネットで調べてみると、答えはすべて「学習率を下げる」です。どのくらい小さいのか、なぜなのか、学習率以外に何が可能なのかについては誰も答えませんでした。
この記事ではこの3つを一気にわかりやすく解説します。プロセス全体は、私の GTX 1660 Ti ラップトップで実際に再現されています。 5829 パラメータを持つ小規模なモデルで十分であり、GPU は必要ありません。
1. 完全な衝突シーンを最初に見てみる
私は 100 という法外な学習率の SGD を使用し、3 層の小規模ネットワークを実行しました。実際の出力、ステップカットなし。

5 つのステップ、完全な死のタイムライン。 0.2027、正常。 27,000、良くない。 2兆5,500億というのはもう恐ろしいですね。ステップ4以降5 番目のステップ NaN は完全に強制終了されました。
このスクリーンショットの最も重要な部分は、最後の 3 行です。
最初のステップでは、NaN がステップ 5 に表示されます。2 番目のステップでは、前のステップ (ステップ 4) での損失が最初に早期警告信号である inf にオーバーフローします。 3番目のステップはフックの位置決めです。 5 番目のステップでは、inf がlayer0 の出力に初めて表示されます。
2. inf は煙警報器を意味し、NaN は家が全焼したことを意味します。
多くの人は inf と NaN を同じものだと考えていますが、実際には、これらは事故連鎖の 2 つの段階です。
loss は inf に変化し、ある計算結果が float32 の上限値 (約 3.4×10^38) を超えたことを示します。現時点では、ネットワークは完全に死んだわけではなく、価値はまだ「存在」していますが、生き残るには大きすぎます。
次のステップでは、重みにはすでに inf が含まれています。順伝播では、inf を使用して、inf から inf を引いたものと、ゼロに inf を乗算した演算を実行します。浮動小数点の規則では、これらの演算の結果が常に NaN になると規定されています。この瞬間から、値は「数値ではない」ようになり、汚染はネットワークに沿って層ごとに広がり、二度と戻ることはありません。
したがって、NaN のトラブルシューティングの第一原則は、inf が表示されるステップに焦点を当てることです。煙警報器です。警報器が鳴ったとき、火災は始まったばかりです。 NaN が現れるまでに、家はすでに全焼していました。
3. 階層ごとに位置決めフックを取り付け、各階層にドライブレコーダーを設置します。
どのステップがわかったところで、さらにどのレベルかを知りたいと思います。この方法は、PyTorch のフォワード フックを使用して、各線形レイヤーにレコーダーを接続することです。各順伝播ステップ中に、この層の出力の最小値と最大値、および inf または NaN があるかどうかを記録します。
上のスクリーンショットの位置決め出力に戻ると、ステップ 5 で、layer0 の出力が初めて inf に表示されます。とても有益です。オーバーフローは深い層から始まるのではなく、最初の層で爆発し、重みは学習率によって押しのけられます。修正する必要があるのはモデル構造ではなく学習率です。
この一連の監視の完全な実装については、リソース パッケージの第 4 章に記載されています。各レイヤーのフックの登録に必要なコードは 3 行だけで、配置後に削除できます。
4. 2 番目の古典的な NaN、手書き損失の log(0)
大きすぎる学習率は、NaN の原因の 1 つにすぎません。もう 1 つのタイプの高リスク領域は、カスタム損失関数です。 2番目の実際のケースを見てみましょう。

手書きのクロスエントロピー、ソフトマックス直後のログ。 logits がわずかに大きい場合、softmax 出力には正確な 0 が表示され、log(0) は負の無限大に等しく、浮動小数点規則では、0 に負の無限大を乗じた値は直接 NaN になります。最初のステップは、何の訓練も受けずに戦闘で死ぬことです。
このケースでややこしいのは、学習率とはまったく関係がなく、パラメーターを永遠に調整しても無駄であるということです。正しい書き込み方法は 2 つあり、log_softmax を安定した値で使用する (推奨) か、ログ内の値に下限保護を追加するです。
5. 比較を修復します。違いは 1 桁だけです
学習率を 100 から 0.05 に下げ、他の単語は変更しません。実際の結果。

200 ステップで、損失は 0.2 から 0.0100 まで着実に減少し、トレーニング精度は 1.000 でした。 2 つの曲線を一緒に見るとより直感的です。

同じモデル、同じデータでは、曲線は 5 ステップで空に上昇し、曲線は 200 ステップで収束しますが、その間の学習率は 1 桁しかありません (実際の違いは 2000 倍です)。
6. FAQ 3
1. インターネット上の人々は学習率を下げるべきだと言います。どれくらい低くすればいいのでしょうか?
推測しないで、二分法を使用してください。現在の値を 10 で除算し、一度実行してから、再度 10 で除算します。基本的には2回以内に安定範囲の上端を見つけることができます。原理はとてもシンプルです。単一ステップの更新量が現在の地形で許可されている最大ステップ サイズを超える場合、ウェイトは押しのけられます。学習率によってこのステップ サイズが制御されます。
2. 学習率と log(0) 以外に、NaN の一般的なソースにはどのようなものがありますか?
実際に遭遇する頻度をランキングにしました。除算または正規化の分母は 0 である場合があります。平方根または対数への入力は負または 0 である場合があります。混合精度トレーニングでは、fp16 の上限は 65504 にすぎず、fp32 よりもはるかにオーバーフローしやすくなります。グラデーションは常に蓄積され、クリップされることはありません。データ自体には、inf または非常に大きな外れ値が混在しています。物事が起こる最も一般的な場所である損失関数から始めて、それらを 1 つずつ整理します。
3. フックのモニタリングによりトレーニングの速度が低下しますか?
前方フックは各ステップでさらにいくつかの統計を計算するだけであり、オーバーヘッドは無視できます。私の習慣は、位置を決めたら外すことです。ただし、実稼働トレーニング スクリプトに軽量チェッカーを永続的にインストールすることには価値があります。損失が NaN であるかどうかは、各ステップで 1 回だけチェックされます。シーンを保存して停止します。 2時間走っただけで無駄だったということになるよりはマシです。
7. この記事の概要
- NaN が表示される前に、inf 警告が発生する可能性が高くなります。足元から目を離さず、家が全焼するのを待ってはいけません。
- フォワードフックはレイヤーごとに監視し、どのレイヤーが最初にオーバーフローするかを特定します。
- NaN の最も一般的な原因は、過剰な学習率とカスタム損失の対数、除算、平方根です。
この記事のピットカード
| 分野 | コンテンツ |
|---|---|
| チート名 | 学習率が大きすぎると MSE 損失がオーバーフローし、損失は 5 ステップ以内で 0.2 から NaN まで変化します。 |
| 現象テキスト | ステップ 4 の損失 = inf、ステップ 5 の損失 = nan |
| 環境 | Win11、Python 3.12、torch 2.9.1+cu126(CPU再現)、5829パラメータ3層MLP |
| 再現手順 | SGD lr=100 勢い=0.9 → ステップ 2 27,000 → ステップ 3 2.55 兆 → ステップ 4 inf → ステップ 5 NaN |
| 根本的な原因 | MSE 勾配は出力とともに無制限に増加し、ステップ サイズは曲率の上限を超えます。重みがオーバーフローした後、inf を介して NaN に伝導されます。 |
| 解決 | 学習率を一桁下げて、バイナリのトラブルシューティングから始めます。フォワードフックを使用して各レイヤーの出力範囲を記録し、最初のオーバーフローレイヤーを見つけます。 |
| 落とし穴を避けるためのヒント | カスタム損失の場合は、log_softmax が推奨されます。トレーニング スクリプトには軽量の NaN チェッカーが常駐しています。 |
| 信頼性 | ネイティブ認証 |
| バージョンのタイムスタンプ | 2026-11-04、トーチ 2.9.1+cu126 |
| 関連ピット | 第 3 条: ラベルのずれ (データとトレーニングの安定性トラックの両方) |
列ナビゲーション
| 章 | テーマ | 州 |
|---|---|---|
| 1 | nvidia-smiで表示されるCUDAのバージョンはインストールしたものですか? | ✅ |
| 2 | torch.cuda.is_available() は常に False で、1 つのコマンドでブレークポイントを見つけます。 | ✅ |
| 3 | ラベルと画像が一致していないため、モデルはノイズを学習し続けます | ✅ |
| 4 | 損失は NaN になり、フックを使用してレイヤーごとに配置します | ✅(この記事) |
| 5 | DataLoader が最初のバッチでスタックする、Windows マルチプロセスの落とし穴 | 予定されている |
| 6 | CUDAを2セットインストールしたのに接続できない、PATHと優先順位の真実 | 予定されている |
| 7 | データ漏洩: パーティショニングの前に前処理が行われます | 予定されている |
| 8 | RAG の回答が半分になったらコンパイルを開始し、取得しきい値と拒否メカニズムを実際に操作します。 | 予定されている |
| 9 | ランダムなシードが設定されていますが、結果はまだ再現できません。 | 予定されている |
| 10 | 類似度はすべて 0.9: ベクトルは正規化されていません | 予定されている |
| 11 | 混合精度が有効な場合、NaN が表示されます。 GradScaler は一体何をしているのでしょうか? | 予定されている |
| 12 | 損失は下がらず、むしろ増加します。まずモデルの構造を疑ってはいけません。 | 予定されている |
| 13 | トレーニングは削減されますが、検証は削減されません。最初にデータを確認してから、規則性について話します。 | 予定されている |
注: カーネル イメージなし (新しいアーキテクチャのグラフィック カードが必要)、WSL2 ドライバーのインストール、docker –gpus。 3 つの質問は、このマシンでの実際の再現をサポートしていません。 「切れなければ質問を変える」の原則に従い、条件が整うまで延期させていただきます。セカンド シーズンの残りのトピック (top-k 変曲点、Tokenizer の切り捨て、ONNX の非互換性など) は順次更新されます。
注: カーネル イメージなし (新しいアーキテクチャのグラフィック カードが必要)、WSL2 ドライバーのインストール、docker –gpus。 3 つの質問は、このマシンでの実際の再現をサポートしていません。 「切れなければ質問を変える」の原則に従い、条件が整うまで延期させていただきます。
次の記事のプレビュー「利用可能なカーネル イメージがありません。グラフィックス カードのコンピューティング能力とコンパイル ターゲットが一致していません。」新しいグラフィックス カードに変更しましたが、コードは 1 行も変更しませんでした。このエラーはトレーニング中に直接報告されました。コンパイル時にフレームワークにはグラフィック カードが含まれないため、ドライバーの再インストールと CUDA の再インストールは役に立ちません。次の記事では、コンピューティング能力の表と比較して適切なバージョンを選択する方法について説明します。
著作権表示: この記事のクラッシュ シーンとカーブはすべて私の実機から再現したものです。このコラムの読者はその使い方を学ぶことができます。全文の転売・譲渡を禁止します。