# 通知設計書 16-CheckpointSaverHook

## 概要

本ドキュメントは、TensorFlowの`CheckpointSaverHook`（SessionRunHook）の通知設計について記載する。このフックは、トレーニング中に定期的にチェックポイント（モデルの重み等）を保存し、保存イベントの前後に`CheckpointSaverListener`への通知を行い、また保存ログをSummaryWriterに記録する機構である。

### 本通知の処理概要

`CheckpointSaverHook`は、Nステップごとまたは N秒ごとにチェックポイントを保存する。保存の前後に登録されたリスナー（`CheckpointSaverListener`）のコールバックを発火し、保存完了時にはSummaryWriterへSessionLogを記録する。リスナーの`after_save`がTrueを返した場合、トレーニングの停止を要求する機能も持つ。

**業務上の目的・背景**：長時間のトレーニングにおいて障害発生時にゼロからの再開を避けるため、定期的にモデル状態を保存する。また、保存イベントを外部のリスナーに通知することで、チェックポイントに連動したカスタム処理（評価実行、モデルデプロイ等）を可能にする。

**通知の送信タイミング**：`SecondOrStepTimer`により`save_steps`ステップごとまたは`save_secs`秒ごとにトリガーされる。セッション作成直後（初回保存）とトレーニング終了時（最終保存）にも実行される。

**通知の受信者**：(1) `CheckpointSaverListener`リスト -- `before_save`/`after_save`コールバックを受信、(2) `SummaryWriter` -- SessionLogを受信、(3) ログ出力 -- `logging.info`による保存ステータスメッセージ。

**通知内容の概要**：保存対象のグローバルステップ番号と保存先パスがログに出力される。リスナーにはセッションオブジェクトとグローバルステップ値が渡される。

**期待されるアクション**：リスナーは保存イベントに応じてカスタム処理（モデル評価、外部通知等）を実行する。運用者はログを確認してチェックポイント保存の正常性を監視する。

## 通知種別

複合型：Pythonロギング（`logging.info`） / リスナーコールバック（`CheckpointSaverListener`） / SummaryWriter（SessionLog）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（after_runフック内およびend内で即時実行） |
| 優先度 | 高（チェックポイント保存はトレーニングの継続性に直結） |
| リトライ | 無し（保存失敗時のリトライ機構なし） |

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

- コンストラクタの`listeners`引数で`CheckpointSaverListener`のリストを受け取る
- `SummaryWriterCache.get(checkpoint_dir)`でSummaryWriterを取得
- `logging.info`でコンソール/ログファイルへ出力

## 通知テンプレート

### ログ出力の場合

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

### 本文テンプレート

リスナー呼び出し前:
```
Calling checkpoint listeners before saving checkpoint {step}...
```

保存実行時:
```
Saving checkpoints for {step} into {save_path}.
```

リスナー呼び出し後:
```
Calling checkpoint listeners after saving checkpoint {step}...
```

リスナー停止要求時:
```
A CheckpointSaverListener requested that training be stopped. listener: {listener}
```

初期化時:
```
Create CheckpointSaverHook.
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| model.ckpt-{step} | チェックポイント | 常に | モデルの重みとオプティマイザ状態 |
| model.ckpt-{step}.meta | MetaGraphDef | save_graph_def=True | グラフ定義のメタ情報 |
| graph.pbtxt | GraphDef | save_graph_def=True（初回のみ） | テキスト形式のグラフ定義 |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| step | 現在のグローバルステップ | `session.run(_global_step_tensor)` | Yes |
| save_path | チェックポイント保存先パス | `os.path.join(checkpoint_dir, checkpoint_basename)` | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| SessionRunHook | `after_create_session` | 常に | 初回チェックポイント保存 + グラフ/メタグラフのSummaryWriter書き出し |
| SessionRunHook | `after_run` | `timer.should_trigger_for_step(global_step)` | Nステップ/N秒経過時に保存 |
| SessionRunHook | `end` | `last_step != timer.last_triggered_step()` | 最終ステップがまだ保存されていない場合に保存 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| タイマー未トリガー | `should_trigger_for_step`がFalseの場合、保存をスキップ |
| stale値チェック | stale_global_step + steps_per_runでの事前判定で不要と判断された場合（行609-610） |
| 最終ステップが保存済み | `end()`で`last_step == timer.last_triggered_step()`の場合、重複保存を回避 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[begin発火] --> B[SummaryWriter取得]
    B --> C[リスナーのbegin呼出]
    C --> D[after_create_session発火]
    D --> E{save_graph_def?}
    E -->|Yes| F[graph.pbtxt書き出し]
    E -->|No| G[スキップ]
    F --> H[MetaGraphDef作成]
    G --> H
    H --> I[SummaryWriterにgraph/meta_graph追加]
    I --> J[初回チェックポイント保存]
    J --> K[タイマー更新]
    K --> L[トレーニングループ開始]
    L --> M[before_run: global_step要求]
    M --> N[session.run実行]
    N --> O[after_run発火]
    O --> P{timer.should_trigger?}
    P -->|No| L
    P -->|Yes| Q[_save実行]
    Q --> R[リスナーbefore_save呼出]
    R --> S[Saver.save実行]
    S --> T[SessionLog記録]
    T --> U[リスナーafter_save呼出]
    U --> V{should_stop?}
    V -->|Yes| W[request_stop]
    V -->|No| L
    W --> X[end発火]
    X --> Y{未保存ステップあり?}
    Y -->|Yes| Z[最終保存]
    Y -->|No| AA[リスナーend呼出]
    Z --> AA
    AA --> AB[終了]
```

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

