まとめBoruta は、ランダムフォレストの feature_importances_ を使って「本当に有用な列」と「偶然高く出た列」を統計的に区別する特徴選択手法です。元の 20 列をそれぞれ行方向にシャッフルした「シャドウ特徴量」20 列を横に付け、40 列で森を学習し、シャドウ側の最大重要度を「偶然で届く上限」として、それを有意に超えた列だけを Confirmed、下回った列を Rejected にします。これを max_iter=100 回繰り返すので、閾値を人が決める必要がなく、同じ乱数なら同じ列が選ばれます。Python では scikit-learn 互換の BorutaPy に RandomForestClassifier を渡すだけで実行でき、このページでは 20 列中 10 列が有効なデータで support_ と ranking_ の読み方、選択前後の交差検証精度を確かめます。
動画で見る:borutaを使って特徴選択をしてみよう!(K_DM チャンネル)特徴選択の基本 で、ランダムフォレストの feature_importances_ を閾値 0.05 で切る埋め込み法を扱っています。Boruta はその「閾値をどう決めるか」を自動化した手法なので、先に読んでおくと狙いが分かります。
直感
#
ランダムフォレストの特徴量重要度を見て「0.05 以上を残す」と決めるとき、その 0.05 に根拠はありません。Boruta の発想は「無関係な列でも重要度はいくらか付く。ではその上限はどこか」を 100 回の実験で実測することです。各列の値を行方向にシャッフルすると、分布はそのままでラベルとの関係だけが壊れた「偽の列」(シャドウ特徴量)ができます。元の 20 列とシャドウ 20 列の計 40 列で森を学習すると、シャドウ側の重要度は「偶然だけでここまで出る」という基準になり、その最大値を超えた元の列だけが本物とみなせます。1 回の学習では偶然の揺れがあるので、シャッフルと学習を 100 回繰り返し、何回上回ったかを二項検定にかけて Confirmed / Rejected を決めます。閾値を人が決めなくてよく、random_state を固定すれば同じ列が選ばれる、再現性のある特徴選択です。
元の 20 列→
各列をシャッフルしてシャドウ 20 列を作る→
40 列でランダムフォレストを学習→
シャドウ側の最大重要度を記録→
元の列がそれを上回ったかを数える→
max_iter 回繰り返したか?→
二項検定で Confirmed / Rejected / Tentative に分類
まだ足りないとき
元の列がそれを上回ったかを数える→
各列をシャッフルしてシャドウ 20 列を作る
アルゴリズムの流れと Python での確認
#
Kursa, Miron B., and Witold R. Rudnicki. “Feature Selection with the Boruta Package.” Journal of Statistical Software 36.11 (2010).
アルゴリズムの流れ
#
1 回の反復は次の 5 段階です。まず各特徴量を行方向にシャッフルした「シャドウ特徴量」を作成し、元の特徴量の横に追加します。次にその 40 列でランダムフォレストを学習し、全列の重要度を計算します。続いてシャドウ特徴量の中での最大重要度(MZSA: Max Z-Score Among Shadows)を記録し、元の各列の重要度が MZSA を上回ったかどうかを「当たり」として数えます。この反復を max_iter 回(下のコードでは 100 回)繰り返し、当たりの回数を二項検定にかけて、有意に多い列を Confirmed、有意に少ない列を Rejected、どちらとも言えない列を Tentative に分類します。Rejected になった列は以降の反復から外されるので、後半は残った列だけで学習が進みます。
ライブラリと実験データ
#
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
|
特徴選択の基本 と同じ、20 列のうち 10 列が有効・5 列が冗長・5 列が雑音のデータを使います。
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の実行
#
深さ 5 の木 100 本の森を重要度の計算器として渡し、最大 100 回の反復で Confirmed / Tentative を判定します。
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_}")
|
結果の可視化
#
各特徴量のランキングを棒グラフにします。ranking_ は 1 が Confirmed、2 が Tentative、3 以上が 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()
|
選択前後の精度比較
#
Confirmed の列だけに絞ったデータと全 20 列のデータで、同じランダムフォレストの 5 分割交差検証精度を比べます。雑音 5 列を落としても精度が下がらなければ、特徴選択が成功しています。
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 のシャドウ特徴量は、元データの各列を行方向にランダムシャッフルしたものです。シャッフルによってターゲットとの関係が破壊されるため、シャドウ特徴量の重要度は「偶然による重要度の上限」を表します。BorutaPy の中で起きていることを 1 回分だけ手で再現し、元の 20 列の重要度とシャドウ側の最大値を同じ図に描きます。
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()
|
まとめ
#
Boruta は、各列をシャッフルしたシャドウ特徴量をランダムフォレストに混ぜて学習し、シャドウ側の最大重要度を「偶然の上限」として、それを有意に超えた列だけを Confirmed にする特徴選択です。重要度の閾値を人が決める代わりに max_iter=100 回の反復と二項検定で決めるので、random_state を固定すれば同じ列が選ばれます。BorutaPy では support_(Confirmed)、support_weak_(Tentative)、ranking_(1 が Confirmed)で結果を読み、20 列中 10 列が有効なデータでは 10 列前後が残って交差検証精度はほぼ変わりませんでした。計算は森を最大 100 回学習する分だけ重いので、列が数千あるときは 特徴選択の基本 のフィルター法で先に絞ってから使います。