Day 107 — 特徴量選択の高度手法 — SHAP・Boruta・PermutationImportance

2026-07-28 青 / Phase 4 コーディング PermutationImportance / Boruta / SHAP

📚 背景知識(読んでから問題へ)

🔵
集計特徴量(Day 099)やRolling・Lag特徴量(Day 100)を組み合わせているうちに、特徴量が数百〜数千個に膨れ上がることが珍しくありません。しかしすべての特徴量が本当に予測に役立つわけではなく、中には「たまたま学習データにだけ相関して見える」ノイズ特徴量が混ざっています。今日は「どの特徴量が本当に効いているか」を見極める3つの代表的な手法を学びます。
⚠️
組み込み重要度(feature_importances_)の弱点: これは「学習データの中で分岐に使われた回数・不純度減少量」の合計に過ぎません。①学習データにしか存在しないノイズによる偶然の分岐も加点される(in-sampleの数値)、②連続値や高カーディナリティ(値の種類が多い)な特徴量ほど分岐の候補が多く有利になりやすい、という構造的なバイアスがあります。今日学ぶ3手法はこのバイアスを避けるためのものです。
用語直感的な意味
Permutation Importanceある特徴量だけをランダムにシャッフルし、testデータでモデル性能がどれだけ落ちるかを測る手法
Boruta全特徴量の「シャッフルしたコピー(シャドウ特徴量)」を用意し、本物の重要度がシャドウ集団の最大値を上回るかを繰り返し検証する手法
SHAP(SHapley Additive exPlanations)ゲーム理論のShapley値を応用し、各特徴量が「1件の予測」にどれだけ貢献したかを公平に配分する手法
mean(|SHAP値|)個々の予測へのSHAP貢献度の絶対値を全サンプルで平均した、SHAPの「グローバル重要度」版

🧩 「本物の特徴量」を見極める3つの検証手法

性能で測る

🔀 Permutation Importance

シャッフルして性能低下を見る

testデータで特徴量を1列ずつシャッフルし、AUC等の性能がどれだけ落ちるかを測る。out-of-sampleなので組み込み重要度のバイアスを受けにくい。モデル非依存で使える。

統計的に判定

🌑 Boruta(シャドウ特徴量)

ランダムなダミー集団と比較する

全特徴量のシャッフル版(シャドウ)を作り、本物の重要度がシャドウ集団の最大値を何度上回るかをカウント。「偶然では説明できない」ことを統計的に検証する発想。

予測ごとに配分

🎲 SHAP

Shapley値で貢献度を公平配分

1件の予測に対し、各特徴量がどれだけ寄与したかをゲーム理論に基づき公平に配分する。グローバル(全体傾向)にもローカル(個別予測)にも使える唯一の手法。

🎯 問題

9個の特徴量(本物の情報を持つ信号5個・純粋なノイズ3個・値の種類が非常に多い疑似ID列1個)の中から「本当に効いている特徴量」を4つの視点で見極める。

使用データSignal×5 + Noise×3 + HighCardID×1
import numpy as np
import pandas as pd
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

RANDOM_STATE = 42
rng = np.random.RandomState(RANDOM_STATE)

# 5個の「本物」信号特徴量
X_base, y = make_classification(
    n_samples=3000, n_features=5, n_informative=5, n_redundant=0,
    n_clusters_per_class=2, class_sep=1.2, random_state=RANDOM_STATE
)
feat_names = [f"Signal{i+1}" for i in range(5)]
df = pd.DataFrame(X_base, columns=feat_names)

# 3個の純粋なノイズ特徴量(yと無関係な標準正規乱数)
for i in range(3):
    df[f"Noise{i+1}"] = rng.normal(0, 1, size=len(df))

# 1個の高カーディナリティな疑似ID列
df["HighCardID"] = rng.randint(0, 100000, size=len(df))

FEATURES = list(df.columns)
X = df[FEATURES].values

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, stratify=y, random_state=RANDOM_STATE
)
カラム意味真の関係
Signal1〜Signal5make_classificationが生成した本物の情報特徴量yと強く関係する
Noise1〜Noise3標準正規分布からの独立な乱数yと完全に無関係
HighCardID0〜99999のランダムな整数(transaction_id等の誤混入を模擬)yと完全に無関係・値の種類が非常に多い

タスク

