機械学習:特徴量の取捨選択
前記事の正則化のポイントは、学習モデルに対して多すぎる特徴量の削減です。特徴量の取捨選択は、次元削減とも言われ、過学習を避ける方法の一つです。
学習モデルに有効な特徴量だけを残す方法には、特徴量選択(feature selection)と特徴量抽出(feature extraction)の二つの方法があり、機械学流のアルゴリズムに応じて使い分けます。
特徴量選択アルゴリズム
貪欲探索(greedy search algorithm)の一種で、問題に最も寄与する特徴量の部分集合を作成して、計算効率を改善できる、改善に無関係な特徴量や雑音を取り除いていきます。正則化をサポートしていない学習モデルや、正則化を行わない学習モデルの決定木やRandomForestに有効です。
選択アルゴリズムの逐次後退選択(SBS:Sequential Backward Selection)は、sklearnには実装されていませんが、考え方はシンプルで、最小化したいCost Functionを$${\bm{J}}$$とし、この$${\bm{J}}$$の低下に最も少ない寄与の特徴量を段階的に削除していきます。
全特徴量空間次元を$${d}$$として、$${k=d}$$と初期化する。
$${\bm{J}}$$の値を最大にする特徴量$${\bm{x}^-}$$を特定する。$${\bm{x}^- = argmax \bm{J}(\bm{X}_k-\bm{x} ) }$$
特徴量集合からこの$${\bm{x}^-}$$を削除する
$${k}$$が目的の特徴量空間次元となるまで2、3を繰り返す
RandomForestを使って、特徴量の重要度を評価してそれを特徴量の選択に使うこともできます。データ同士が線形分離不可であっても、Forest内の全ての決定木から計算された不純度の平均的減少量を使い、特徴量の重要度を測定する方法です。
RandomForestClassifierでフィットした後、scikit_learnのRainForestで計算してくれる特徴量の重要度を通じて、feature_importtance_を使って値を取得します。
ただし、この方法は特徴量に相関性があった場合を考慮に入れていません。特徴量の解釈が必要なケースでは、注意が必要です。
サンプルデータとして、全記事と同様のdf_wineを使用します。
from sklearn.model_selection import train_test_split
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=0, stratify=y)
from sklearn.ensemble import RandomForestClassifier
forest = RandomForestClassifier(n_estimators=500,random_state=0)
forest.fit(X_train, y_train)
importances = forest.feature_importances_
indices = np.argsort(importances)[::-1]
for f in range(X_train.shape[1]):
print("%2d) %-*s %f" % (f + 1, 30,
df_wine.columns[indices[f]+1],
importances[indices[f]]))
この結果の重要度が高い方から並べてみました。

RandomForestClassifierを定義した後、sciket_learnのSelectFromModelクラスを使うと、ユーザが指定した閾値以上の重要度をもつ特徴量を選択してくれます。
from sklearn.model_selection import train_test_split
X, y = df_wine.iloc[:, 1:].values, df_wine.iloc[:, 0].values
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=0, stratify=y)
from sklearn.ensemble import RandomForestClassifier
forest = RandomForestClassifier(n_estimators=500,random_state=0)
from sklearn.feature_selection import SelectFromModel
sfm = SelectFromModel(forest, threshold=0.1, prefit=True)
X_selected = sfm.transform(X_train)
for f in range(X_selected.shape[1]):
print("%2d) %-*s %f" % (f + 1, 30,
df_wine.columns[indices[f]+1],
importances[indices[f]]))

前記事の正則化の結果と比べてみるのもお面白いと思います。
ここで重要と判断されているものがガッツリ落とされていたり、いらないだろうと判断されているもののweightが高かったりしています。
このように、特徴量の次元削減は非常にデリケートな問題なのです。