ナード戦隊データマン

データサイエンスを用いて悪と戦うぞ

特徴量の自動選択

特徴量が少ない場合は、EDAなどによって手動で特徴量を選ぶことができますが、特徴量が多い場合は自動的な方法に頼ったほうが良い場合があります。特徴量を減らすと、モデルの複雑さが減るので、汎化性能が高まります。ここでは、3つの方法を考えます。

SelectPercentile

SelectPercentileでは、ANOVAによる単変量によって特徴量を選択します。p値の大きな特徴量を除去し、指定した割合の特徴量が残るようにします。

%matplotlib inline
from sklearn.datasets import load_breast_cancer
from sklearn.feature_selection import SelectPercentile
from sklearn.model_selection import train_test_split
import numpy as np
import matplotlib.pyplot as plt

cancer = load_breast_cancer()

rng = np.random.RandomState(42)
noise = rng.normal(size=(len(cancer.data), 50))

print("original:{}".format(cancer.data.shape))

X_w_noise = np.hstack([cancer.data, noise])
X_train, X_test, y_train, y_test = train_test_split(X_w_noise, cancer.target, random_state=0, test_size=.5)
select = SelectPercentile(percentile=50)
select.fit(X_train, y_train)
X_train_selected = select.transform(X_train)

print("noised:{}".format(X_train.shape))
print("selected:{}".format(X_train_selected.shape))
original:(569, 30)
noised:(284, 80)
selected:(284, 40)

上記は、targetとは全く関係のないノイズを特徴量に50個追加したものです。特徴量選択を行うと、40まで削減されていることがわかります。どの特徴量が選択されたのか視覚化してみましょう。

plt.matshow(select.get_support().reshape(1, -1), cmap="gray_r")
plt.xlabel("sample index")

f:id:mathgeekjp:20170912182005p:plain

元の特徴量は概ね、選択されていることがわかります。しかし、完璧ではないのでノイズも選択されています。ノイズを含んだデータと特徴量選択したデータを使って、精度分析の結果を比較してみましょう。

from sklearn.linear_model import LogisticRegression

X_test_selected = select.transform(X_test)
lr = LogisticRegression()
print("noised  :{}".format(lr.fit(X_train, y_train).score(X_test, y_test)))
print("selected:{}".format(lr.fit(X_train_selected, y_train).score(X_test_selected, y_test)))
noised  :0.9298245614035088
selected:0.9403508771929825

このように、特徴量選択を行うと精度が上がります。

SelectFromModel

SelectFromModelは、機械学習を用いて特徴量選択する方法です。ランダムフォレストなどのモデルを指定し、スレッショルド(基準となるしきい値)を指定して使います。使用するモデルは、特徴量の重要性指標を出力するものでなければなりません。

from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier

select = SelectFromModel(
    RandomForestClassifier(n_estimators=100,random_state=42), 
    threshold="median").fit(X_train, y_train)
X_train_selected = select.transform(X_train)
X_test_selected = select.transform(X_test)

plt.matshow(select.get_support().reshape(1, -1), cmap="gray_r")
plt.xlabel("sample index")

print("noised  :{}".format(lr.fit(X_train, y_train).score(X_test, y_test)))
print("selected:{}".format(lr.fit(X_train_selected, y_train).score(X_test_selected, y_test)))

f:id:mathgeekjp:20170912182541p:plain

noised  :0.9298245614035088
selected:0.9508771929824561

RFE

RFEは、全ての特徴量を含んだモデルから開始し、指定した数になるまで重要性の最も低い特徴量を削減する方法です。モデルベースと同様に、選択するモデルは特徴量の重要性を出力するものである必要があります。

from sklearn.feature_selection import RFE
select = RFE(
    RandomForestClassifier(n_estimators=100, random_state=42), 
    n_features_to_select=40).fit(X_train, y_train)
X_train_selected = select.transform(X_train)
X_test_selected = select.transform(X_test)

plt.matshow(select.get_support().reshape(1, -1), cmap="gray_r")
plt.xlabel("sample index")

print("noised  :{}".format(lr.fit(X_train, y_train).score(X_test, y_test)))
print("selected:{}".format(lr.fit(X_train_selected, y_train).score(X_test_selected, y_test)))

f:id:mathgeekjp:20170912182851p:plain

noised  :0.9298245614035088
selected:0.9508771929824561

参考

  1. 1.13. Feature selection — scikit-learn 0.19.0 documentation
  2. Introduction to Machine Learning with Python - O'Reilly Media