# 機能設計書 44-決定木分類

## 概要

本ドキュメントは、Apache Spark MLlibにおける決定木分類（Decision Tree Classification）機能の設計を記述する。決定木アルゴリズムに基づく分類モデルの訓練と予測を提供する。

### 本機能の処理概要

決定木分類は、特徴量空間を再帰的に分割して分類ルールを学習するアルゴリズムであり、解釈性の高い分類モデルを提供する。

**業務上の目的・背景**：決定木は機械学習の中で最も直感的で解釈しやすいアルゴリズムの一つであり、ビジネスルールの可視化やモデル解釈が重要な業務で広く使用される。また、ランダムフォレストやGBTなどのアンサンブル手法の基盤としても不可欠である。

**機能の利用シーン**：二値・多クラス分類問題において、モデルの解釈性が重要な場面（信用スコアリング、医療診断支援等）で使用される。連続特徴量とカテゴリ特徴量の両方をサポートする。

**主要な処理内容**：
1. カテゴリ特徴量のメタデータ抽出
2. RandomForest.run()を呼び出して単一決定木を訓練（numTrees=1）
3. 不純度指標（Gini / Entropy）に基づくノード分割
4. 予測時にルートノードから葉ノードまで辿って分類結果を出力
5. 特徴量重要度の算出

**関連システム・外部連携**：Pipeline API、RandomForest実装（内部的に共通のRandomForest.run()を使用）。

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

## 関連画面

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

## 機能種別

計算処理 / 機械学習分類

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| featuresCol | String | Yes | 特徴量カラム名 | Vector型カラム |
| labelCol | String | Yes | ラベルカラム名 | 非負整数 |
| weightCol | String | No | 重みカラム名 | 非負の数値 |
| maxDepth | Int | No | 木の最大深度、デフォルト5 | 非負整数 |
| maxBins | Int | No | 連続特徴量の最大ビン数、デフォルト32 | 2以上 |
| minInstancesPerNode | Int | No | ノード分割に必要な最小インスタンス数、デフォルト1 | 1以上 |
| minWeightFractionPerNode | Double | No | ノード分割に必要な最小重みフラクション、デフォルト0.0 | [0, 0.5] |
| minInfoGain | Double | No | 分割に必要な最小情報利得、デフォルト0.0 | 0以上 |
| maxMemoryInMB | Int | No | ヒストグラム集約に使用する最大メモリ、デフォルト256 | 正の整数 |
| cacheNodeIds | Boolean | No | ノードIDキャッシュ有無、デフォルトfalse | - |
| checkpointInterval | Int | No | キャッシュチェックポイント間隔、デフォルト10 | 1以上 |
| impurity | String | No | 不純度指標、"gini"/"entropy"、デフォルト"gini" | サポートされた値 |
| seed | Long | No | 乱数シード | - |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| DecisionTreeClassificationModel | DecisionTreeClassificationModel | 学習済み決定木モデル（rootNode、numFeatures、numClasses） |
| prediction | Double | 予測クラスラベル |
| rawPrediction | Vector | 各クラスの不純度統計値 |
| probability | Vector | 各クラスの予測確率（正規化済み） |
| leafCol | Double | 予測に使用された葉ノードのインデックス（設定時） |

### 出力先

メモリ内のDecisionTreeClassificationModelオブジェクト、またはMLWriter経由でファイルシステムに永続化。

## 処理フロー

### 処理シーケンス

```
1. train()メソッド
   └─ カテゴリ特徴量のメタデータ抽出
   └─ クラス数取得
   └─ thresholdsの長さ検証
   └─ Instanceデータセット作成（label, weight, features）
   └─ OldStrategy設定（Classification, impurity, subsamplingRate=1.0）
   └─ RandomForest.run(instances, strategy, numTrees=1, featureSubsetStrategy="all")
   └─ 生成された木の先頭をDecisionTreeClassificationModelとして返却

2. predict(features)
   └─ rootNode.predictImpl(features).prediction

3. predictRaw(features)
   └─ rootNode.predictImpl(features).impurityStats.stats

4. raw2probabilityInPlace(rawPrediction)
   └─ normalizeToProbabilitiesInPlace(dv)で確率に正規化
```

### フローチャート