1
ベースラインモデル: RandomForestClassifier(n_estimators=300, max_depth=6)を学習し、test AUCを算出する
2
組み込み重要度: feature_importances_を取得し、9特徴量をランキングする
3
Permutation Importance: permutation_importanceをtestデータにscoring="roc_auc", n_repeats=30で計算する
4
Boruta風シャドウ特徴量テスト: 各特徴量のシャッフル版を作り、本物の重要度がシャドウの最大値を上回った回数を20試行分カウントする
5
SHAP: shap.TreeExplainerでtestデータのSHAP値を計算し、mean(|SHAP値|)でランキングする
6
比較・考察: 4手法のランキングを1つの表にまとめ、「どの特徴量を残すべきか」を結論づける

📐 組み込み重要度 vs Permutation Importance(各系列の最大値を100%として正規化)

実際に計算した値をもとに作図。オレンジ=組み込み重要度、青=Permutation Importance。ノイズ系4特徴量(右側4組)で、オレンジは薄く残るのに青はほぼ消えることに注目。

9特徴量: 組み込み重要度(橙)と Permutation Importance(青)の正規化比較 100% 0% Signal1 Signal2 Signal3 Signal4 Signal5 Noise1 Noise2 Noise3 HighCard ID ■ 組み込み重要度 ■ Permutation Imp. ノイズ4種は組み込みで5〜6%残るが、Permutationではほぼ0%まで落ちる

📊 実行結果の比較(4手法)

実際にコードを実行して得た値(乱数シード固定・環境により多少前後する)。

Test AUC

RandomForest(max_depth=6)
0.9487
9特徴量投入時点

組み込み重要度 vs Permutation Importance(下位4特徴量に注目)

Noise3: 組み込み重要度
0.0136(最上位ノイズ)
Noise3: Permutation Imp.
-0.0001(実質ゼロ)
HighCardID: 組み込み重要度
0.0124
HighCardID: Permutation Imp.
0.0003(実質ゼロ)

組み込み重要度では「高カーディナリティのHighCardID」より「ただの連続値ノイズNoise3」の方が高い値になった。特徴量の種類にかかわらず、in-sampleの数値は偶然の相関を拾いうる。

Boruta風シャドウ特徴量テスト(20試行中の勝利回数)

Signal1〜Signal5
20 / 20
全試行で本物と判定
Noise3
10 / 20
五分五分=境界事例
HighCardID
6 / 20
ノイズ寄り
Noise2
1 / 20
ほぼノイズ確定
Noise1
0 / 20
ノイズ確定

SHAP: mean(|SHAP値|)

Signal2
0.1140
最上位
Signal1(最弱のSignal)
0.0520
それでもNoise3の12倍以上
Noise3(最強のノイズ)
0.0041
Signal群と明確に分離
Noise1(最弱のノイズ)
0.0016
最下位

SHAPのランキングはPermutation Importanceとほぼ同じ並びになった。Signal群(0.05〜0.11)とノイズ群(0.0016〜0.0041)の間に明確な断絶があり、判断がしやすい。

💡 ヒント

ヒント1方向性

feature_importances_は「学習データの中で、その特徴量による分岐がどれだけ不純度を減らしたか」の合計。学習データだけを見て計算されるため、偶然の相関を「重要」と誤認しうる。Permutation Importanceは未知のtestデータでシャッフル前後の性能差を見るため、より正直に汎化性能への寄与を反映する。2つの手法が大きく違う評価をする場合、信じるべきは基本的に後者(out-of-sample)。

ヒント2アプローチ
  • from sklearn.inspection import permutation_importanceresult = permutation_importance(model, X_test, y_test, n_repeats=30, scoring="roc_auc", random_state=42)result.importances_meanresult.importances_stdが得られる
  • シャドウ特徴量は「各列を独立にシャッフルしたコピー」。np.random.permutation(X_train[:, j])を各列ごとに適用し、np.hstack([X_train, X_shadow])で元の特徴量と横に連結してから学習する
  • Boruta風の判定は「各試行で、本物の特徴量iの重要度 > シャドウ特徴量全体の最大重要度」をTrue/Falseで記録し、複数試行の中でTrueだった回数を数える
  • SHAPはexplainer = shap.TreeExplainer(model), shap_values = explainer.shap_values(X_test)。sklearn/shapのバージョンによって返り値の形(クラスごとのリスト or 3次元配列)が異なるので分岐が必要な場合がある
