枝刈りとは?決定木の過学習対策・方法・評価を解説

枝刈りとは、決定木の枝や葉を削除して、木が学習データに適合しすぎることを抑える手法です。決定木は分割を続けると訓練データを細かく分類できますが、サンプル固有の偶然まで覚え、未知データで性能が下がる過学習を起こすことがあります。枝刈りでは、予測にほとんど寄与しない分割や、サンプルが少なすぎる葉を取り除き、汎化性能と説明しやすさのバランスを整えます。

枝刈りには、木を成長させながら分割を制限する事前枝刈りと、いったん大きな木を作ってから不要な枝を削る事後枝刈りがあります。scikit-learnでは、最大深さや葉の最小サンプル数、複雑度パラメータによるコスト複雑度枝刈りを利用できます。本記事では、枝刈りの意味、過学習との関係、代表的なパラメータ、交差検証による選び方、評価と運用上の注意点を整理します。

枝刈りとは?決定木の複雑さを抑えて汎化性能を高める方法

決定木は、特徴量の条件でデータを二つ以上の子ノードへ分け、分割後のクラス不純度や誤差を下げる処理を繰り返します。深い木では、訓練データの1件だけを分ける条件まで作れるため、訓練スコアは高くなります。しかし、その条件が新しいデータでも有効とは限りません。枝刈りは、木の構造を小さくして、偶然の分割を予測ルールから外します。

枝刈りの目的は、訓練データの正解率を最大化することではありません。検証データや将来データで安定して予測できること、ルールを人が確認できること、推論や保守のコストを抑えることを含めて、適切な複雑さを選ぶことです。葉を削りすぎると今度は表現力が不足するため、アンダーフィッティングとのバランスも評価します。

枝刈りと過学習の関係|訓練性能と検証性能の差を見る

木の深さや葉の数を横軸、訓練スコアと検証スコアを縦軸にして比較すると、枝刈りの効果を確認できます。浅い木では両方のスコアが低く、深くするにつれて検証スコアが改善します。さらに深くすると訓練スコアだけが上がり、検証スコアが下がる領域が現れます。枝刈りは、この過学習領域へ入る前、または入った後に木を簡素化する操作です。

分類では正解率だけでなく、少数クラスの再現率、適合率、F1、混同行列を確認します。回帰ではMAEやRMSE、予測残差の分布を確認します。クラス不均衡や外れ値があると、単一のスコアが枝の選択を誤らせることがあるため、業務上の誤りコストに合わせた指標を使ってください。

事前枝刈りの方法|木を成長させる段階で制限する

max_depthで深さを制限する

max_depth は、根から葉までの最大深さを指定します。値を小さくすると、木のルールが短くなり、局所的な分割を抑えられます。大きすぎると過学習し、小さすぎると複雑な関係を表現できません。候補値を交差検証で比較し、訓練スコアと検証スコアの差、ルールの長さを記録します。

min_samples_splitで分割に必要な件数を指定する

min_samples_split は、ノードをさらに分割するために必要な最小サンプル数です。値を増やすと、少数データだけで作る枝が減ります。ただし、割合で指定する場合はデータ数に応じて実際の件数が変わるため、学習データのサイズを変えたときの挙動も確認してください。

min_samples_leafで葉の最小件数を保証する

min_samples_leaf は、分割後の各葉に残す最小サンプル数を指定します。葉が1件だけになることを防ぎ、分類確率や回帰平均のばらつきを抑える効果があります。顧客や店舗など同じグループの観測が複数ある場合は、単なる件数ではなく、グループ単位の評価と組み合わせてください。

min_impurity_decreaseで改善幅を要求する

min_impurity_decrease は、分割によって不純度が一定以上改善する場合にだけ分割を許可するパラメータです。わずかな改善しかない分割を除ける一方、サンプル重みやクラス重みを使うと計算される改善幅も変わります。パラメータの意味を実装バージョンの公式ドキュメントで確認し、重みを含む学習条件を固定して比較します。

事後枝刈りの方法|大きな木から不要な枝を削る

事後枝刈りでは、まず停止条件を緩めた木を成長させ、複雑な候補を作ります。その後、予測性能を大きく損なわない枝を順に削ります。候補を比較しやすい反面、最大の木を作るため学習時の計算量やメモリが増えます。データが少ない場合は、事前枝刈りと交差検証を組み合わせ、過度に大きな木を作らない設計も検討します。

コスト複雑度枝刈りとccp_alpha

scikit-learnの決定木では、コスト複雑度枝刈りを ccp_alpha で制御できます。木の不純度に、葉の数に応じた複雑さのペナルティを加え、合計の評価が小さくなる部分木を選ぶ考え方です。ccp_alpha=0 では通常、事後枝刈りを行わず、値を大きくするほど複雑な枝が削られます。

from sklearn.tree import DecisionTreeClassifier

tree = DecisionTreeClassifier(random_state=42)
path = tree.cost_complexity_pruning_path(X_train, y_train)
alphas = path.ccp_alphas

models = [DecisionTreeClassifier(ccp_alpha=a, random_state=42).fit(X_train, y_train)
          for a in alphas]

cost_complexity_pruning_path は、枝を削る候補となる複雑度パラメータと不純度の情報を返します。すべての候補をそのまま採用せず、訓練データ内の交差検証で検証スコアを比較します。最終的な評価には、パラメータ選択に使っていないテストデータを一度だけ使い、選択後の性能を報告してください。

