# 通知設計書 20-SummarySaverHook

## 概要

本ドキュメントは、TensorFlowの`SummarySaverHook`（SessionRunHook）の通知設計について記載する。このフックは、TensorFlowサマリー（メトリクス、ヒストグラム、画像等）を定期的にSummaryWriterへ保存し、TensorBoardでの可視化を可能にする通知機構である。

### 本通知の処理概要

`SummarySaverHook`は、Nステップごとまたは N秒ごとにサマリーオペレーション（`summary_op`）を実行し、その結果を`SummaryWriter`に書き出す。初回実行時にはSessionLog.STARTのセッションログも記録する。トレーニング終了時にはSummaryWriterをflushする。

**業務上の目的・背景**：トレーニング中の各種メトリクス（loss、accuracy、重み分布、勾配ヒストグラム、画像出力等）をTensorBoardで可視化するための基盤を提供する。サマリーの定期保存により、トレーニングの詳細な推移を事後分析でき、モデルの理解と改善に役立つ。

**通知の送信タイミング**：`save_steps`ステップごとまたは`save_secs`秒ごとにサマリーを保存する。`SecondOrStepTimer`によりトリガータイミングが管理される。

**通知の受信者**：TensorBoardを使用する開発者・研究者。SummaryWriterを通じてイベントファイルに書き出され、TensorBoardで可視化される。

**通知内容の概要**：`summary_op`または`scaffold.summary_op`で定義されたサマリー（スカラー値、ヒストグラム、画像等）がイベントファイルに保存される。

**期待されるアクション**：受信者はTensorBoardでサマリーを確認し、トレーニングの進捗や異常を監視する。

## 通知種別

ファイル出力（TensorBoardイベントファイル / SummaryWriter経由）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（before_run/after_runフック内で実行） |
| 優先度 | 中（可視化目的の記録） |
| リトライ | 無し |

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

- `summary_writer`が指定された場合はそれを使用
- 未指定で`output_dir`が指定された場合は`SummaryWriterCache.get(output_dir)`で取得
- `summary_op`が指定された場合はそれを使用、未指定の場合は`scaffold.summary_op`を使用

## 通知テンプレート

### TensorBoardサマリーの場合

| 項目 | 内容 |
|-----|------|
| 出力先 | SummaryWriter（イベントファイル） |
| 形式 | Summaryプロトコルバッファ |

### 本文テンプレート

サマリーの内容は`summary_op`で定義されたオペレーションの出力に依存する。典型的な内容：
- スカラー値（loss、accuracy等）
- ヒストグラム（重み分布、勾配分布等）
- 画像（入力画像、特徴マップ等）
- テキスト

初回セッションログ：
```
SessionLog(status=SessionLog.START)
```

### 添付ファイル

該当なし（イベントファイルへの直接書き出し）

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| summary_op | サマリーオペレーション | コンストラクタ引数 or scaffold.summary_op | Yes |
| global_step | 現在のグローバルステップ | `session.run(_global_step_tensor)` | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| SessionRunHook | `after_run` | 初回（`_next_step is None`）または `timer.should_trigger_for_step(_next_step)` | Nステップ/N秒経過時にサマリーを保存 |
| SessionRunHook | `end` | `_summary_writer`が存在する場合 | SummaryWriterをflush |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| タイマー未トリガー | `should_trigger_for_step`がFalseの場合 |
| summary_writerが未設定 | SummaryWriterがNoneの場合、after_run全体をスキップ（行857-858） |
| summary_opがNone | `_get_summary_op()`がNoneの場合、サマリー要求をスキップ（行850-851） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[begin発火] --> B{summary_writer未設定 AND output_dir指定?}
    B -->|Yes| C[SummaryWriterCache.get]
    B -->|No| D[既存設定を使用]
    C --> E[global_step_tensor取得]
    D --> E
    E --> F[トレーニングループ開始]
    F --> G[before_run発火]
    G --> H{_next_step is None OR timer.should_trigger?}
    H -->|Yes| I{_get_summary_op() != None?}
    H -->|No| J[global_stepのみ要求]
    I -->|Yes| K[global_step + summary_opを要求]
    I -->|No| J
    J --> L[session.run実行]
    K --> L
    L --> M[after_run発火]
    M --> N{summary_writer存在?}
    N -->|No| F
    N -->|Yes| O{_next_step is None?}
    O -->|Yes| P[SessionLog.START記録]
    O -->|No| Q[スキップ]
    P --> R{_request_summary?}
    Q --> R
    R -->|Yes| S[timer更新]
    S --> T{summaryあり?}
    T -->|Yes| U[summary_writer.add_summary]
    T -->|No| V[スキップ]
    R -->|No| W[_next_step更新]
    U --> W
    V --> W
    W --> F