ヒント3コード骨格
from sklearn.ensemble import RandomForestClassifier
from sklearn.inspection import permutation_importance
from sklearn.metrics import roc_auc_score
import shap

# 1. ベースライン
rf = RandomForestClassifier(n_estimators=300, max_depth=6, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)
auc = roc_auc_score(y_test, rf.predict_proba(X_test)[:, 1])

# 2. 組み込み重要度
imp_builtin = pd.Series(rf.feature_importances_, index=FEATURES).sort_values(ascending=False)

# 3. Permutation Importance(testデータ)
perm = permutation_importance(rf, X_test, y_test, n_repeats=30, random_state=42, scoring="roc_auc")
imp_perm = pd.Series(perm.importances_mean, index=FEATURES).sort_values(ascending=False)

# 4. Boruta風シャドウ特徴量テスト
N_TRIALS = 20
hit_counts = pd.Series(0, index=FEATURES)
for trial in range(N_TRIALS):
    trial_rng = np.random.RandomState(1000 + trial)
    X_shadow = np.column_stack([trial_rng.permutation(X_train[:, j]) for j in range(X_train.shape[1])])
    X_combined = np.hstack([X_train, X_shadow])
    combined_names = FEATURES + [f"shadow_{f}" for f in FEATURES]
    rf_shadow = RandomForestClassifier(n_estimators=150, max_depth=6, random_state=trial_rng, n_jobs=-1)
    rf_shadow.fit(X_combined, y_train)
    imp_combined = pd.Series(rf_shadow.feature_importances_, index=combined_names)
    max_shadow = imp_combined[[f"shadow_{f}" for f in FEATURES]].max()
    for f in FEATURES:
        if imp_combined[f] > max_shadow:
            hit_counts[f] += 1

# 5. SHAP
explainer = shap.TreeExplainer(rf)
shap_values = explainer.shap_values(X_test)
sv = shap_values[1] if isinstance(shap_values, list) else shap_values[:, :, 1]
mean_abs_shap = pd.Series(np.abs(sv).mean(axis=0), index=FEATURES).sort_values(ascending=False)

模範解答

import numpy as np
import pandas as pd
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.inspection import permutation_importance
from sklearn.metrics import roc_auc_score
import shap

RANDOM_STATE = 42
rng = np.random.RandomState(RANDOM_STATE)

# ====== データ準備 ======
X_base, y = make_classification(
    n_samples=3000, n_features=5, n_informative=5, n_redundant=0,
    n_clusters_per_class=2, class_sep=1.2, random_state=RANDOM_STATE
)
feat_names = [f"Signal{i+1}" for i in range(5)]
df = pd.DataFrame(X_base, columns=feat_names)
for i in range(3):
    df[f"Noise{i+1}"] = rng.normal(0, 1, size=len(df))
df["HighCardID"] = rng.randint(0, 100000, size=len(df))

FEATURES = list(df.columns)
X = df[FEATURES].values
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, stratify=y, random_state=RANDOM_STATE
)

# ====== 1. ベースラインモデル ======
rf = RandomForestClassifier(n_estimators=300, max_depth=6, random_state=RANDOM_STATE, n_jobs=-1)
rf.fit(X_train, y_train)
auc = roc_auc_score(y_test, rf.predict_proba(X_test)[:, 1])
print(f"Test AUC: {auc:.4f}")

# ====== 2. 組み込み重要度 ======
imp_builtin = pd.Series(rf.feature_importances_, index=FEATURES).sort_values(ascending=False)

# ====== 3. Permutation Importance(testデータ、n_repeats=30) ======
perm = permutation_importance(rf, X_test, y_test, n_repeats=30, random_state=RANDOM_STATE, scoring="roc_auc", n_jobs=-1)
imp_perm = pd.Series(perm.importances_mean, index=FEATURES).sort_values(ascending=False)

