特徴選択の基本

入門

2.7.1

特徴選択の基本

最終更新 2020-01-29 読了時間 2 分
まとめ
  • 特徴選択(Feature Selection)は不要な特徴量を除外し、過学習の防止・計算効率化・解釈性の向上を実現する前処理。
  • フィルター法・ラッパー法・埋め込み法の3つのアプローチを理解し、使い分ける。
  • scikit-learnのSelectKBestmutual_info_classifを使った基本的な実装パターンを学ぶ。

直感 #

特徴量が増えると表現力は上がりますが、同時にノイズも増え、モデルが学習データに過剰適合しやすくなります。特徴選択は「本当に予測に役立つ特徴量だけを残す」処理で、モデルの汎化性能を高めるために重要です。手法は大きく3つに分かれます。

詳細な解説 #

3つのアプローチ #

手法概要代表例
フィルター法統計量でスコアリングし、閾値で選択分散、相関、カイ二乗検定、相互情報量
ラッパー法モデルの評価指標で特徴の組み合わせを探索RFE(再帰的特徴除去)、前方選択、後方除去
埋め込み法モデル学習の過程で重要度を算出Lasso(L1正則化)、ランダムフォレスト重要度

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_selection import (
    SelectKBest,
    mutual_info_classif,
    f_classif,
)

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,
)

フィルター法: 相互情報量でスコアリング #

各特徴量とターゲットの相互情報量を計算し、スコアが高いものを選びます。

1
2
3
4
5
6
7
8
mi_scores = mutual_info_classif(X, y, random_state=42)

plt.figure(figsize=(10, 4))
plt.bar(range(len(mi_scores)), mi_scores)
plt.xlabel("特徴量インデックス")
plt.ylabel("相互情報量")
plt.title("特徴量ごとの相互情報量スコア")
plt.show()

選択数kと精度の関係 #

SelectKBestで上位k個を選び、ランダムフォレストの交差検証スコアを比較します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
k_values = range(1, 21)
scores = []
for k in k_values:
    selector = SelectKBest(mutual_info_classif, k=k)
    X_selected = selector.fit_transform(X, y)
    rf = RandomForestClassifier(n_estimators=50, random_state=42)
    score = cross_val_score(rf, X_selected, y, cv=5, scoring="accuracy").mean()
    scores.append(score)

plt.figure(figsize=(10, 4))
plt.plot(k_values, scores, "o-")
plt.xlabel("選択する特徴量の数 (k)")
plt.ylabel("交差検証 Accuracy")
plt.title("特徴量の数と精度の関係")
plt.grid(True)
plt.show()

F検定によるフィルタリング #

分散分析のF値を使う方法もあります。線形な関係を前提とするため、非線形な依存は見逃す可能性があります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
selector_f = SelectKBest(f_classif, k=10)
X_f = selector_f.fit_transform(X, y)

rf = RandomForestClassifier(n_estimators=50, random_state=42)
score_f = cross_val_score(rf, X_f, y, cv=5, scoring="accuracy").mean()
score_mi = cross_val_score(
    rf,
    SelectKBest(mutual_info_classif, k=10).fit_transform(X, y),
    y, cv=5, scoring="accuracy",
).mean()
print(f"F検定 (k=10):   {score_f:.4f}")
print(f"相互情報量 (k=10): {score_mi:.4f}")

埋め込み法: ランダムフォレストの特徴量重要度 #

モデル自体が学習過程で算出する重要度を使って選択する方法です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
rf_full = RandomForestClassifier(n_estimators=100, random_state=42)
rf_full.fit(X, y)

importances = rf_full.feature_importances_
indices = np.argsort(importances)[::-1]

plt.figure(figsize=(10, 4))
plt.bar(range(len(importances)), importances[indices])
plt.xlabel("特徴量(重要度順)")
plt.ylabel("重要度")
plt.title("ランダムフォレスト 特徴量重要度")
plt.show()

# 重要度が一定以上の特徴量を選択
threshold = 0.05
selected = np.where(importances >= threshold)[0]
print(f"閾値 {threshold} 以上の特徴量: {len(selected)} 個")
  • Boruta — ランダムフォレストベースの特徴選択手法