# 通知設計書 18-StepCounterHook

## 概要

本ドキュメントは、TensorFlowの`StepCounterHook`（SessionRunHook）の通知設計について記載する。このフックは、トレーニングのステップ実行速度（steps/sec）を計測し、ログ出力およびSummaryWriterへの記録を通じて通知する機構である。

### 本通知の処理概要

`StepCounterHook`は、Nステップごとまたは N秒ごとにグローバルステップの進行速度（steps/sec）を計算し、`logging.info`でコンソール出力するとともに、`SummaryWriter`にサマリーとして記録する。これによりTensorBoardでの可視化が可能になる。

**業務上の目的・背景**：トレーニングのスループット（steps/sec）をリアルタイムにモニタリングすることで、ハードウェアリソースの利用効率を把握し、ボトルネックの特定やパフォーマンスチューニングの判断材料を提供する。分散トレーニングではワーカーごとの処理速度の比較にも利用される。

**通知の送信タイミング**：`every_n_steps`ステップごと（デフォルト100）または`every_n_secs`秒ごとに計測と通知を実行する。`SecondOrStepTimer`によりトリガータイミングが管理される。

**通知の受信者**：(1) ログ出力を確認する開発者・運用者（`logging.info`経由）、(2) TensorBoardを使用する分析者（SummaryWriter経由）。

**通知内容の概要**：`global_step/sec`形式のサマリータグ名とsteps/secの値が出力される。

**期待されるアクション**：受信者はトレーニングのスループットを監視し、異常な低下が見られた場合にデータパイプライン、ハードウェア、ネットワーク等の問題を調査する。

## 通知種別

複合型：Pythonロギング（`logging.info`） / TensorBoardサマリー（`Summary`プロトコル）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（after_runフック内で即時実行） |
| 優先度 | 低（パフォーマンスモニタリング目的） |
| リトライ | 無し |

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

- `summary_writer`が指定された場合はそれを使用
- 未指定で`output_dir`が指定された場合は`SummaryWriterCache.get(output_dir)`で取得
- どちらもない場合はログ出力のみ

## 通知テンプレート

### ログ出力の場合

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

### 本文テンプレート

ログ出力:
```
{summary_tag}: {steps_per_sec}
```

例:
```
global_step/sec: 125.3
```

サマリー:
```
Summary.Value(tag="global_step/sec", simple_value=steps_per_sec)
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| summary_tag | サマリータグ名 | `get_global_step().op.name + "/sec"` | Yes |
| steps_per_sec | ステップ/秒 | `elapsed_steps / elapsed_time` | Yes |
| global_step | 現在のグローバルステップ | `session.run(_global_step_tensor)` | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| SessionRunHook | `after_run` | `timer.should_trigger_for_step(global_step)` AND `elapsed_time is not None` | Nステップ/N秒経過時にsteps/secをログ+サマリー出力 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| タイマー未トリガー | `should_trigger_for_step`がFalseの場合 |
| 初回トリガー | `elapsed_time is None`（初回はベースライン設定のみで出力なし） |
| global_step未増加 | `stale_global_step == _last_global_step`の場合、警告を5回まで出力（行742-748） |

## 処理フロー

### 送信フロー

```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[summary_tag設定]
    F --> G[トレーニングループ開始]
    G --> H[before_run: global_step要求]
    H --> I[session.run実行]
    I --> J[after_run発火]
    J --> K{timer.should_trigger?（stale + steps_per_run）}
    K -->|No| L{global_step未増加?}
    K -->|Yes| M[session.runで最新global_step取得]
    M --> N{timer.should_trigger?（最新値）}
    N -->|No| L
    N -->|Yes| O[timer.update_last_triggered_step]
    O --> P{elapsed_time != None?}
    P -->|No| L
    P -->|Yes| Q[_log_and_record]
    Q --> R[steps_per_sec計算]
    R --> S{summary_writer存在?}
    S -->|Yes| T[Summary追加]
    S -->|No| U[スキップ]
    T --> V[logging.info出力]
    U --> V
    V --> L
    L -->|Yes| W[logging.warn出力（最大5回）]
    L -->|No| X[_last_global_step更新]
    W --> X
    X --> G
```

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

### 参照テーブル一覧

該当なし

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ValueError | `every_n_steps`と`every_n_secs`のどちらも未指定/両方指定 | コンストラクタで例外送出（行683-685） |
| RuntimeError | グローバルステップテンソルが未作成 | `begin()`で例外送出（行701-703） |
| 警告 | グローバルステップが増加していない | `logging.log_first_n`で最大5回警告（行742-748） |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| ステップ単位 | `every_n_steps`で指定（デフォルト100） |
| 時間単位 | `every_n_secs`で指定 |

### 配信時間帯

制限なし

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

- steps/secの値のみを出力するため、セキュリティ上の懸念はない

## 備考

- `_steps_per_run`は`_set_steps_per_run`メソッドで外部から設定可能（Estimatorから呼ばれる）
- stale値による二重チェック機構あり（行722-726）
- global_stepが増加しない場合（SyncReplicaOptimizer等の仕様）に対する警告メッセージあり
- `summary_tag`は`global_step`オペレーション名 + `/sec`で自動生成される（行704）

---

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

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

### 推奨読解順序

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

Summary プロトコルバッファと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） |

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

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

**主要処理フロー**:
1. **行683-685**: every_n_steps/every_n_secsの排他チェック
2. **行686-687**: SecondOrStepTimer生成
3. **行689-691**: summary_writer/output_dir設定

#### Step 3: 計測・通知ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | begin/before_run/after_run（行697-750）の計測フロー |

**主要処理フロー**:
- **行697-704**: `begin` - SummaryWriter取得、global_step_tensor取得、summary_tag設定
- **行706-707**: `before_run` - global_step_tensorの要求
- **行709-716**: `_log_and_record` - steps/sec計算、Summary追加、logging.info
- **行718-750**: `after_run` - タイマー判定、二重チェック、グローバルステップ停滞警告

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

```
MonitoredTrainingSession
    │
    ├─ StepCounterHook.begin() [行697-704]
    │      ├─ SummaryWriterCache.get() [行699]
    │      └─ training_util.get_global_step().op.name + "/sec" [行704]
    │
    ├─ [各ステップのループ]
    │      ├─ StepCounterHook.before_run() [行706-707]
    │      │      └─ SessionRunArgs(_global_step_tensor)
    │      │
    │      └─ StepCounterHook.after_run() [行718-750]
    │             ├─ timer.should_trigger_for_step() [行722-726]
    │             ├─ timer.update_last_triggered_step() [行727-728]
    │             └─ _log_and_record() [行709-716]
    │                    ├─ Summary(value=[Summary.Value(tag, simple_value)]) [行712-713]
    │                    ├─ summary_writer.add_summary() [行715]
    │                    └─ logging.info() [行716]
    │
    └─ [global_step未増加時]
           └─ logging.log_first_n(WARN, ..., 5) [行742-748]
```

### データフロー図

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

global_step_tensor         ───▶ after_run()                 ───▶ logging.info (steps/sec)
                                │
timer (elapsed_time/steps) ───▶ ├─ should_trigger判定       ───▶ Summary (TensorBoard)
                                ├─ update_last_triggered
                                └─ _log_and_record()
                                       └─ steps/sec = elapsed_steps / elapsed_time
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| basic_session_run_hooks.py | `tensorflow/python/training/basic_session_run_hooks.py` | ソース | StepCounterHookクラス定義（行674-750） |
| 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 |
| summary_pb2 | `tensorflow/core/framework/summary_pb2` | ソース | Summaryプロトコルバッファ定義 |
