# 通知設計書 19-NanTensorHook

## 概要

本ドキュメントは、TensorFlowの`NanTensorHook`（SessionRunHook）の通知設計について記載する。このフックは、指定されたテンソル（通常はloss）のNaN検出時にトレーニングの停止を通知する異常検知機構である。

### 本通知の処理概要

`NanTensorHook`は、各`session.run()`の実行後にlossテンソルの値を検査し、NaNが含まれている場合にトレーニングを停止させる。`fail_on_nan_loss=True`（デフォルト）の場合は`NanLossDuringTrainingError`例外を発生させ、`False`の場合は`request_stop()`で穏やかに停止する。

**業務上の目的・背景**：モデルトレーニング中にNaN（Not a Number）が発生すると、以降の計算が全て無意味になる。NaN発生を即座に検知して停止することで、計算リソースの浪費を防ぎ、問題の早期発見を促進する。数値的不安定性（学習率過大、勾配爆発等）の診断に不可欠な機構である。

**通知の送信タイミング**：各`session.run()`の実行後（`after_run`フック）で、毎ステップlossテンソルの値を検査する。

**通知の受信者**：(1) `fail_on_nan_loss=True`の場合：例外ハンドリングを行うトレーニングループ、(2) `fail_on_nan_loss=False`の場合：`MonitoredSession`（`request_stop`シグナル）、(3) ログ出力を確認する開発者（`logging.error`または`logging.warning`）。

**通知内容の概要**：`Model diverged with loss = NaN.`というメッセージが出力される。

**期待されるアクション**：受信者はNaN発生の原因を調査し、学習率の低減、データの前処理改善、勾配クリッピングの導入、モデルアーキテクチャの見直しなどを行う。

## 通知種別

複合型：Python例外（`NanLossDuringTrainingError`） / プログラム内部通知（`request_stop`） / Pythonロギング（`logging.error` / `logging.warning`）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（after_runフック内で即時実行） |
| 優先度 | 高（異常検知・トレーニング即時停止） |
| リトライ | 無し（NaN検出は即時対応が必要） |

### 送信先決定ロジック

- `fail_on_nan_loss=True`（デフォルト）：`logging.error` + `NanLossDuringTrainingError`例外送出
- `fail_on_nan_loss=False`：`logging.warning` + `run_context.request_stop()`

## 通知テンプレート

### ログ出力の場合

| 項目 | 内容 |
|-----|------|
| 出力先 | tf_logging（ERROR or WARNINGレベル） |
| 形式 | テキスト |

### 本文テンプレート

```
Model diverged with loss = NaN.
```

例外メッセージ（`NanLossDuringTrainingError.__str__`）:
```
NaN loss during training.
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| loss_tensor | 監視対象のlossテンソル | コンストラクタ引数 | Yes |
| run_values.results | lossテンソルの評価値 | `session.run()`の結果 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| SessionRunHook | `after_run` | `np.isnan(run_values.results)` | lossがNaNの場合にエラー/停止通知 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| lossが正常値 | `np.isnan()`がFalseの場合、通知なし |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[before_run発火] --> B[SessionRunArgsにloss_tensor追加]
    B --> C[session.run実行]
    C --> D[after_run発火]
    D --> E{np.isnan(loss)?}
    E -->|No| F[正常継続]
    E -->|Yes| G{fail_on_nan_loss?}
    G -->|True| H[logging.error出力]
    G -->|False| I[logging.warning出力]
    H --> J[NanLossDuringTrainingError送出]
    I --> K[run_context.request_stop]
    J --> L[トレーニング異常終了]
    K --> M[トレーニング穏やかに停止]
    F --> N[次のステップへ]
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| NanLossDuringTrainingError | lossがNaN AND fail_on_nan_loss=True | RuntimeError派生の例外を送出。トレーニングループが捕捉 |
| request_stop | lossがNaN AND fail_on_nan_loss=False | MonitoredSessionが穏やかに停止 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（NaN検出時は即時停止、リトライなし） |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 頻度 | 毎ステップ（全ステップでlossを検査） |

### 配信時間帯

制限なし

## セキュリティ考慮事項

- loss値のみを検査するため、セキュリティ上の懸念はない

## 備考

- `NanLossDuringTrainingError`は`RuntimeError`を継承し、`__str__`で`"NaN loss during training."`を返す（行754-757）
- `np.isnan()`はスカラー値を前提としており、テンソルが多次元の場合の動作は未定義
- `before_run`でloss_tensorをSessionRunArgsとして渡し、毎ステップ評価する

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

NanLossDuringTrainingErrorの定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | NanLossDuringTrainingError（行754-757）-- RuntimeError派生クラス |

**読解のコツ**: この例外クラスは非常にシンプルで`__str__`メソッドのみを定義する。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | NanTensorHook.__init__（行767-775）でloss_tensorとfail_on_nan_loss設定 |

**主要処理フロー**:
1. **行774**: `_loss_tensor`設定
2. **行775**: `_fail_on_nan_loss`設定（デフォルトTrue）

#### Step 3: NaN検出ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | before_run/after_run（行777-789）のNaN検出フロー |

**主要処理フロー**:
- **行777-778**: `before_run` - loss_tensorをSessionRunArgsとして要求
- **行780-789**: `after_run` - `np.isnan`でNaN判定、fail_on_nan_lossフラグに応じてエラー/停止

### プログラム呼び出し階層図

```
MonitoredTrainingSession
    │
    ├─ [各ステップのループ]
    │      ├─ NanTensorHook.before_run() [行777-778]
    │      │      └─ SessionRunArgs(_loss_tensor)
    │      │
    │      └─ NanTensorHook.after_run() [行780-789]
    │             ├─ np.isnan(run_values.results) [行781]
    │             ├─ [NaN検出時]
    │             │      ├─ fail_on_nan_loss=True:
    │             │      │      ├─ logging.error() [行784]
    │             │      │      └─ raise NanLossDuringTrainingError [行785]
    │             │      └─ fail_on_nan_loss=False:
    │             │             ├─ logging.warning() [行787]
    │             │             └─ run_context.request_stop() [行789]
    │             └─ [正常時] → 次のステップへ
    │
    └─ NanLossDuringTrainingError [行754-757]
           └─ __str__(): "NaN loss during training."
```

### データフロー図

```
[入力]                        [処理]                           [出力]

loss_tensor              ───▶ before_run()               ───▶ SessionRunArgs
                               │
session.run() result     ───▶ after_run()                ───▶ logging.error / warning
                               └─ np.isnan()                  │
                                                              ├─ NanLossDuringTrainingError
                                                              └─ request_stop()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | ソース | NanTensorHookクラス定義（行761-789） |
| basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | ソース | NanLossDuringTrainingError（行754-757） |
| session_run_hook.py | `tensorflow/python/training/session_run_hook.py` | ソース | SessionRunHook基底クラス、SessionRunContext.request_stop |