枝刈りのパラメータを選ぶ手順|交差検証と業務制約を組み合わせる

1. 目的と評価指標を決める

まず、誤分類や予測誤差のどれを減らしたいかを決めます。医療のスクリーニングなら少数クラスの見逃し、審査なら誤承認と誤却下、予測値なら許容できる金額や時間のずれが重要です。単純なaccuracyだけで枝刈りの設定を選ぶと、業務上重要なクラスの性能が隠れるため、主指標と補助指標を事前に定義します。

2. データ分割と前処理を固定する

パラメータを選ぶ検証データと最終テストデータを分けます。時系列なら未来の期間を評価側に置き、同じ顧客や設備が学習側とテスト側にまたがるならグループ分割を検討します。欠損補完やカテゴリ変換の基準は学習分割だけで適合させ、テスト情報が枝の選択に漏れないようにします。

3. 検証スコアと木の複雑さを並べる

各パラメータについて、検証スコア、葉の数、深さ、学習時間、推論時間を記録します。スコアがほぼ同じなら、葉が少なく説明しやすい木を選ぶことに価値があります。交差検証の平均だけでなく、分割ごとのばらつきも見て、特定の期間やグループにだけ依存していないか確認します。

4. 最終テストと再学習を行う

設定を選んだら、テストデータで一度だけ評価します。問題がなければ、運用で利用する学習データ全体に同じ設定で再学習します。再学習後の木は、パラメータ選択時と構造が変わる可能性があるため、ルール、特徴量の順序、クラス対応、モデルファイルのハッシュを保存します。

枝刈りの効果|性能・説明性・運用コストを評価する

枝刈り後の木は、条件分岐が減るため、人が確認しやすくなります。審査や営業支援など、予測理由を説明する必要がある業務では、深い木より短いルールが適することがあります。ただし、浅くすれば必ず説明が正確になるわけではありません。各葉のサンプル数、クラス割合、適用条件を表示し、説明対象のケースがどの葉に到達したかを追跡します。

推論速度やメモリ使用量も、木が小さくなるほど改善する可能性があります。低遅延のAPIやエッジ端末では、精度が少し下がっても、計算量が安定する木を採用する価値があります。一方、ランダムフォレストや勾配ブースティングの各木を個別に枝刈りする場合は、アンサンブル全体の検証が必要です。単一木で得た効果を、そのままアンサンブルへ当てはめないでください。

枝刈りで失敗しやすい点と対策

テストデータをパラメータ選択に使う

ccp_alphaやmax_depthをテストスコアを見ながら調整すると、テストデータへ過学習します。テストデータは最終確認だけに残し、パラメータ選択は訓練データ内の交差検証で行います。テスト結果が期待より悪かった場合に、同じテストデータで何度も設定を変えないことが重要です。

サンプルの少ない葉を性能だけで評価する

訓練データで不純度0の葉が多くても、各葉が数件しかなければ新しいデータで不安定になります。葉ごとの件数と検証時の誤りを確認し、min_samples_leafを設定します。少数クラスの葉を単純に削ると見逃しが増える場合があるため、クラス別指標を併記します。

分布の変化を考慮しない

本番のクラス割合や特徴量の分布が学習時から変わると、枝刈りで選んだ木の性能も変化します。期間別、地域別、顧客属性別に評価し、データドリフトを監視します。再学習の基準、木の深さや葉数の上限、性能が下がった場合の切り戻し方法を運用手順に含めてください。

枝刈りまとめ|検証データで適切な複雑さを選ぶ

枝刈りは、決定木の不要な分割を削除し、過学習を抑えながら汎化性能と説明性を整える方法です。max_depth、min_samples_split、min_samples_leafなどで成長中の木を制限する事前枝刈りと、ccp_alphaを使って大きな木から部分木を選ぶ事後枝刈りがあります。

設定はテストデータを見ずに交差検証で選び、検証スコアだけでなく、葉数、深さ、推論コスト、少数クラスの性能、説明のしやすさを比較します。評価条件を固定し、データ分割と前処理の情報漏えいを防ぎます。木を小さくすること自体を目的にせず、未知データで安定し、業務の誤りコストに合うモデルを選んでください。

よくある質問(FAQ)

Q. 枝刈りとは何ですか?

枝刈りとは、決定木の不要な枝や葉を削除し、過学習を抑える手法です。木の複雑さを下げ、未知データでの汎化性能や説明性を改善します。

Q. 事前枝刈りと事後枝刈りの違いは何ですか?

事前枝刈りは木を成長させる途中で深さやサンプル数を制限します。事後枝刈りは大きな木を作ってから、複雑度のペナルティなどで不要な枝を削ります。

Q. ccp_alphaは何を表しますか?

ccp_alphaはコスト複雑度枝刈りの複雑度パラメータです。大きくすると葉の数に対するペナルティが強くなり、より小さな部分木が選ばれます。交差検証で値を決めます。

Q. 枝刈りの設定はどう選びますか?

訓練データ内の交差検証で、主指標と補助指標、木の深さ、葉数、推論コストを比較します。最終的な性能は、設定選択に使っていないテストデータで一度だけ確認します。

Q. 深い決定木は必ず枝刈りすべきですか?

必ずではありませんが、深い木は過学習しやすいため、検証性能と葉のサンプル数を確認します。枝刈り前後を同じ条件で比較し、精度、説明性、運用コストに合う設定を選びます。

参考資料