# 機能設計書 19-ドロップアウト（Dropout）

## 概要

本ドキュメントは、TensorFlowにおけるドロップアウト（Dropout）正則化機能に関する設計書である。`tf.nn.dropout`関数とその内部実装`_dropout`の仕様、Stateless版、Keras Dropoutレイヤーとの関係を詳述する。

### 本機能の処理概要

ドロップアウト機能は、ニューラルネットワークの過学習を防止するための正則化手法を提供する。訓練時に入力テンソルの各要素を確率`rate`でゼロに設定し、残りの要素を`1/(1-rate)`でスケールアップすることで、期待値を維持する。

**業務上の目的・背景**：深層ニューラルネットワークは大量のパラメータを持つため過学習のリスクが高い。ドロップアウトは各ニューロンの出力をランダムに無効化することで、ネットワークが特定のニューロンの組み合わせに過度に依存することを防ぎ、汎化性能を向上させる。

**機能の利用シーン**：全結合層間のドロップアウト、RNNの再帰接続でのドロップアウト、CNNの特徴マップでの空間ドロップアウト（noise_shape利用）、Transformerの注意重みドロップアウト。

**主要な処理内容**：
1. `tf.nn.dropout(x, rate)` - V2 API（rate指定）
2. `tf.nn.dropout(x, keep_prob=)` - V1 API（keep_prob指定、非推奨）
3. `tf.nn.experimental.stateless_dropout` - 状態なしドロップアウト
4. noise_shapeによるブロードキャストドロップアウト
5. 高速パス: rate=0の場合はドロップアウトをスキップ

**関連システム・外部連携**：乱数生成器（random_ops）、Keras Dropoutレイヤー。

**権限による制御**：特になし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能はモデル内部で使用される正則化処理であり、画面に直接関連しない |

## 機能種別

計算処理（正則化）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| x | tf.Tensor (float) | Yes | 入力テンソル | 浮動小数点型であること |
| rate | float/Tensor | Yes（V2） | ドロップ確率 [0, 1) | 0以上1未満 |
| keep_prob | float | No（V1、非推奨） | 保持確率 (1-rate) | rate への変換 |
| noise_shape | Tensor/list (int) | No | ドロップマスクの形状 | xの形状にブロードキャスト可能 |
| seed | int | No | 乱数シード | Python整数 |
| name | string | No | 演算名 | - |
| rng_alg | string | No（stateless版） | 乱数アルゴリズム | 有効なRNGアルゴリズム |

### 入力データソース

前層からのテンソル出力。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| output | tf.Tensor | ドロップアウト適用後のテンソル（入力と同形状・同dtype） |

### 出力先

次層への入力テンソル。

## 処理フロー

### 処理シーケンス

```
1. tf.nn.dropout(x, rate) 呼び出し
   └─ dropout_v2 -> _dropout 内部実装に委譲
2. 入力検証
   └─ rateが[0,1)の範囲内か
   └─ xが浮動小数点型か
3. 高速パス判定
   └─ rate==0 の場合: dummy_rng_step() 後にxをそのまま返却
4. スケーリング計算
   └─ scale = 1 / (1 - rate)
   └─ ret = x * scale
5. ドロップマスク生成
   └─ noise_shape解決（ブロードキャスト形状）
   └─ random_tensor = uniform_sampler(noise_shape)
   └─ keep_mask = random_tensor >= rate
6. マスク適用
   └─ ret = where(keep_mask, ret, 0)
7. 結果返却
```

### フローチャート