```

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

### 参照テーブル一覧

該当なし

### 更新テーブル一覧

該当なし（イベントファイルへの書き出し）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ValueError | `scaffold`と`summary_op`のどちらも未指定/両方指定 | コンストラクタで例外送出（行823-826） |
| RuntimeError | グローバルステップテンソルが未作成 | `begin()`で例外送出（行840-842） |

### リトライ仕様

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

## 配信設定

### レート制限

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

### 配信時間帯

制限なし

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

- サマリーにはモデルの重み分布や入力データの情報が含まれる可能性がある。イベントファイルの保存先ディレクトリのアクセス制御が必要
- `output_dir`のパスバリデーションは行われない

## 備考

- `summary_op`はリスト形式でも単一テンソルでも受け付ける。`_get_summary_op()`で常にリストに正規化される（行881-898）
- `_next_step`は`global_step + 1`で管理され、次のステップでの事前トリガー判定に使用される
- `end()`では`summary_writer.flush()`のみが実行され、最終ステップのサマリーは`after_run`で既に処理されている
- stale値問題に対応するため、`_request_summary`がTrueの場合に最新の`global_step`を`session.run`で取得する（行863）

---

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

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

### 推奨読解順序

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

Summaryプロトコルバッファとsummary_opの関係を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 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） |

**読解のコツ**: `summary_op`は`tf.summary.merge_all()`などで生成されるテンソルオペレーションであり、実行結果はSummaryプロトコルバッファのシリアライズされた文字列となる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | SummarySaverHook.__init__（行796-832）でタイマー/SummaryWriter/summary_op設定 |

**主要処理フロー**:
1. **行823-826**: scaffold/summary_opの排他チェック
2. **行831-832**: SecondOrStepTimerの生成

#### Step 3: サマリー保存ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | before_run/after_run/end（行844-879）のサマリー保存フロー |
| 3-2 | basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | `_get_summary_op`（行881-898）のサマリーop取得ロジック |

**主要処理フロー**:
- **行844-853**: `before_run` - トリガー判定、summary_opの要求
- **行855-875**: `after_run` - SessionLog記録、サマリー保存、_next_step更新
- **行877-879**: `end` - SummaryWriterのflush
- **行881-898**: `_get_summary_op` - summary_opまたはscaffold.summary_opの取得とリスト正規化

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

```
MonitoredTrainingSession
    │
    ├─ SummarySaverHook.begin() [行835-842]
    │      └─ SummaryWriterCache.get() [行837]
    │
    ├─ [各ステップのループ]
    │      ├─ SummarySaverHook.before_run() [行844-853]
    │      │      ├─ timer.should_trigger_for_step() [行847]
    │      │      └─ _get_summary_op() [行850-851]
    │      │
    │      └─ SummarySaverHook.after_run() [行855-875]
    │             ├─ summary_writer.add_session_log(SessionLog.START) [行866-867] ※初回のみ
    │             ├─ timer.update_last_triggered_step() [行870]
    │             └─ summary_writer.add_summary() [行873] ※サマリーあり時
    │
    └─ SummarySaverHook.end() [行877-879]
           └─ summary_writer.flush() [行879]
```

### データフロー図

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

summary_op               ───▶ before_run()               ───▶ SessionRunArgs
                               │
session.run() results    ───▶ after_run()                ───▶ SummaryWriter (イベントファイル)
                               │                                │
global_step_tensor       ───▶ ├─ SessionLog.START (初回)   ├─ SessionLog記録
                               ├─ timer判定                     └─ サマリー記録
                               └─ add_summary()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | ソース | SummarySaverHookクラス定義（行793-898） |
| 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 |
| event_pb2 | `tensorflow/core/util/event_pb2` | ソース | SessionLogプロトコルバッファ定義 |