### 参照テーブル一覧

該当なし（データベースを使用しない）

### 更新テーブル一覧

該当なし（ファイルシステムへのチェックポイント書き出し）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ValueError | `save_steps`と`save_secs`のどちらも未指定/両方指定 | SecondOrStepTimerのコンストラクタで例外送出 |
| ValueError | `saver`と`scaffold`の両方を指定 | コンストラクタで例外送出（行558-559） |
| RuntimeError | グローバルステップテンソルが未作成 | `begin()`で例外送出（行579-581） |
| RuntimeError | SAVERSコレクションが空/複数 | `_get_saver()`で例外送出（行660-667） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし） |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| ステップ単位 | `save_steps`で指定 |
| 時間単位 | `save_secs`で指定 |

### 配信時間帯

制限なし

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

- チェックポイントファイルにはモデルの重みが含まれるため、機密性の高いモデルでは保存先ディレクトリのアクセス制御が必要
- `checkpoint_dir`のパスバリデーションは行われない

## 備考

- `_steps_per_run`はデフォルト1000000に設定され、Estimator使用時に`_set_steps_per_run`で調整される（行570-574）
- `checkpoint_basename`のデフォルトは`model.ckpt`（行532）
- `save_graph_def=True`の場合、`after_create_session`でGraphDefとMetaGraphDefが書き出される
- リスナーの`after_save`がTrueを返すとトレーニング停止を要求する機能があり、分散トレーニングではchiefのみで使用すべき

---

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

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

### 推奨読解順序

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

チェックポイント保存に関連するデータ構造（Saver、SessionLog、SummaryWriter）を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | session_run_hook.py | `tensorflow/python/training/session_run_hook.py` | SessionRunHook基底クラス（行94-182） |
| 1-2 | basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | SecondOrStepTimer（行86-150） |

**読解のコツ**: CheckpointSaverHookはSecondOrStepTimerを使用して定期的な保存を制御する。リスナーパターンでbefore_save/after_saveを提供する。

#### Step 2: コンストラクタを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | CheckpointSaverHook.__init__（行527-571）でタイマー/リスナー/パス設定 |

**主要処理フロー**:
1. **行557-559**: saver/scaffoldの排他チェック
2. **行564-565**: SecondOrStepTimerの生成
3. **行566**: リスナーリストの設定

#### Step 3: 保存処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | `_save`メソッド（行625-648）のリスナー通知と保存フロー |

**主要処理フロー**:
- **行627-630**: リスナーのbefore_save呼び出し
- **行632-634**: Saver.save実行
- **行635-638**: SummaryWriterへSessionLog記録
- **行641-648**: リスナーのafter_save呼び出しとshould_stop判定

#### Step 4: ライフサイクルフローを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | begin/after_create_session/after_run/end（行576-623）の一連のフロー |

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

```
MonitoredTrainingSession
    │
    ├─ CheckpointSaverHook.begin() [行576-583]
    │      ├─ SummaryWriterCache.get() [行577]
    │      └─ listeners[].begin() [行582-583]
    │
    ├─ CheckpointSaverHook.after_create_session() [行585-602]
    │      ├─ training_util.write_graph() [行591-593]
    │      ├─ meta_graph.create_meta_graph_def() [行596-597]
    │      ├─ summary_writer.add_graph/add_meta_graph() [行598-599]
    │      └─ _save() [行601]
    │
    ├─ [各ステップのループ]
    │      ├─ CheckpointSaverHook.before_run() [行604-605]
    │      └─ CheckpointSaverHook.after_run() [行607-616]
    │             └─ _save() [行615]
    │                    ├─ listeners[].before_save() [行629-630]
    │                    ├─ saver.save() [行633-634]
    │                    ├─ summary_writer.add_session_log() [行635-638]
    │                    └─ listeners[].after_save() [行642-648]
    │
    └─ CheckpointSaverHook.end() [行618-623]
           ├─ _save() [行621] ※最終保存
           └─ listeners[].end() [行622-623]
```

### データフロー図

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

global_step_tensor         ───▶ after_run()                 ───▶ チェックポイントファイル
checkpoint_dir             ───▶ _save()                     ───▶ graph.pbtxt / .meta
saver (model weights)      ───▶ ├─ listeners.before_save()  ───▶ SummaryWriter (SessionLog)
listeners                  ───▶ ├─ saver.save()             ───▶ logging.info (ログ出力)
                                ├─ summary_writer.add_log()
                                └─ listeners.after_save()   ───▶ request_stop (条件付き)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | ソース | CheckpointSaverHookクラス定義（行524-670） |
| basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | ソース | CheckpointSaverListenerクラス定義（行457-520） |
| basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | ソース | SecondOrStepTimer（行86-150） |
| session_run_hook.py | `tensorflow/python/training/session_run_hook.py` | ソース | SessionRunHook基底クラス |
| summary_io.py | `tensorflow/python/training/summary_io.py` | ソース | SummaryWriterCache |
