Pythonに幾つかの自動特徴量エンジニアリング(Automatic Feature Engineering)のためのパッケージがあります。
その中の1つに「AutoFeat」というものがあります。回帰問題と分類問題で利用できます。
AutoFeatは非常に簡単に使えます。ただ、完全な特徴量エンジニアリング(Feature Engineering)を実施してくれるわけではありません。例えば、欠測値補完や異常値処理、正規化処理などは実施しません。
何をやるかと言うと……
- 元の特徴量からたくさんの非線形の特徴量を生成し「特徴量プール」を作ります
- 「特徴量プール」から有効な特徴量を選択します(特徴量選択)
……という感じです。
特徴量選択の機能だけ使いたい、という方もいるかもしれません。ということで今回は、特徴量選択の機能だけ使う場合のやり方について、簡単に説明します。
Contents
【ちょっと理論的なお話し】AutoFeatの特徴量選択方法
先ず、AutoFeatの特徴量選択方法について、簡単な解説をします。興味のない方は、飛ばしてください。
先ほどの説明の繰り返しになりますが、ざっくり説明するとAutoFeatの特徴量エンジニアリング(Feature Engineering)は、次のようになっています。
- 元の特徴量からたくさんの非線形の特徴量を生成し「特徴量プール」を作ります
- 「特徴量プール」から有効な特徴量を選択します(特徴量選択)
「特徴量プール」とは、元の特徴量と新たに生成した非線形の特徴量を合わせた特徴量の集合のことです。この「特徴量プール」に対し、特徴量選択を実施していきます。
特徴量選択は、ざっくり次の2つのステップからなります。
- 先ず、相関の高い特徴量を削る
- 次に、L1正則化で特徴量を選択する
相関の高い特徴量を削るとき、より単純な特徴量(元の特徴量に近い方)を残します。
例えば、xとzという元の特徴量からx^2とz^2という特徴量を作ったとします。xとz^2の相関が高いときxの方を残しz^2の方を削ります。
L1正則化で特徴量で選択するとき、Lasso LARS回帰モデルとL1正則化ロジスティック回帰モデルを活用したラッパー法(Wrapper Method)を使い、特徴量選択をしています。
- 回帰問題に対しLasso LARS回帰モデル
- 分類問題に対しL1正則化ロジスティック回帰モデル
L1正則化で特徴量を選択するとき、「特徴量プール」から「初期セット」を選びます。そして、「特徴量プール」に残ったものを幾つかに分割し「特徴量のチャンク(塊)」を作ります。「初期セット」に各チャンクを追加し特徴量セットを作りモデルをフィットさせ、特徴量選択の検討を実施していきます。そういう意味でラッパー法(Wrapper Method)です。
ラッパー法(Wrapper Method)に関しては以下の記事で簡単に解説していますので、参考にして頂ければと思います。
第232話|3タイプの特徴量エンジニアリング(feature engineering)基礎テクニック
https://www.salesanalytics.co.jp/column/no00232/#Wrapper_Method
ちなみに、「初期セット」は、すべての特徴量を使いL1正則化線形モデルを学習し、係数の絶対値の大きな特徴量を選んだものです。
ロバスト性(頑健性)を高めるために、データのサンプリングなどを何度も何度も実施し、このようなことを何度も何度も繰り返し検討し、比較的いつも残る(選択される)特徴量を、最終的な特徴量として選択します。
なぜでこのような面倒なことをするのか? という疑問もあることでしょう。
最初に実施する、「すべての特徴量を使いL1正則化線形モデルを学習し、係数の絶対値の大きな特徴量を選ぶ」、それだけでいいのではないか? と思うかもしれません。
しかし、それでは上手くいかないことが知られています。
「すべての特徴量を使いL1正則化線形モデルを学習し、係数の絶対値の大きな特徴量を選ぶ」だけで上手くいく条件があります。それは、特徴量同士の相関が低い場合です。
今回の場合ですと、元の特徴量から非線形な変換をして作っていることもあり、特徴量同士の相関が低いとは言い切れず、どちらかというと相関の高い特徴量の組み合わせが混じっている可能性が高いです。今回の例に限らず、どちらかというと相関の高い特徴量の組み合わせが混じっている可能性が高いため、「すべての特徴量を使いL1正則化線形モデルを学習し、係数の絶対値の大きな特徴量を選ぶ」という方法は多くの場合あまり適切とは言えません。
気になる方は、以下を参考にして頂ければと思います。
Agnostic feature selection
https://hal.archives-ouvertes.fr/hal-02436824
そのため、AutoFeatではこのようなアプローチで特徴量選択を実施しています。
AutoFeatのFeature Selectorを使って見よう!
今回は、意図的に特徴量(説明変数)を膨大にするために、Scikit-Learn(sklearn)を使い以下のようなサンプルデータセットを生成し、特徴量選択を実施しています。
- 回帰問題用に生成するサンプルデータ
- サンプル数:1,100
- 特徴量の数:1,000
- 分類問題用に生成するサンプルデータ
- サンプル数:1,100
- 特徴量の数:1,000
- 目的変数のクラス数:2
ライブラリーの読み込み
先ず、必要なライブラリーを読み込みます。
# ライブラリーの読み込み import pandas as pd from autofeat import FeatureSelector from sklearn.datasets import make_regression, make_classification from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression,LogisticRegression from sklearn.metrics import r2_score,accuracy_score
回帰問題に対する特徴量選択例
次に、サンプルデータセットを生成します。
以下、コードです。
# サンプルデータの生成 X, y = make_regression(n_samples=1100, #サンプルの数 n_features=1000, #特徴量の数 n_informative=10, #目的変数と関連性の強い特徴量の数 noise=10, #ノイズの標準偏差 bias=0, #バイアス(切片) random_state=12) X = pd.DataFrame(X) y = pd.DataFrame(y)
生成したデータセットを学習データとテストデータに分割します。
以下、コードです。
# データセットの分割(学習データとテストデータ) X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.5, test_size=0.5, random_state=42)
- 学習データ
- 特徴量:X_train
- 目的変数:y_train
- テストデータ
- 特徴量:X_test
- 目的変数:y_test
変数選択用のモデルを定義します。
以下、コードです。
# モデル定義 model = FeatureSelector(problem_type="regression",verbose=1)
学習データを利用し特徴量選択を実施します。
以下、コードです。
# 特徴量選択(学習データ利用) X_train_fsel = model.fit_transform(X_train, y_train)
以下、実行結果です。
元の特徴量からどのくらい減ったのかを見てみます。
以下、コードです。
print("number of features in X_train:", X_train.shape[1]) #元の特徴量Xの数 print("number of features in X_train_fsel:", X_train_fsel.shape[1]) #選択された特徴量Xの数
以下、実行結果です。
1,000個あった特徴量が、23個に減っています。
これで、モデルの予測精度が恐ろしく悪化したのでは、身も蓋もありません。
テストデータで確認してみます。
この自動選択した特徴量と同じ特徴量のテストデータを作ります。X_test_fselに格納します。
以下、コードです。
# テストデータの特徴量生成 X_test_fsel = model.transform(X_test)
元の特徴量のデータセット(X_trainとy_train)と新たな特徴量のデータセット(X_train_fselとy_train)で、線形回帰(重回帰)モデルを構築してみます。
以下、コードです。
# モデル構築 model_1 = LinearRegression().fit(X_train,y_train) model_2 = LinearRegression().fit(X_train_fsel, y_train)
モデルの精度(R2:決定係数)を比較してみたいと思います。学習データとテストデータでそれぞれで比較してみます。
先ず、学習データでモデルの精度(R2:決定係数)を比較します。
以下、コードです。
# 評価(R2) ※学習データ print("model_1 R^2: %.4f" % r2_score(y_train, model_1.predict(X_train)) ) print("model_2 R^2: %.4f" % r2_score(y_train, model_2.predict(X_train_fsel)))
以下、実行結果です。
学習データを使った精度検証なので、基本高精度になります。
元の特徴量のデータセットを使った方の決定係数は100%と高精度です。一方、特徴量を絞った新たな特徴量のデータセットを使った方の決定係数は99.71%と精度が若干落ちます。
特徴量の数が減ったので当然の結果ですが、大幅に特徴量を減らした割に、それほど精度悪化していないことが分かります。
次に、テストデータでモデルの精度(R2:決定係数)を比較します。
以下、コードです。
# 評価(R2) ※テストデータ print("model_1 R^2: %.4f" % r2_score(y_test, model_1.predict(X_test)) ) print("model_2 R^2: %.4f" % r2_score(y_test, model_2.predict(X_test_fsel)))
以下、実行結果です。
テストデータを使った精度検証なので、学習データに対する決定係数よりも基本悪化します。問題は、どの程度悪化するのか、ということになります。
元の特徴量のデータセットを使った方の決定係数は49.39%と悪化します。一方、特徴量を絞った新たな特徴量のデータセットを使った方の決定係数は99.47%と高精度のままです。
特徴量選択が上手くいっていることが分かるかと思います。
分類問題に対する特徴量選択例
先ず、サンプルデータセットを生成します。
以下、コードです。
# サンプルデータの生成 X, y = make_classification(n_samples=1100, #サンプルの数 n_features=1000, #特徴量の数 n_informative=10, #目的変数と関連性の強い特徴量の数 n_classes=2, #目的変数のクラスの数 random_state=12) X = pd.DataFrame(X) y = pd.DataFrame(y)
生成したデータセットを学習データとテストデータに分割します。
以下、コードです。
# データセットの分割(学習データとテストデータ) X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.5, test_size=0.5, random_state=42)
- 学習データ
- 特徴量:X_train
- 目的変数:y_train
- テストデータ
- 特徴量:X_test
- 目的変数:y_test
変数選択用のモデルを定義します。
以下、コードです。
# モデル定義 model = FeatureSelector(problem_type="classification",verbose=1)
学習データを利用し特徴量選択を実施します。
以下、コードです。
# 特徴量選択(学習データ利用) X_train_fsel = model.fit_transform(X_train, y_train)
以下、実行結果です。
元の特徴量からどのくらい減ったのかを見てみます。
以下、コードです。
print("number of features in X_train:", #元の特徴量Xの数 X_train.shape[1]) print("number of features in X_train_fsel:", X_train_fsel.shape[1]) #選択された特徴量Xの数
以下、実行結果です。
1,000個あった特徴量が、13個に減っています。
これで、モデルの予測精度が恐ろしく悪化したのでは、身も蓋もありません。
テストデータで確認してみます。
この自動選択した特徴量と同じ特徴量のテストデータを作ります。X_test_fselに格納します。
以下、コードです。
# テストデータの特徴量生成 X_test_fsel = model.transform(X_test)
元の特徴量のデータセット(X_trainとy_train)と新たな特徴量のデータセット(X_train_fselとy_train)で、ロジスティック回帰モデルを構築してみます。
以下、コードです。
# モデル構築 ## model_1 model_1 = LogisticRegression(class_weight='balanced') model_1.fit(X_train,y_train) ## model_2 model_2 = LogisticRegression(class_weight='balanced') model_2.fit(X_train_fsel, y_train)
モデルの精度(accuracy:正答率)を比較してみたいと思います。学習データとテストデータでそれぞれで比較してみます。
先ず、学習データでモデルの精度(accuracy:正答率)を比較します。
以下、コードです。
# 評価(accuracy) ※学習データ print("model_1 accuracy:%.4f" % accuracy_score(y_train, model_1.predict(X_train)) ) print("model_2 accuracy:%.4f" % accuracy_score(y_train, model_2.predict(X_train_fsel)))
以下、実行結果です。
学習データを使った精度検証なので、基本高精度になります。
元の特徴量のデータセットを使った方の正答率は100%と高精度です。一方、特徴量を絞った新たな特徴量のデータセットを使った方の正答率は88.55%と精度が若干落ちます。
特徴量の数が減ったので当然の結果ですが、大幅に特徴量を減らした割に、それほど精度悪化していないことが分かります。
次に、テストデータでモデルの精度(accuracy:正答率)を比較します。
以下、コードです。
# 評価(accuracy) ※テストデータ print("model_1 accuracy:%.4f" % accuracy_score(y_test, model_1.predict(X_test)) ) print("model_2 accuracy:%.4f" % accuracy_score(y_test, model_2.predict(X_test_fsel)))
以下、実行結果です。
テストデータを使った精度検証なので、学習データに対する正答率よりも基本悪化します。問題は、どの程度悪化するのか、ということになります。
元の特徴量のデータセットを使った方の正答率は75.64%と悪化します。一方、特徴量を絞った新たな特徴量のデータセットを使った方の正答率は81.64%と高精度のままです。
特徴量選択が上手くいっていることが分かるかと思います。
まとめ
人によっては、特徴量選択の機能だけ使いたい、という方もいるかもしれません。今回は、特徴量選択の機能だけ使う場合のやり方について、簡単に説明しました。
PythonのAutoFeatを使った自動特徴量エンジニアリング(Automatic Feature Engineering)その1(回帰問題)
PythonのAutoFeatを使った自動特徴量エンジニアリング(Automatic Feature Engineering)その2(分類問題)