```mermaid
flowchart TD
    A[tf.nn.dropout 呼び出し] --> B[入力検証]
    B --> C{rate == 0?}
    C -->|Yes| D[dummy_rng_step]
    D --> E[x をそのまま返却]
    C -->|No| F[scale = 1/(1-rate)]
    F --> G[ret = x * scale]
    G --> H[noise_shape 解決]
    H --> I[random_tensor 生成]
    I --> J[keep_mask = random >= rate]
    J --> K[ret = where keep_mask, ret, 0]
    K --> L[結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 期待値保持 | ドロップされなかった要素を1/(1-rate)でスケールアップ | 全ドロップアウト |
| BR-02 | rate=1禁止 | rate=1（全ドロップ）は許可されない | ValueError |
| BR-03 | rate=0高速パス | rate=0の場合は乱数生成をスキップ（ダミーRNGステップあり） | rate==0 |
| BR-04 | noise_shapeブロードキャスト | noise_shapeをxの形状にブロードキャストしてマスク生成 | noise_shape指定時 |
| BR-05 | V1互換 | keep_probはrateに変換（rate = 1 - keep_prob） | V1 API使用時 |
| BR-06 | 浮動小数点制約 | 入力xは浮動小数点型でなければならない | 全ドロップアウト |

### 計算ロジック

- スケーリング: `output = x * (1 / (1 - rate))` (保持された要素)
- マスク生成: `keep_mask = uniform_random(noise_shape) >= rate`
- 最終出力: `output = where(keep_mask, scaled_x, 0)`

## データベース操作仕様

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ValueError | rate範囲外 | rateが[0,1)の範囲外 | 0以上1未満の値を指定 |
| ValueError | 非浮動小数点入力 | xが浮動小数点型でない | float型のテンソルを使用 |
| ValueError | rateがスカラーでない | rate Tensorが0次元でない | スカラーテンソルを使用 |
| ValueError | 型不互換 | xとrateのdtypeが互換でない | 同じdtypeを使用 |
| ValueError | keep_prob不正 | keep_probがfloat/Tensorでない | 正しい型を使用 |

### リトライ仕様

該当なし。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- rate=0の高速パスにより、推論時のドロップアウト無効化が効率的
- noise_shapeの利用により、チャネル単位ドロップアウトがブロードキャストで効率化
- stateless版はRNG状態管理のオーバーヘッドを回避

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

- 乱数シードの固定は再現性のために重要だが、セキュリティ目的での乱数使用には適さない

## 備考

- `tf.nn.experimental.stateless_dropout`が推奨（状態管理の問題回避）
- Keras `tf.keras.layers.Dropout`は訓練時のみドロップアウトを適用するラッパー
- TF1からTF2への移行時、keep_probからrateへのパラメータ変更に注意

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | nn_ops.py | `tensorflow/python/ops/nn_ops.py` | dropout関連の型定義と定数 |

**読解のコツ**: ドロップアウトの実装はPure Pythonであり、C++ Opとしての登録は行われていない。乱数生成（random_ops）とテンソル操作（array_ops.where_v2）の組み合わせで実現される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | nn_ops.py | `tensorflow/python/ops/nn_ops.py` | dropout（5380行目）、dropout_v2（5432行目） |

**主要処理フロー**:
1. **5380-5427行目**: dropout（V1 API）- keep_probをrateに変換してdropout_v2に委譲
2. **5432-5522行目**: dropout_v2（V2 API）- uniform_samplerを構成して_dropoutに委譲
3. **5516行目**: uniform_sampler = functools.partial(random_ops.random_uniform, seed=seed)

#### Step 3: 内部実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | nn_ops.py | `tensorflow/python/ops/nn_ops.py` | _dropout（5740-5817行目） |

**主要処理フロー**:
- **5762-5766行目**: rate範囲検証（[0, 1)）
- **5769-5772行目**: 浮動小数点型検証
- **5773-5784行目**: rate=0高速パス - dummy_rng_step()後にxを返却
- **5788-5791行目**: 数値rateの場合: scale = 1/(1-rate)、ret = x * scale
- **5796-5806行目**: Tensor rateの場合: ランク0検証、dtype互換性チェック、real_div
- **5808-5814行目**: noise_shape解決、乱数テンソル生成、keep_mask計算、where適用

#### Step 4: Stateless版を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | nn_ops.py | `tensorflow/python/ops/nn_ops.py` | stateless_dropout（5525行目〜） |

**主要処理フロー**:
- stateless版はstateless_random_ops.stateless_random_uniformを使用
- 状態を持たないため、同じseedで常に同じ結果を生成

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

```
tf.nn.dropout(x, rate, noise_shape, seed, name)    [V2 API]
    |
    +-- dropout_v2(x, rate, noise_shape, seed, name)
            |
            +-- uniform_sampler = partial(random_ops.random_uniform, seed)
            +-- dummy_rng_step = lambda: random_seed.get_seed(seed)
            |
            +-- _dropout(x, rate, noise_shape, uniform_sampler, dummy_rng_step, name)
                    |
                    +-- [rate==0] dummy_rng_step() -> return x
                    |
                    +-- [rate>0] scale = 1/(1-rate)
                    |            ret = x * scale
                    |
                    +-- noise_shape = _get_noise_shape(x, noise_shape)
                    +-- random_tensor = uniform_sampler(noise_shape, dtype)
                    +-- keep_mask = random_tensor >= rate
                    +-- ret = where_v2(keep_mask, ret, 0)
                    +-- return ret

tf.nn.dropout(x, keep_prob=0.5)    [V1 API, deprecated]
    |
    +-- rate = 1 - keep_prob
    +-- dropout_v2(x, rate, ...)
```

### データフロー図

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

x (任意形状)        ---> スケーリング x * 1/(1-rate) ---> scaled_x
                          |
                          v
noise_shape         ---> uniform_sampler(shape) ------> random_tensor
                          |
                          v
rate                ---> keep_mask = random >= rate ---> マスク
                          |
                          v
                    where(keep_mask, scaled_x, 0)
                          |
                          v
                    ドロップアウト適用済みテンソル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| nn_ops.py | `tensorflow/python/ops/nn_ops.py` | ソース | dropout/dropout_v2/_dropout実装 |
| random_ops.py | `tensorflow/python/ops/random_ops.py` | ソース | 乱数生成（uniform_sampler） |
| stateless_random_ops.py | `tensorflow/python/ops/stateless_random_ops.py` | ソース | Stateless乱数生成 |
| random_seed.py | `tensorflow/python/framework/random_seed.py` | ソース | シード管理 |
| array_ops.py | `tensorflow/python/ops/array_ops.py` | ソース | where_v2 |
| gen_math_ops.py | `tensorflow/python/ops/gen_math_ops.py` | 生成 | mul, real_div, cast |
| core.py | `tensorflow/python/keras/layers/core.py` | ソース | Keras Dropoutレイヤー |
