Boruta

中級

2.7.2

Boruta

最終更新 2020-02-12 読了時間 2 分
まとめ
  • Borutaはランダムフォレストの重要度を利用し、「本当に有用な特徴量」と「偶然の重要度」を統計的に区別する特徴選択手法。
  • 元の特徴量をシャッフルした「シャドウ特徴量」を追加し、その最大値より有意に高い重要度を持つ特徴だけを採用する。
  • scikit-learnベースのBorutaPyで手軽に実装できる。

直感 #

ランダムフォレストの特徴量重要度を見て「閾値をどこに設定するか」は主観的な判断になりがちです。Borutaは「シャッフルされた偽の特徴量(シャドウ特徴量)」をベンチマークとして使い、それを超える重要度を持つ特徴だけを統計的に選択します。閾値の設定が不要で、再現性のある特徴選択が可能になります。

詳細な解説 #

Kursa, Miron B., and Witold R. Rudnicki. “Feature Selection with the Boruta Package.” Journal of Statistical Software 36.11 (2010).

アルゴリズムの流れ #

  1. 各特徴量をシャッフルした「シャドウ特徴量」を作成し、元の特徴量に追加する
  2. ランダムフォレストを学習し、全特徴量の重要度を計算する
  3. シャドウ特徴量の最大重要度(MZSA: Max Z-Score Among Shadows)を記録する
  4. 元の特徴量の重要度がMZSAを有意に上回るか統計検定する
  5. Confirmed(有意に上回る)、Rejected(有意に下回る)、Tentative(未決定)に分類する
  6. 手順1〜5を規定回数繰り返し、最終判定を行う

ライブラリと実験データ #

1
2
3
4
5
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from boruta import BorutaPy
1
2
3
4
X, y = make_classification(
    n_samples=500, n_features=20, n_informative=10,
    n_redundant=5, n_classes=2, random_state=42,
)

Borutaの実行 #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
rf = RandomForestClassifier(
    n_estimators=100, max_depth=5, random_state=42, n_jobs=-1,
)
boruta = BorutaPy(
    estimator=rf,
    n_estimators="auto",
    max_iter=100,
    random_state=42,
    verbose=0,
)
boruta.fit(X, y)

print(f"選択された特徴量: {np.where(boruta.support_)[0]}")
print(f"Tentativeな特徴量: {np.where(boruta.support_weak_)[0]}")
print(f"選択数: {boruta.n_features_}")

結果の可視化 #

各特徴量のランキングを可視化します。ランキング1が「Confirmed」、それ以上は「Rejected」です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
ranking = boruta.ranking_

colors = ["green" if r == 1 else "orange" if r == 2 else "red" for r in ranking]

plt.figure(figsize=(12, 4))
plt.bar(range(len(ranking)), ranking, color=colors)
plt.xlabel("特徴量インデックス")
plt.ylabel("Borutaランキング")
plt.title("Boruta 特徴量ランキング(1=Confirmed, 2+=Rejected)")
plt.axhline(y=1.5, color="gray", linestyle="--", alpha=0.5)
plt.show()

選択前後の精度比較 #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from sklearn.model_selection import cross_val_score

rf_eval = RandomForestClassifier(n_estimators=100, random_state=42)

score_all = cross_val_score(rf_eval, X, y, cv=5, scoring="accuracy").mean()
X_selected = X[:, boruta.support_]
score_boruta = cross_val_score(rf_eval, X_selected, y, cv=5, scoring="accuracy").mean()

print(f"全特徴量 ({X.shape[1]}個):       Accuracy = {score_all:.4f}")
print(f"Boruta選択 ({X_selected.shape[1]}個): Accuracy = {score_boruta:.4f}")

シャドウ特徴量の仕組み #

Borutaのシャドウ特徴量は、元データの各列を行方向にランダムシャッフルしたものです。シャッフルによってターゲットとの関係が破壊されるため、シャドウ特徴量の重要度は「偶然による重要度の上限」を表します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# シャドウ特徴量の例
np.random.seed(42)
X_shadow = X.copy()
for col in range(X_shadow.shape[1]):
    np.random.shuffle(X_shadow[:, col])

# 元の特徴量とシャドウを結合して学習
X_combined = np.hstack([X, X_shadow])
rf_shadow = RandomForestClassifier(n_estimators=100, random_state=42)
rf_shadow.fit(X_combined, y)

imp_original = rf_shadow.feature_importances_[:20]
imp_shadow = rf_shadow.feature_importances_[20:]
shadow_max = imp_shadow.max()

plt.figure(figsize=(12, 4))
plt.bar(range(20), imp_original, alpha=0.7, label="元の特徴量")
plt.axhline(y=shadow_max, color="red", linestyle="--", label=f"シャドウ最大 ({shadow_max:.4f})")
plt.xlabel("特徴量インデックス")
plt.ylabel("重要度")
plt.title("元の特徴量 vs シャドウ特徴量の最大値")
plt.legend()
plt.show()