# ====== 4. Boruta風シャドウ特徴量テスト(20試行) ======
N_TRIALS = 20
hit_counts = pd.Series(0, index=FEATURES)
for trial in range(N_TRIALS):
    trial_rng = np.random.RandomState(1000 + trial)
    X_shadow = np.column_stack([trial_rng.permutation(X_train[:, j]) for j in range(X_train.shape[1])])
    X_combined = np.hstack([X_train, X_shadow])
    combined_names = FEATURES + [f"shadow_{f}" for f in FEATURES]
    rf_shadow = RandomForestClassifier(n_estimators=150, max_depth=6, random_state=trial_rng, n_jobs=-1)
    rf_shadow.fit(X_combined, y_train)
    imp_combined = pd.Series(rf_shadow.feature_importances_, index=combined_names)
    max_shadow = imp_combined[[f"shadow_{f}" for f in FEATURES]].max()
    for f in FEATURES:
        if imp_combined[f] > max_shadow:
            hit_counts[f] += 1

# ====== 5. SHAP ======
explainer = shap.TreeExplainer(rf)
shap_values = explainer.shap_values(X_test)
sv = shap_values[1] if isinstance(shap_values, list) else shap_values[:, :, 1]
mean_abs_shap = pd.Series(np.abs(sv).mean(axis=0), index=FEATURES).sort_values(ascending=False)

# ====== 6. 4手法の比較表 ======
comparison = pd.DataFrame({
    "builtin_rank": imp_builtin.rank(ascending=False).astype(int),
    "builtin_value": imp_builtin,
    "perm_rank": imp_perm.rank(ascending=False).astype(int),
    "perm_value": imp_perm,
    "boruta_hits/20": hit_counts,
    "shap_rank": mean_abs_shap.rank(ascending=False).astype(int),
    "shap_value": mean_abs_shap,
}).loc[FEATURES].sort_values("perm_rank")
print(comparison.round(4).to_string())

実際に実行して得た出力(RANDOM_STATE=42固定・環境により多少前後する):

Test AUC: 0.9487

            builtin_rank  builtin_value  perm_rank  perm_value  boruta_hits/20  shap_rank  shap_value
Signal2                1         0.2159          1      0.1268              20          1      0.1140
Signal3                3         0.1929          2      0.1123              20          2      0.0922
Signal1                5         0.1530          3      0.0938              20          5      0.0520
Signal4                4         0.1826          4      0.0680              20          4      0.0713
Signal5                2         0.2054          5      0.0649              20          3      0.0853
HighCardID              7         0.0124          6      0.0003               6          7      0.0036
Noise1                  9         0.0120          7      0.0002               0          9      0.0016
Noise2                  8         0.0123          8      0.0000               1          8      0.0023
Noise3                  6         0.0136          9     -0.0001              10          6      0.0041
🧠
考察(タスク6): 4手法とも「Signal1〜5が本物」で一致しています。組み込み重要度だけを見るとNoise3(0.0136)がHighCardID(0.0124)より高く、「高カーディナリティだけが危ない」という単純な思い込みは誤りだと分かります。Permutation ImportanceとSHAPはNoise1・Noise2・HighCardIDをほぼゼロと明確に判定し、Boruta風テストでも0〜6/20回と低い水準でした。唯一Noise3だけがBorutaで10/20回という境界値になり、これは試行回数を増やして再検証すべき対象です。

🪜 Step-by-Step 解説

1ベースラインモデルを学習する

rf = RandomForestClassifier(n_estimators=300, max_depth=6, random_state=42, n_jobs=-1)
rf.fit(X_train, y_train)
# Test AUC: 0.9487
🔑
なぜこうするか: max_depth=6で意図的に木の深さを制限している。制限しない場合、木は容易に訓練データのノイズにまで適合してしまい(過学習)、後続の4手法の比較がより極端になる。Test AUC=0.9487と十分に高い性能が出ており、「そこそこ良いモデル」の内部で何が起きているかを分析する土台になっている。

2組み込み重要度を確認する — 一見もっともらしいが、罠がある

Signal2 0.2159, Signal5 0.2054, ... Noise3 0.0136, HighCardID 0.0124, Noise2 0.0123, Noise1 0.0120
👀
なぜこうするか: 5つのSignal特徴量が上位を占めるのは正しい傾向に見えるが、注目すべきは下位。Noise3(純粋なノイズ)がHighCardIDより高い重要度(0.0136 > 0.0124)を得ている。訓練データの中でたまたま生じた偶然の相関を拾ってしまえば、どんな種類の特徴量でも組み込み重要度は非ゼロの値を返してしまう。

3Permutation Importanceで「本当に効いているか」を検証する

