決定木分析|分岐で直感的に理解する予測モデル(過学習と剪定)
ルールが読みやすく、ビジネス説明性の高いモデルです。一方で、深く伸ばすと過学習しやすいため、剪定や深さ制御が重要です。
この記事が想定する読者:分類・回帰で説明性の高いモデルが欲しく、if-thenの分岐で解釈したい分析担当者。
判断を誤るとどうなるか:深さや剪定を制御しないと過学習し、実務で使えない。先に目的(説明性か精度か)を決め、max_depth・min_samples_leaf・剪定(ccp_alpha)で過学習を抑え、不均衡ならclass_weight・閾値・評価指標を整えると失敗しにくい。
まずはここだけ(やさしい導入)
- 何をする?: if-then の分岐でデータを分け、最後の“葉”で予測(分類/回帰)する
- いつ使う?: 説明性が重要、非線形な境界、相互作用が多い表形式データ
- どう読む?: 分割基準(Gini/情報利得)と葉のクラス分布、深さを見て過学習を点検
用語ミニ辞典(1 行で)
- Gini/エントロピー: 混ざり具合(不純度)。小さくなる分割が良い
- 剪定(cost-complexity): 木の複雑さに罰則(α)をかけてシンプルに保つ
- min_samples_leaf: 葉に必要な最小サンプル。大きくすると過学習を抑制
- class_weight: クラス不均衡での重み付け。誤りコストを近似的に反映
最小コード(分類)
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import matplotlib.pyplot as plt
import pandas as pd
X = pd.DataFrame({ 'age':[22,25,47,52,46,56,55,60], 'spend':[200,220,500,520,480,600,590,610] })
y = [0,0,1,1,1,1,1,1]
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, random_state=42)
clf = DecisionTreeClassifier(max_depth=3, random_state=42)
clf.fit(X_tr, y_tr)
pred = clf.predict(X_te)
print('acc=', accuracy_score(y_te, pred))
plot_tree(clf, feature_names=X.columns, class_names=['no','yes'], filled=True)
plt.show()
実務ケーススタディ(購入確率のスクリーニング)
目的: EC の購入成否(0/1)を予測し、優先フォロー対象を抽出する。
- データと前処理
- 特徴: 年齢、閲覧回数、直近閲覧日、カテゴリ、在庫、キャンペーン履歴
- カテゴリはワンホット化。欠損は単純補完でも動く
- 学習と評価
- train/valid 分割。max_depth と min_samples_leaf を調整
- 不均衡なら class_weight='balanced' を検討
- 運用
- 葉ノードの“確度 × 件数”で優先順位を作成。週次で再学習
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
from sklearn.tree import plot_tree
import matplotlib.pyplot as plt
# X, y は前処理済みの特徴量/目的
X_tr, X_va, y_tr, y_va = train_test_split(X, y, test_size=0.2, random_state=42)
# cost-complexity 剪定パスから α を探索
tmp = DecisionTreeClassifier(random_state=42, class_weight='balanced')
path = tmp.cost_complexity_pruning_path(X_tr, y_tr)
best_auc, best_alpha = -1, 0.0
for a in path.ccp_alphas:
clf = DecisionTreeClassifier(random_state=42, ccp_alpha=a, max_depth=6, min_samples_leaf=20, class_weight='balanced')
clf.fit(X_tr, y_tr)
proba = clf.predict_proba(X_va)[:,1]
auc = roc_auc_score(y_va, proba)
if auc > best_auc:
best_auc, best_alpha = auc, a
print('best_alpha=', round(best_alpha,5), 'AUC=', round(best_auc,3))
# pruning パスの可視化(α と葉数)
alphas, impurities = path.ccp_alphas, path.impurities
plt.plot(alphas, impurities, marker='o')
plt.xlabel('ccp_alpha'); plt.ylabel('Total Impurity of Leaves')
plt.title('Cost-Complexity Pruning Path')
plt.tight_layout(); plt.show()
剪定と誤りコスト(実務の勘所)
- 剪定(ccp_alpha): 交差検証や pruning path で α を選ぶと汎化が安定
- 不均衡/誤りコスト: class_weight、しきい値最適化、コスト行列(運用で反映)
- 深さ制御: max_depth/min_samples_leaf/min_impurity_decrease を併用
可視化の読み方(プロのコツ)
- 分割条件: どの特徴がどの閾値で効いたか(ビジネス解釈と整合するか)
- 葉の分布: サンプル数とクラス比。極小葉は過学習の兆候
- 経路の一貫性: 上位分割と下位分割の整合。スパゲティ化していないか
- 注意: 1 本の木のルールを過信しない。期間差や再学習で安定性を確認
練習問題(理解を定着)
- 正例が 5% の不均衡。学習と評価の工夫は?
- ヒント: class_weight、しきい値最適化、PR-AUC で評価
- 木が深くなり精度は train で 0.99、valid で低い。対策は?
- ヒント: 剪定(ccp_alpha)、max_depth/min_samples_leaf、特徴量の簡素化
- 可視化で最上位分割が“在庫>0”。何が示唆される?
- ヒント: 供給制約の支配。施策は在庫と連動で検討
- 重み付けと閾値最適化で再現率/適合率のバランスを最適化、評価は PR-AUC。
- モデルを簡素化し汎化を優先。α と深さ・葉サイズで調整。
- 需要より供給の影響が強い可能性。ビジネス要件を反映し説明変数を見直す。
ハイパラ要点
- max_depth/min_samples_leaf で過学習抑制
- 基準: Gini/エントロピー(分類)、MSE(回帰)
実務の落とし穴
- わずかなデータ変動で木が変わる → アンサンブル(ランダムフォレスト)を併用
- 連続変数の多い分割 → 過学習しやすいので深さを制御
判断の土台として押さえておくこと
- if-thenの分岐で予測し、葉でクラス/数値を出す:分割基準(Gini/情報利得)で不純度を下げる。説明性が高い代わりに深くしすぎると過学習するため、剪定・深さ制御が必須。
- 過学習対策:max_depth・min_samples_leaf・ccp_alpha(cost-complexity剪定)で木を簡素化。検証データで汎化を確認。不均衡ならclass_weight・閾値調整・AUC/PRで評価する。
- 運用:可視化で分割条件と葉の分布を確認。1本の木のルールを過信せず、期間差・再学習で安定性を確認。説明性と精度の両立には浅い木+ルール抽出やRF+SHAPの併用も検討する。
次の一手:ランダムフォレスト/線形回帰/データサイエンス手法の全体像
関連と次の一歩
よくある質問(FAQ)
- Q: 不均衡データで性能が出ない
- A: class_weight や閾値調整、評価指標(AUC/PR)で最適化
- Q: 説明性を保ちつつ精度も欲しい
- A: 浅い木+ルール抽出、もしくは RF/XGBoost + SHAP の併用
決定木分析についてのご相談はこちら