```mermaid
flowchart TD
    A[train開始] --> B[カテゴリ特徴量抽出]
    B --> C[クラス数取得]
    C --> D[OldStrategy構築]
    D --> E[RandomForest.run numTrees=1]
    E --> F[DecisionTreeClassificationModel返却]
    F --> G[predict/transform]
    G --> H[rootNode.predictImpl]
    H --> I[impurityStats正規化]
    I --> J[prediction出力]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-44-01 | bootstrap不使用 | 決定木分類ではbootstrapサンプリングは使用しない（subsamplingRate=1.0） | 常時 |
| BR-44-02 | 不純度指標 | GiniまたはEntropyを選択可能 | impurityパラメータ |
| BR-44-03 | 特徴量重要度 | ノード分割時の利得に基づいて計算、合計1に正規化 | featureImportances呼び出し時 |

### 計算ロジック

- Gini不純度: `1 - sum(p_k^2)` （p_kはクラスkの割合）
- Entropy: `-sum(p_k * log(p_k))`
- 特徴量重要度: 各特徴量で分割されたノードの利得の加重和を正規化

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| IllegalArgumentException | バリデーションエラー | thresholdsの長さがnumClassesと不一致 | thresholdsをnumClassesに合わせる |
| require | バリデーションエラー | bootstrapがtrueに設定されている | 決定木ではbootstrapは不要 |

### リトライ仕様

特になし。

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

該当なし。

## パフォーマンス要件

- maxMemoryInMBでヒストグラム集約のメモリ使用量を制御
- cacheNodeIdsでノードID情報をキャッシュして再計算を回避

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

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

## 備考

- 内部的にRandomForest.run()をnumTrees=1で呼び出すため、ランダムフォレストと共通の実装を使用している
- 特徴量重要度は単一決定木では分散が大きいため、RandomForestClassifierの使用が推奨される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | DecisionTreeClassifier.scala | `mllib/src/main/scala/org/apache/spark/ml/classification/DecisionTreeClassifier.scala` | DecisionTreeClassifierParams（ツリーパラメータ）とDecisionTreeClassificationModel（rootNode, numFeatures, numClasses）を理解する |

**読解のコツ**: DecisionTreeClassifierParamsは`TreeClassifierParams`と`DecisionTreeParams`を組み合わせたトレイトで、多くのパラメータが親トレイトから継承されている。

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

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

**主要処理フロー**:
1. **119行目**: `MetadataUtils.getCategoricalFeatures`でカテゴリ特徴量抽出
2. **120行目**: `getNumClasses`でクラス数取得
3. **128-133行目**: Instance RDD作成（ラベル・重み・特徴量チェック付き）
4. **135行目**: `getOldStrategy`でOldStrategy構築
5. **136行目**: `require(!strategy.bootstrap)` - bootstrap不使用を検証
6. **142-143行目**: `RandomForest.run`を`numTrees=1, featureSubsetStrategy="all"`で呼び出し
7. **145行目**: 結果の先頭木をDecisionTreeClassificationModelにキャスト

#### Step 3: モデルの予測処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | DecisionTreeClassifier.scala | `mllib/src/main/scala/org/apache/spark/ml/classification/DecisionTreeClassifier.scala` | DecisionTreeClassificationModel（176-281行目）のpredict/predictRaw/raw2probabilityInPlaceを理解する |

**主要処理フロー**:
- **200-202行目**: `predict()` - rootNode.predictImpl(features).prediction
- **227-229行目**: `predictRaw()` - impurityStats.statsのクローン
- **231-240行目**: `raw2probabilityInPlace()` - 確率正規化
- **271行目**: `featureImportances` - TreeEnsembleModel.featureImportances呼び出し

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

```
DecisionTreeClassifier.train(dataset)
    |
    +-- MetadataUtils.getCategoricalFeatures()
    +-- getNumClasses(dataset)
    +-- getOldStrategy(categoricalFeatures, numClasses)
    +-- RandomForest.run(instances, strategy, numTrees=1)
            |
            +-- [内部] 決定木構築アルゴリズム
            |       +-- ノード分割（Gini/Entropy）
            |       +-- 再帰的木構築
            |
            +-- DecisionTreeClassificationModel

DecisionTreeClassificationModel.transform(dataset)
    |
    +-- predictRaw(features)
    |       +-- rootNode.predictImpl(features)
    |
    +-- raw2probabilityInPlace(rawPrediction)
    +-- predict(features)
```

### データフロー図

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

Dataset ──────> カテゴリ特徴量抽出
(label,          |
 features,       v
 weight)    OldStrategy構築
                 |
                 v
            RandomForest.run ──────> DecisionTreeClassificationModel
            (numTrees=1)                 |
                                         v
                                    predictImpl(features)
                                         |
                                         v
                                    (prediction, rawPrediction, probability)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| 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` | ソース | 決定木/ランダムフォレスト共通の木構築アルゴリズム |
| Node.scala | `mllib/src/main/scala/org/apache/spark/ml/tree/Node.scala` | ソース | 木のノード構造定義 |
| TreeEnsembleModel.scala | `mllib/src/main/scala/org/apache/spark/ml/tree/TreeEnsembleModel.scala` | ソース | 特徴量重要度の計算 |
| ProbabilisticClassifier.scala | `mllib/src/main/scala/org/apache/spark/ml/classification/ProbabilisticClassifier.scala` | ソース | 確率的分類器の基底クラス |
