# 機能設計書 45-ランダムフォレスト分類

## 概要

本ドキュメントは、Apache Spark MLlibにおけるランダムフォレスト分類（Random Forest Classification）機能の設計を記述する。アンサンブル学習手法であるランダムフォレストによる分類モデルの訓練と予測を提供する。

### 本機能の処理概要

ランダムフォレスト分類は、複数の決定木を並列に訓練し、多数決（投票）により分類結果を決定するアンサンブル学習手法である。

**業務上の目的・背景**：単一の決定木は過学習しやすく分散が大きいという課題がある。ランダムフォレストはバギング（Bootstrap Aggregating）と特徴量サブセット選択により、個々の決定木の分散を低減し、高い汎化性能を実現する。多くの実務的な分類タスクにおいて高い精度を達成できる。

**機能の利用シーン**：二値・多クラス分類問題において、高い精度と安定性が求められる場面（信用審査、顧客チャーン予測、画像分類等）で使用される。特徴量重要度の算出にも広く利用される。

**主要な処理内容**：
1. カテゴリ特徴量のメタデータ抽出とクラス数取得
2. Bootstrapサンプリングの設定
3. RandomForest.run()による複数決定木の並列訓練
4. 予測時に全木の投票結果を集約して分類
5. 特徴量重要度の算出（全木の平均）
6. モデルサマリー（訓練/評価）の提供

**関連システム・外部連携**：Pipeline API、CrossValidator/TrainValidationSplit。

**権限による制御**：特段のロール制御はない。

## 関連画面

本機能に直接関連する画面はない。

## 機能種別

計算処理 / 機械学習分類（アンサンブル）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| featuresCol | String | Yes | 特徴量カラム名 | Vector型カラム |
| labelCol | String | Yes | ラベルカラム名 | 非負整数 |
| weightCol | String | No | 重みカラム名 | 非負の数値 |
| numTrees | Int | No | 木の数、デフォルト20 | 1以上 |
| maxDepth | Int | No | 各木の最大深度、デフォルト5 | 非負整数 |
| maxBins | Int | No | 連続特徴量の最大ビン数、デフォルト32 | 2以上 |
| minInstancesPerNode | Int | No | ノード分割に必要な最小インスタンス数、デフォルト1 | 1以上 |
| minWeightFractionPerNode | Double | No | ノード分割に必要な最小重みフラクション | [0, 0.5] |
| minInfoGain | Double | No | 分割に必要な最小情報利得 | 0以上 |
| impurity | String | No | 不純度指標、"gini"/"entropy" | サポートされた値 |
| subsamplingRate | Double | No | サブサンプリング率、デフォルト1.0 | (0, 1] |
| featureSubsetStrategy | String | No | 特徴量サブセット戦略（"auto"/"all"/"sqrt"/"log2"/"onethird"） | サポートされた値 |
| bootstrap | Boolean | No | Bootstrapサンプリング有無、デフォルトtrue | - |
| seed | Long | No | 乱数シード | - |

### 入力データソース

MLlibのDataFrame/Datasetとして提供されるラベル付きトレーニングデータ。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| RandomForestClassificationModel | RandomForestClassificationModel | 学習済みランダムフォレストモデル（DecisionTreeClassificationModel配列） |
| prediction | Double | 予測クラスラベル |
| rawPrediction | Vector | 各クラスの投票値 |
| probability | Vector | 各クラスの予測確率（正規化済み） |
| leafCol | Vector | 各木の予測に使用された葉ノードインデックス（設定時） |

### 出力先

メモリ内のRandomForestClassificationModelオブジェクト、またはMLWriter経由で永続化。

## 処理フロー

### 処理シーケンス

```
1. train()メソッド
   └─ カテゴリ特徴量抽出・クラス数取得
   └─ Instance RDD作成
   └─ OldStrategy構築（bootstrap設定含む）
   └─ RandomForest.run(instances, strategy, numTrees, featureSubsetStrategy)
   └─ 各木にパラメータをコピー
   └─ RandomForestClassificationModel構築
   └─ モデルサマリー作成

2. predictRaw(features)
   └─ 各木のrootNode.predictImpl()を呼び出し
   └─ 全木のクラス別投票値を集約

3. raw2probabilityInPlace(rawPrediction)
   └─ 投票値を確率に正規化
```

### フローチャート