HighCardID  : mean=0.0003
Noise1      : mean=0.0002
Noise2      : mean=0.0000
Noise3      : mean=-0.0001
🔀
なぜこうするか: testデータでシャッフルした結果、4つの「ノイズ系」特徴量はすべてほぼ0、標準偏差の範囲内に収まる値になった(Noise3にいたっては負の値)。組み込み重要度ではNoise3が0.0136という無視できない値だったのに対し、out-of-sampleで検証すると実際には何の予測力もないことが明確になる。これがPermutation Importanceの核心的な価値。

4Boruta風シャドウ特徴量テストで統計的に判定する

Signal1〜5: 20/20回すべてでシャドウの最大値を上回った
Noise3: 10/20回(五分五分)
HighCardID: 6/20回
Noise2: 1/20回
Noise1: 0/20
🌑
なぜこうするか: 5つのSignal特徴量は20試行すべてでシャドウ特徴量の最大値を上回り、疑いの余地なく「本物」と判定できる。一方Noise3は10/20回と五分五分——偶然そのもの(ノイズなので、シャドウ集団の中のたまたま一番強いノイズより強い日もあれば弱い日もある)。本家のBorutaパッケージはこの「五分五分」を厳密な二項検定で判定し、既定では最大100試行を回す。今回の簡易実装(20試行)で境界事例が生まれるのは自然で、試行回数を増やすほど判定は安定する。

5SHAPでモデルの判断根拠を特徴量に分配する

Signal2 0.1140 … Noise3 0.0041, HighCardID 0.0036, Noise2 0.0023, Noise1 0.0016
🎲
なぜこうするか: SHAPのランキングはPermutation Importanceとほぼ同じ並び(Signal群が上位、ノイズ群が下位)になったが、値そのものの意味は異なる。SHAPのmean(|SHAP値|)は「予測値にどれだけ影響したか」の平均的な大きさを表すのに対し、Permutation Importanceは「性能指標(AUC)がどれだけ落ちるか」を表す。測っているものが違うのに、今回はランキングがほぼ一致したことで、この2手法の結論に対する確信が強まる。

64手法をまとめて「残すべき特徴量」を決める

🧭
なぜこうするか: 組み込み重要度だけを見ていたら「9特徴量すべてに多少の重要度があるから全部残そう」という誤った判断をしがちである。しかしPermutation Importance・Boruta・SHAPの3手法がそろって「Signal1〜5だけが本物」と示しており、Noise1・Noise2・HighCardIDは自信を持って除外できる。Noise3だけは唯一Boruta風テストで境界値になったため、試行回数を増やして再検証するか保守的に除外するかの判断が必要——1つの手法だけで決め切らず、複数手法が一致する部分を信頼するのが今日一番の学び。

🧮 数学・統計の補足(文系向け)

📐
直感的な理解: 組み込み重要度は「テスト前の予想問題集の的中率」、Permutation Importanceは「本番の試験でその範囲を外した場合の点数の下がり方」に例えられる。予想問題集(訓練データ)だけを見ていると「この単元は重要そうだ」と誤って思い込むことがあるが、本番の試験(testデータ)で実際に効果を検証しない限り、本当に点数に直結する範囲かはわからない。
🌑
身近な例(Boruta): 宝くじの当選番号予測で「今週の当選番号予測に◯◯という占いが当たった」と主張する人がいたとする。それが本物の予知能力かを確かめるには、完全にランダムな数字を予測に使うダミーの占い師を何人も用意し、その占い師たちの最高成績と、当の占い師の成績を毎週比較する。もし当の占い師がダミーの誰よりも一貫して当たり続けるなら「本物かもしれない」となるが、たまに勝ってたまに負ける程度なら「ただの偶然」と判断すべき。Borutaのシャドウ特徴量は、この「ランダムなダミー集団」に相当する。

数式を見たら

PermImp(f) = Score(y, ŷ) − Score(y, ŷ_f shuffled)

「特徴量fをシャッフルする前のスコア」から「シャッフルした後のスコア」を引いたもの。効いている特徴量ほどシャッフルでスコアが大きく下がる(差が大きい)ため、この差=重要度になる。

φ_i = Σ_S [ |S|!(n−|S|−1)! / n! ] × [v(S∪{i}) − v(S)]

