機械学習や深層学習のアルゴリズムは,様々な種類の特徴から構成されるデータから学習する.機械学習アルゴリズムの学習時間と性能は、データセットの特徴量に大きく依存する。
理想的には、機械学習モデルの学習に実際に役立つ特徴量のみをデータセットに残すべきである。
不要な特徴や冗長な特徴は、アルゴリズムの学習時間を遅くするだけでなく、アルゴリズムの性能にも影響を与える。
機械学習モデルの学習に最も適した特徴を選択するプロセスを「特徴選択」と呼ぶ。
機械学習モデルを学習する前に特徴選択を行うことにはいくつかの利点があり、以下にそのいくつかを挙げる。
- 少ない素性数で高い説明度を得ることができる。
- 少ない素性で機械学習モデルを実装することが容易である。
- 少ない素性は汎化性を高め、オーバーフィッティングを減少させる。
- 特徴量の選択により、データの冗長性を排除できる
- 少ない素性によるモデルの学習時間は大幅に短縮される
- 特徴数が少ないモデルはエラーが起こりにくい
機械学習アルゴリズムに最適な素性を選択するために、いくつかの方法が開発されてきた。
このような手法の1つがフィルタリング手法と呼ばれるものである。
本稿では、特徴量の選択に用いる基本的なフィルタ手法をいくつか紹介する。
特徴抽出のためのフィルタ法
フィルタ法は、機械学習アルゴリズムのモデルとは独立に特徴を選択する特徴選択法のカテゴリに属する。
これはフィルタ法の最大の利点の一つである。
フィルター法を用いて選択された特徴は、あらゆる機械学習モデルの入力として用いることができる。
また、非常に高速であることもフィルタリング手法の利点である。
フィルター法は一般に、特徴選択パイプラインの最初のステップとなる。
フィルター法は大きく2つのカテゴリに分類される。
一変量フィルター法と多変量フィルター法である。
一変量フィルター法は、個々の特徴を特定の基準に従ってランク付けするタイプの方法である。
そして、上位N個の特徴が選択される。
一変量フィルター法では、例えばフィッシャースコア、相互情報量、特徴の分散など、さまざまな種類のランキング基準が使用される。
一変量フィルタ法の大きな欠点は、個々の特徴間の関係が考慮されないため、冗長な特徴を選択する可能性があることである。
一変量フィルタ法は、データから定数や準定数の特徴を除去するのに適している。
多変量フィルター法は、特徴間の相互関係を考慮するため、データから冗長な特徴を除去することが可能である。
多変量フィルター法は、データから重複特徴や相関特徴を除去するのに利用できる。
今回は、Pythonを使って、データセットから定数特徴、準定数特徴、重複特徴、相関特徴を除去する方法を紹介します。
定数機能の削除
定数特徴とは、データセット内のすべての出力に対して1つの値のみを含むタイプの特徴です。
定数特徴は、手元のレコードの分類に役立つ情報を提供しない。
したがって、データセットからすべての定数素性を削除することが望ましいです。
それでは、どのようにデータセットから定数特徴を削除するか見てみましょう。
この例で使用するデータセットは、KaggleからダウンロードできるSantandar Customer Satisfaction datasetです。
ファイル名は “train.csv “を使用します。
ただし、読みやすさのために「santandar_data.csv」にリネームしています。
必要なライブラリとデータセットのインポート
定数特徴量は、すべての値が同じであるため、分散がゼロの値を持つ。
PythonのScikit Learnライブラリの VarianceThreshold
関数を用いて、定数列を求めることができます。
以下のスクリプトを実行し、必要なライブラリとデータセットをインポートする。
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import VarianceThreshold
santandar_data = pd.read_csv(r"E:Datasetssantandar_data.csv", nrows=40000)
santandar_data.shape
上位4万件のレコードをフィルタリングしてみました。
出力には、(40000, 371)と表示されます。
これは、データセットに4万行と371列があることを意味します。
データをトレーニングセットとテストセットに分ける
ここで重要なことは、オーバーフィッティングを避けるために、特徴選択はトレーニングセットのみに適用することです。
それでは、データをトレーニングセットとテストセットに分割してみましょう。
以下のスクリプトを実行する。
train_features, test_features, train_labels, test_labels=train_test_split(
santandar_data.drop(labels=['TARGET'], axis=1),
santandar_data['TARGET'],
test_size=0.2,
random_state=41)
分散閾値を用いた定数特徴量の除去
次は、定数特徴の除去です。
そのためには、先ほどインポートした VarianceThreshold
関数を使用します。
この関数では、パラメータ threshold
に値を指定する必要があります。
このパラメータに 0 を渡すと、分散が 0 のフィーチャーがすべてフィルタリングされます。
以下のスクリプトを実行して、定数フィーチャーに対するフィルタを作成します。
constant_filter = VarianceThreshold(threshold=0)
次に、このフィルタを学習セットに適用する。
constant_filter.fit(train_features)
次に、作成したフィルタの get_support()
メソッドを用いて、一定でない特徴をすべて取得します。
以下のスクリプトを実行すると、非一定特徴量の数が表示されます。
len(train_features.columns[constant_filter.get_support()])
出力には320と表示されていますが、これはトレーニングセットの370個の素性のうち、320個の素性が一定でないことを意味します。
同様に、以下のスクリプトを実行することで、定数特徴量の数を知ることができます。
constant_columns = [column for column in train_features.columns
if column not in train_features.columns[constant_filter.get_support()]]
print(len(constant_columns))
すべての定数カラムを見るには、以下のスクリプトを実行します。
for column in constant_columns:
print(column)
出力はこのようになる。
ind_var2_0
ind_var2
ind_var18_0
ind_var18
ind_var27_0
ind_var28_0
ind_var28
ind_var27
ind_var34_0
ind_var34
ind_var41
ind_var46_0
ind_var46
num_var18_0
num_var18
num_var27_0
num_var28_0
num_var28
num_var27
num_var34_0
num_var34
num_var41
num_var46_0
num_var46
saldo_var18
saldo_var28
saldo_var27
saldo_var34
saldo_var41
saldo_var46
delta_imp_amort_var18_1y3
delta_imp_amort_var34_1y3
imp_amort_var18_hace3
imp_amort_var18_ult1
imp_amort_var34_hace3
imp_amort_var34_ult1
imp_reemb_var13_hace3
imp_reemb_var17_hace3
imp_reemb_var33_hace3
imp_trasp_var17_out_hace3
imp_trasp_var33_out_hace3
num_var2_0_ult1
num_var2_ult1
num_reemb_var13_hace3
num_reemb_var17_hace3
num_reemb_var33_hace3
num_trasp_var17_out_hace3
num_trasp_var33_out_hace3
saldo_var2_ult1
saldo_medio_var13_medio_hace3
最後に、トレーニングセットとテストセットから定数特徴を削除するには、constant_filter
の transform()
メソッドを使います。
以下のスクリプトを実行してください。
train_features = constant_filter.transform(train_features)
test_features = constant_filter.transform(test_features)
train_features.shape, test_features.shape
上記のスクリプトを実行すると、50個の定数カラムが削除されたため、トレーニングセットとテストセットの両方が320カラムになることがわかります。
準定数機能の削除
準定常特徴とは、その名が示すように、ほぼ一定の特徴である。
言い換えれば、これらの特徴は出力の非常に大きなサブセットに対して同じ値を持っています。
このような素性は、予測にはあまり役に立ちません。
準一定特徴量の分散の閾値をどうするかというルールはない。
しかし、経験則として、出力観測値に対して99%以上の類似値を持つ準定常特徴を削除する。
ここでは、VarianceThreshold
関数を用いて準定常フィルタを作成します。
ただし、threshold
パラメータに 0 を渡す代わりに、0.01 を渡します。
これは、ある列の値の分散が 0.01 より小さい場合、その列を削除することを意味します。
言い換えれば、約99%の値が類似している特徴列を削除します。
手順は前節とよく似ています。
データセットとライブラリをインポートし、訓練とテストの分割を行い、最初に一定の特徴を削除します。
必要なライブラリとデータセットのインポート
以下のスクリプトを実行し、データセットと必要なライブラリをインポートしてください。
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import VarianceThreshold
santandar_data = pd.read_csv(r"E:Datasetssantandar_data.csv", nrows=40000)
santandar_data.shape
データをトレーニングセットとテストセットに分ける
train_features, test_features, train_labels, test_labels = train_test_split(
santandar_data.drop(labels=['TARGET'], axis=1),
santandar_data['TARGET'],
test_size=0.2,
random_state=41)
分散閾値を用いた定数特徴量の除去
準一定特徴量を削除する前に、まず定数特徴量を削除する必要があります。
そのために以下のスクリプトを実行する。
constant_filter = VarianceThreshold(threshold=0)
constant_filter.fit(train_features)
len(train_features.columns[constant_filter.get_support()])
constant_columns = [column for column in train_features.columns
if column not in train_features.columns[constant_filter.get_support()]]
train_features.drop(labels=constant_columns, axis=1, inplace=True)
test_features.drop(labels=constant_columns, axis=1, inplace=True)
分散しきい値を用いた擬似一定特徴量の除去
準定数フィルタを作成しましょう。
以下のスクリプトを実行してください。
qconstant_filter = VarianceThreshold(threshold=0.01)
残りのステップは同じです。
あとは、以下のように fit()
メソッドを用いて、学習セットにフィルタを適用します。
qconstant_filter.fit(train_features)
準定数でないカラムの数を調べてみましょう。
以下のスクリプトを実行します。
len(train_features.columns[qconstant_filter.get_support()])
出力には265と表示されています。
これは、定数特徴量を取り除いた320列のうち、55列が準定数であることを意味します。
準定数の列の数を確認するには、以下のスクリプトを実行する。
qconstant_columns = [column for column in train_features.columns
if column not in train_features.columns[qconstant_filter.get_support()]]
print(len(qconstant_columns))
出力に55と表示されるはずである。
それでは、すべての準定数の列の名前を出力してみよう。
次のスクリプトを実行する。
for column in qconstant_columns:
print(column)
出力には、以下の列名が表示されるはずである。
ind_var1
ind_var6_0
ind_var6
ind_var13_largo
ind_var13_medio_0
ind_var13_medio
ind_var14
ind_var17_0
ind_var17
ind_var19
ind_var20_0
ind_var20
ind_var29_0
ind_var29
ind_var30_0
ind_var31_0
ind_var31
ind_var32_cte
ind_var32_0
ind_var32
ind_var33_0
ind_var33
ind_var40
ind_var39
ind_var44_0
ind_var44
num_var6_0
num_var6
num_var13_medio_0
num_var13_medio
num_op_var40_hace3
num_var29_0
num_var29
delta_imp_aport_var33_1y3
delta_num_aport_var33_1y3
ind_var7_emit_ult1
ind_var7_recib_ult1
num_aport_var33_hace3
num_aport_var33_ult1
num_var7_emit_ult1
num_meses_var13_medio_ult3
num_meses_var17_ult3
num_meses_var29_ult3
num_meses_var33_ult3
num_meses_var44_ult3
num_reemb_var13_ult1
num_reemb_var17_ult1
num_reemb_var33_ult1
num_trasp_var17_in_hace3
num_trasp_var17_in_ult1
num_trasp_var17_out_ult1
num_trasp_var33_in_hace3
num_trasp_var33_in_ult1
num_trasp_var33_out_ult1
num_venta_var44_hace3
最後に、トレーニングセットとテストセットが非定数と非準定数の列のみを含むかどうかを確認するために、qconstant_filter
の transform()
メソッドを使用することができます。
そのためには、以下のスクリプトを実行する。
train_features = qconstant_filter.transform(train_features)
test_features = qconstant_filter.transform(test_features)
train_features.shape, test_features.shape
上記のスクリプトを実行すると、デフォルトの370列から50列の定数と55列の準定数が削除され、トレーニングセットとテストセットの両方が265列になったことがわかります。
重複する機能の削除
重複Featureとは、類似した値を持つFeatureのことである。
重複素性はアルゴリズムの学習に何の価値も与えず、むしろ学習時間にオーバーヘッドと不必要な遅延を与える。
したがって、学習前にデータセットから重複する素性を削除することが推奨される。
必要なライブラリとデータセットのインポート
以下のスクリプトを実行し、データセットと必要なライブラリをインポートしてください。
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import VarianceThreshold
santandar_data = pd.read_csv(r"E:Datasetssantandar_data.csv", nrows=20000)
santandar_data.shape
重複する列の削除は、重複する特徴を削除する前にデータ行列の転置を取る必要があるため、計算コストがかかる場合があります。
したがって、上記のスクリプトでは、この記事で使用したSantandarの顧客満足度データから最初の2万レコードのみをインポートしています。
データをトレーニングセットとテストセットに分ける
train_features, test_features, train_labels, test_labels = train_test_split(
santandar_data.drop(labels=['TARGET'], axis=1),
santandar_data['TARGET'],
test_size=0.2,
random_state=41)
トランスポーズによる重複フィーチャーの除去
定数や準定数の特徴量とは異なり、重複する特徴量を除去するPythonのビルトインメソッドはありません。
しかし、pandasのdataframeの中で重複する行を特定するのに役立つメソッドがあります。
このメソッドを使って、まず以下のようにデータセットの転置を行います。
train_features_T = train_features.T
train_features_T.shape
上記のスクリプトでは、学習データの転置を取り、 train_features_T
データフレームに格納します。
最初のトレーニングセットには16000行と370列が含まれていますが、転置されたトレーニングセットの形状を見てみると、370行と16000列が含まれていることがわかります。
幸運なことに、pandasには duplicated()
メソッドがあり、データフレームから重複した行を見つけることができます。
転置されたデータフレームの行は、実際には実際のデータフレームの列や特徴であることに注意してください。
以下のように sum()
メソッドと duplicated()
メソッドを連結して、データセット内の重複する特徴の総数を求めてみましょう。
print(train_features_T.duplicated().sum())
出力では、94と表示されます。
最後に、drop_duplicates()
メソッドで重複した行を削除します。
drop_duplicates()メソッドの
keepパラメータに文字列値
first` を渡すと、最初のコピー以外の重複した行をすべて削除することができます。
次のステップでは、重複した行をすべて削除し、その結果を転置して、重複した列を含まないオリジナルのトレーニングセットを取得します。
以下のスクリプトを実行してください。
unique_features = train_features_T.drop_duplicates(keep='first').T
それでは、重複するフィーチャーのない新しいトレーニングセットの形状を表示してみましょう。
unique_features.shape
出力には、(16000,276)と表示され、94個の重複列を削除した後、特徴セットのサイズが大幅に小さくなったことがわかります。
重複した列の名前を見るには、このスクリプトを実行します。
duplicated_features = [dup_col for dup_col in train_features.columns if dup_col not in unique_features.columns]
duplicated_features
出力には、以下のカラムが表示される。
['ind_var2',
'ind_var13_medio',
'ind_var18_0',
'ind_var18',
'ind_var26',
'ind_var25',
'ind_var27_0',
'ind_var28_0',
'ind_var28',
'ind_var27',
'ind_var29_0',
'ind_var29',
'ind_var32',
'ind_var34_0',
'ind_var34',
'ind_var37',
'ind_var40_0',
'ind_var40',
'ind_var41',
'ind_var39',
'ind_var46_0',
'ind_var46',
'num_var13_medio',
'num_var18_0',
'num_var18',
'num_var26',
'num_var25',
'num_op_var40_hace3',
'num_op_var39_hace3',
'num_var27_0',
'num_var28_0',
'num_var28',
'num_var27',
'num_var29_0',
'num_var29',
'num_var32',
'num_var34_0',
'num_var34',
'num_var37',
'num_var40_0',
'num_var40',
'num_var41',
'num_var39',
'num_var46_0',
'num_var46',
'saldo_var18',
'saldo_var28',
'saldo_var27',
'saldo_var29',
'saldo_var34',
'saldo_var40',
'saldo_var41',
'saldo_var46',
'delta_imp_amort_var18_1y3',
'delta_imp_amort_var34_1y3',
'delta_imp_reemb_var33_1y3',
'delta_imp_trasp_var17_out_1y3',
'delta_imp_trasp_var33_out_1y3',
'delta_num_reemb_var13_1y3',
'delta_num_reemb_var17_1y3',
'delta_num_reemb_var33_1y3',
'delta_num_trasp_var17_in_1y3',
'delta_num_trasp_var17_out_1y3',
'delta_num_trasp_var33_in_1y3',
'delta_num_trasp_var33_out_1y3',
'imp_amort_var18_hace3',
'imp_amort_var18_ult1',
'imp_amort_var34_hace3',
'imp_amort_var34_ult1',
'imp_var7_emit_ult1',
'imp_reemb_var13_hace3',
'imp_reemb_var17_hace3',
'imp_reemb_var33_hace3',
'imp_reemb_var33_ult1',
'imp_trasp_var17_out_hace3',
'imp_trasp_var17_out_ult1',
'imp_trasp_var33_in_hace3',
'imp_trasp_var33_out_hace3',
'ind_var7_emit_ult1',
'num_var2_0_ult1',
'num_var2_ult1',
'num_var7_emit_ult1',
'num_reemb_var13_hace3',
'num_reemb_var17_hace3',
'num_reemb_var33_hace3',
'num_reemb_var33_ult1',
'num_trasp_var17_out_hace3',
'num_trasp_var17_out_ult1',
'num_trasp_var33_in_hace3',
'num_trasp_var33_out_hace3',
'saldo_var2_ult1',
'saldo_medio_var13_medio_hace3',
'saldo_medio_var13_medio_ult1',
'saldo_medio_var29_hace3']
関連する機能の削除
データセットには、重複する特徴に加えて、相関のある特徴も含まれることがあります。
2つ以上の特徴が線形空間上で近接している場合、相関があることになる。
フルーツバスケットの特徴セットを例にとると、フルーツバスケットの重さは通常、価格と相関がある。
重量が多いほど、価格は高くなる。
出力観測値と入力特徴量の間の相関は非常に重要であり、そのような特徴量は保持されるべきです。
しかし、2つ以上の特徴が相互に相関している場合、それらはモデルに冗長な情報を伝えるので、特徴の数を減らすために、相関する特徴のうちの1つだけを保持する必要があります。
このセクションで使用するデータセットは、BNP Paribas Cardif Claims Management datasetで、Kaggleからダウンロードできます。
以下の手順で、データセットから相関のある特徴を探し、削除します。
必要なライブラリとデータセットのインポート
以下のスクリプトを実行し、データセットと必要なライブラリをインポートしてください。
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import VarianceThreshold
paribas_data = pd.read_csv(r"E:Datasetsparibas_data.csv", nrows=20000)
paribas_data.shape
上のスクリプトでは、データセットと必要なライブラリをインポートしています。
次に、データフレームの形状を出力しました。
出力には、(20000, 133)と表示されていますが、これはデータセットに2万行と133個の素性が含まれていることを意味します。
相関を求めるには、データセットに含まれる数値素性だけが必要です。
数値以外の特徴量を除外するために、データの前処理が必要です。
データ前処理
以下のスクリプトを実行し、データセットから非数値の特徴を削除する。
num_colums = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
numerical_columns = list(paribas_data.select_dtypes(include=num_colums).columns)
paribas_data = paribas_data[numerical_columns]
上のスクリプトの最初の行では、データセットに残したいカラムのデータ型を含むリストを定義している。
次に、データセットに対して select_dtypes()
メソッドを呼び出し、保持したいカラムのデータ型を含む num_colums
リストを渡します。
select_dtypes()メソッドは、指定した数値カラムの名前を返し、それを
numeric_columnsリストに格納します。
次に、paribas_dataデータフレームから、
numerical_columsリストを使用してカラムをフィルタリングします。
次のスクリプトを実行して、paribas_data` データフレームの形状を表示して、数値カラムがいくつあるかを確認してみましょう。
paribas_data.shape
出力には(20000, 114)と表示されます。
これは、このデータセットには2万件のレコードと114の素性があることを意味します。
以前は 133 個の素性があったことを思い出してください。
データをトレーニングセットとテストセットに分ける
例によって、相関のある特徴を除去する前に、データをトレーニングセットとテストセットに分割する必要があるので、以下のスクリプトを実行して、データをトレーニングセットとテストセットに分割します。
train_features, test_features, train_labels, test_labels = train_test_split(
paribas_data.drop(labels=['target', 'ID'], axis=1),
paribas_data['target'],
test_size=0.2,
random_state=41)
上記のスクリプトでは、データを80%のトレーニングセットと20%のテストセットに分割しています。
Corr()メソッドによる相関特徴量の除去
相関のある特徴を除去するために、pandas dataframe の corr()
メソッドを利用することができます。
corr()` メソッドは、dataframe のすべての列の相関を含む相関行列を返します。
そして、相関行列をループして、2つの列の間の相関が閾値相関よりも大きい場合、その列を相関のある列のセットに追加することができます。
その列の集合を実際のデータセットから削除することができます。
まず、データセット内の列の相関行列と、相関のある特徴をすべて含む空集合を作成しよう。
以下のスクリプトを実行する。
correlated_features = set()
correlation_matrix = paribas_data.corr()
上のスクリプトでは、データセットの全カラムに対して相関行列 correlation_matrix
を作成する。
また、相関のある特徴量の名前を格納したセット correlated_features
を作成する。
次に、下図のように correlation_matrix
の全ての列をループし、相関値が 0.8 の列を correlated_features
セットに追加します。
相関の閾値は自由に設定することができる。
for i in range(len(correlation_matrix .columns)):
for j in range(i):
if abs(correlation_matrix.iloc[i, j]) > 0.8:
colname = correlation_matrix.columns[i]
correlated_features.add(colname)
データセットに含まれるカラムのうち、少なくとも1つのカラムと0.8以上の相関を持つカラムの総数を見てみましょう。
以下のスクリプトを実行します。
len(correlated_features)
出力には55個が表示され、これはデータセットに含まれるオリジナルの特徴量のほぼ40%にあたる。
このように、データセットにどれだけ冗長な情報が含まれているかが分かる。
これらの特徴量の名前を見るには、以下のスクリプトを実行する。
print(correlated_features)
出力は以下のようになる。
{'v55', 'v105', 'v130', 'v12', 'v60', 'v67', 'v63', 'v46', 'v53', 'v43', 'v68', 'v123', 'v95', 'v103', 'v44', 'v108', 'v89', 'v104', 'v109', 'v83', 'v115', 'v21', 'v101', 'v93', 'v40', 'v78', 'v54', 'v118', 'v124', 'v73', 'v96', 'v121', 'v77', 'v114', 'v48', 'v116', 'v87', 'v86', 'v65', 'v122', 'v64', 'v81', 'v128', 'v49', 'v37', 'v84', 'v98', 'v111', 'v41', 'v25', 'v106', 'v32', 'v126', 'v76', 'v100'}
特徴量の名前は機密情報を含むため、銀行によって隠されていますが、特徴量のコードネームを見ることができます。
これらの相関列は学習アルゴリズムに類似の情報を伝えるので、削除する必要がある。
以下のスクリプトはデータセットからこれらのカラムを削除する。
train_features.drop(labels=correlated_features, axis=1, inplace=True)
test_features.drop(labels=correlated_features, axis=1, inplace=True)
結論
特徴量の選択は,あらゆる機械学習モデルの性能と学習において重要な役割を果たす.機械学習アルゴリズムの特徴選択には様々な種類の手法が提案されている。
本稿では、Pythonを用いた特徴抽出のための様々な種類のフィルタ手法を研究した。
まず、定数と準定数の特徴を除去することから始め、次に重複する特徴を除去することを検討した。
最後に、データセットから相関のある特徴を除去する方法について検討しました。
次回は、他の特徴選択方法について見ていきます。