```mermaid
flowchart TD
    A[train開始] --> B[カテゴリ特徴量抽出]
    B --> C[OldStrategy構築]
    C --> D[bootstrap設定]
    D --> E[RandomForest.run numTrees=N]
    E --> F[DecisionTreeClassificationModel配列]
    F --> G[RandomForestClassificationModel構築]
    G --> H[サマリー作成]
    H --> I[predict/transform]
    I --> J[各木の投票集約]
    J --> K[確率正規化]
    K --> L[prediction出力]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-45-01 | 多数決投票 | 各木のクラス別確率を合計し、最大投票クラスを予測 | predictRaw時 |
| BR-45-02 | 特徴量重要度 | 全木の特徴量重要度の平均を正規化して算出 | featureImportances呼び出し時 |
| BR-45-03 | 最低1本の木 | ランダムフォレストには最低1本の木が必要 | モデル構築時 |
| BR-45-04 | サマリー分岐 | numClasses <= 2の場合はBinary、それ以外はMulticlassサマリー | サマリー作成時 |

### 計算ロジック

- 各木の予測: `classCounts = tree.rootNode.predictImpl(features).impurityStats.stats`
- 投票値: `votes(k) += classCounts(k) / total` （各木のクラス確率を合計）
- 確率: 投票値を正規化して確率に変換

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

本機能はデータベースへの直接操作は行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| IllegalArgumentException | バリデーションエラー | thresholdsの長さがnumClassesと不一致 | thresholdsを正しく設定 |
| require | バリデーションエラー | 木の数が0 | numTreesを1以上に設定 |

### リトライ仕様

特になし。

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

該当なし。

## パフォーマンス要件

- 複数木は並列に訓練可能
- featureSubsetStrategyによりノード分割時の特徴量候補数を制御して計算量を削減

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

モデル永続化時のアクセス制御を適用する必要がある。

## 備考

- ランダムフォレストはDecisionTreeClassificationModelの配列をアンサンブルとして保持する
- Spark 3.1.0以降でモデルサマリー（トレーニング/評価）が追加された
- 全木の重みは均一（1.0）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | RandomForestClassifier.scala | `mllib/src/main/scala/org/apache/spark/ml/classification/RandomForestClassifier.scala` | RandomForestClassifierParamsのパラメータ群（numTrees, bootstrap, featureSubsetStrategy等）を理解する |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RandomForestClassifier.scala | `mllib/src/main/scala/org/apache/spark/ml/classification/RandomForestClassifier.scala` | train()メソッド（138-177行目）を理解する |

**主要処理フロー**:
1. **142-143行目**: カテゴリ特徴量抽出
2. **144行目**: クラス数取得
3. **152-157行目**: Instance RDD作成
4. **159-161行目**: OldStrategy構築、bootstrap設定
5. **168-170行目**: `RandomForest.run`呼び出し
6. **176行目**: `createModel`でモデル構築

#### Step 3: 予測処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | RandomForestClassifier.scala | `mllib/src/main/scala/org/apache/spark/ml/classification/RandomForestClassifier.scala` | predictRaw()（315-332行目）の投票集約ロジックを理解する |

**主要処理フロー**:
- **319行目**: `votes`配列初期化
- **320-331行目**: 各木のクラス別確率を合計
- **334-343行目**: `raw2probabilityInPlace`で正規化

#### Step 4: サマリーとモデル永続化を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | RandomForestClassifier.scala | `mllib/src/main/scala/org/apache/spark/ml/classification/RandomForestClassifier.scala` | createSummary（379-400行目）、永続化（419-462行目）を理解する |

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

```
RandomForestClassifier.train(dataset)
    |
    +-- MetadataUtils.getCategoricalFeatures()
    +-- getNumClasses(dataset)
    +-- getOldStrategy(categoricalFeatures, numClasses)
    +-- RandomForest.run(instances, strategy, numTrees, featureSubsetStrategy)
    |       |
    |       +-- [並列] 複数の決定木構築
    |
    +-- createModel(dataset, trees, numFeatures, numClasses)
            +-- RandomForestClassificationModel構築
            +-- createSummary(dataset)

RandomForestClassificationModel.predictRaw(features)
    |
    +-- [各木] tree.rootNode.predictImpl(features)
    +-- 投票値集約 (votes += classCounts / total)
    +-- Vectors.dense(votes)
```

### データフロー図

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

Dataset ──────> OldStrategy構築 + Bootstrap
(label,          |
 features,       v
 weight)    RandomForest.run ──────> DecisionTreeClassificationModel[]
            (numTrees=N)                |
                                        v
                                   RandomForestClassificationModel
                                        |
                                        v
                                   predictRaw: 全木投票集約
                                        |
                                        v
                                   (prediction, rawPrediction, probability)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RandomForestClassifier.scala | `mllib/src/main/scala/org/apache/spark/ml/classification/RandomForestClassifier.scala` | ソース | ランダムフォレスト分類の全実装 |
| DecisionTreeClassifier.scala | `mllib/src/main/scala/org/apache/spark/ml/classification/DecisionTreeClassifier.scala` | ソース | 個々の決定木モデル |
| RandomForest.scala | `mllib/src/main/scala/org/apache/spark/ml/tree/impl/RandomForest.scala` | ソース | 木構築の共通アルゴリズム |
| TreeEnsembleModel.scala | `mllib/src/main/scala/org/apache/spark/ml/tree/TreeEnsembleModel.scala` | ソース | アンサンブル特徴量重要度計算 |
| EnsembleModelReadWrite.scala | `mllib/src/main/scala/org/apache/spark/ml/tree/EnsembleModelReadWrite.scala` | ソース | アンサンブルモデルの永続化 |