これがShapley値(SHAPの理論的な土台)。文系向けに言い換えると「特徴量iを、他の特徴量のあらゆる組み合わせの順番に追加したときの貢献度の平均」。チームの成果(賞金)をメンバー全員に公平に配分する際、「この人が加入する前と後でチームの成果がどれだけ変わったか」を、あらゆる加入順序で計算して平均する——という考え方と同じ。

🏆 Kaggleでの実践的な使い方

よく使われるコンペカテゴリ: ☑ 表形式データ(Tabular) / ☐ 自然言語処理(NLP) / ☐ 画像認識(CV) / ☑ 時系列(Time Series)

  • 特徴量エンジニアリング後の絞り込み: 集計特徴量・Rolling/Lag特徴量(Day 099・100)を大量に生成した後、Permutation ImportanceかSHAPのmean(|SHAP値|)が低い特徴量を機械的に足切りし、上位N個だけで再学習することでCVスコアと学習速度の両方が改善することが多い
  • Boruta(boruta_py: 過去の上位解法ノートブックで「Borutaで数百の候補特徴量から本物の特徴量だけを抽出した」という報告は頻出。計算コストが高い(多数のモデルを繰り返し学習する)ため、GBDT系の高速版と組み合わせて使うのが実務的
  • SHAPは「特徴量選択」と「モデル解釈」の二役: Kaggleの最終レポート(Solution write-up)ではSHAP summary plotで「どの特徴量がなぜ効いたか」を説明するのが定番。Public/Privateのシェイクアップ対策として、特定の1〜2個の特徴量にSHAP依存度が偏りすぎていないか確認するのにも使われる
  • 相関の強い特徴量への注意: Permutation Importanceは、ある特徴量をシャッフルしても相関する別の特徴量が同じ情報を持っていれば「性能があまり落ちない=重要度が低く見える」という弱点がある。強相関のグループはまとめてシャッフルする「grouped permutation importance」で対処するのが上位陣のテクニック

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
feature_importances_が高ければ、その特徴量は本物の予測力を持つと信じてよいsklearnが標準搭載しており最も手軽に見られる指標のため今日の実行結果では純粋なノイズであるNoise3(0.0136)が高カーディナリティのHighCardID(0.0124)より高い値になった。組み込み重要度は訓練データ内の偶然の相関にも反応するため、単独で判断材料にしてはいけない
高カーディナリティな特徴量だけが組み込み重要度のバイアスを受けると思う教科書的に「IDのような列が危ない」という説明をよく見るため今回のように、特別高カーディナリティでなくてもただの連続値ノイズが偶然高い重要度を得ることがある。特徴量の種類にかかわらず、out-of-sampleで検証する習慣が必要
Permutation Importanceが0や負の値になった特徴量は完全に無価値だと断定してよい「数値が0=無関係」という直感今回のように真にノイズなら妥当な結論だが、相関の強い特徴量が他にある場合、片方をシャッフルしても情報が別の特徴量から補われ、見かけ上重要度が低く出ることがある。相関構造も合わせて確認する必要がある
Boruta風テストは1回や少数回の試行で確定的な結論を出せる「1回でも基準を満たせば十分」という思い込み今日のNoise3(20回中10回)のように、試行回数が少ないと純粋なノイズでも五分五分の結果が出ることがある。本家Borutaが多数回の試行と二項検定を使うのはこのため
SHAPとPermutation Importanceは常に同じランキングになるはずだどちらも「特徴量の重要度」を測る手法という理解にとどまっているためSHAPは「予測値への貢献」、Permutation Importanceは「性能指標の低下量」を測っており、測定対象が異なる。今日はたまたまほぼ一致したが、特徴量間の相関が強い場合など、ランキングが食い違うことは珍しくない

🚀 次のステップ

  • 発展: Signal1〜5同士に人工的な相関を持たせたデータ(例: Signal1_dup = Signal1 + 少量のノイズ)を追加し、Permutation Importanceで元のSignal1Signal1_dupの重要度がどう分散するかを確認してみる。相関特徴量が重要度を「薄める」現象を体験できる
  • 次回予告: Day 108「SHAP値の解釈(グローバル/ローカル解釈・Kaggleでの活用)」 — 今日はモデル全体の傾向(グローバル)だけを見たが、次回は1件の予測ごとに「なぜこの予測になったか」を説明するローカル解釈(force plot・waterfall plot)とKaggleでの具体的な活用法に進む

📝 自己評価(解いた後に記入)

自分の回答・気づき・メモ: