使い方
機械学習のうち、ニューラルネットワークやSVMなどのモデルは、データの値の大きさやレンジが異なる場合、過学習になったり精度が悪くなることがあり、データを揃えるための前処理が必要になる(SVMの例、ニューラルネットワークの例)。
scikit-learnのpreprocessing
モジュールには、データの前処理を行う各種のクラスが準備されている。一般的な使い方は以下の通り。
- データを訓練データとテストデータに分ける
- 各preprocessorの
fit()
メソッドに訓練データを与えて変換用のパラメータを準備する(変換モデルを構築する)fit()
メソッドは、各列が特徴量、各行がデータレコードである2次元配列を想定している
- 変換器の
transform()
メソッドに訓練データを与えて前処理を施す - 同じ変換器の
transform()
メソッドにテストデータを与えて前処理をほどこす
なお、fit()
メソッドとtransform()
メソッドをそれぞれ分けて行うほか、fit().transform()
とメソッドチェーンで実行してもよい。またpreprocessorにはこれらを一体化したfit_transform()
というメソッドも準備されている。
実行例
preprocessing
のscaler系のクラスの1つ、MinMaxScaler
を例にして、その挙動を追ってみる。
まず必要なライブラリーやクラスをインポートし、Breast cancerデータを読み込み、データを訓練データとテストデータに分ける。cancerデータは30の特徴量を列とし、569のレコードを持つが、それを3:1に分け、426セットの訓練データと143セットのテストデータとしている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import numpy as np from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split np.set_printoptions(suppress=True, precision=2, floatmode='fixed') cancer = load_breast_cancer() X_train, X_test, y_train, y_test =\ train_test_split(cancer.data, cancer.target, random_state=1) print("shepe of trainning data:{}".format(X_train.shape)) print("shepe of test data :{}".format(X_test.shape)) # shepe of trainning data:(426, 30) # shepe of test data :(143, 30) |
次にMinMaxScaler
のインスタンスを生成し、fit()
メソッドに訓練データX_train
を与えて、変換用のモデルを構築する。
1 2 3 4 |
from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler() scaler.fit(X_train) |
preprocessing
でいうモデルの構築とは、基準となるデータを与えて、変換用のパラメータを算出・保持するのに相当する。
今回の例のMinMaxScaler
オブジェクトでは、特徴量数を要素数とする1次元配列で、データセット中の各特徴量の最小値(data_min_
)、最大値(data_max_
)、最大値-最小値のレンジ(data_range_
)、レンジの逆数であるscales_
がインスタンス内に保持されている。
これらのパラメーターは、30の特徴量について、426個のデータの最小値、最大値・・・などとなっている。たとえば1つ目の特徴量については、最大値-最小値は28.11−6.98=21.13となり、data_range_
の1つ目の値と符合している。またscales_
の各要素は、data_range_
の各要素の逆数となっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
print("-----traing data characteristics and parameters") print("mins :\n{}".format(scaler.data_min_)) print("maxs :\n{}".format(scaler.data_max_)) print("ranges:\n{}".format(scaler.data_range_)) print("scales:\n{}".format(scaler.scale_)) # -----traing data characteristics and parameters # mins : # [ 6.98 9.71 43.79 143.50 0.05 0.02 0.00 0.00 0.11 0.05 # 0.12 0.36 0.76 6.80 0.00 0.00 0.00 0.00 0.01 0.00 # 7.93 12.02 50.41 185.20 0.07 0.03 0.00 0.00 0.16 0.06] # maxs : # [ 28.11 39.28 188.50 2501.00 0.16 0.29 0.43 0.20 0.30 # 0.10 2.87 4.88 21.98 542.20 0.03 0.14 0.40 0.05 # 0.06 0.03 36.04 49.54 251.20 4254.00 0.22 0.94 1.17 # 0.29 0.58 0.15] # ranges: # [ 21.13 29.57 144.71 2357.50 0.11 0.27 0.43 0.20 0.20 # 0.05 2.76 4.52 21.22 535.40 0.03 0.13 0.40 0.05 # 0.05 0.03 28.11 37.52 200.79 4068.80 0.15 0.91 1.17 # 0.29 0.42 0.09] # scales: # [ 0.05 0.03 0.01 0.00 9.03 3.74 2.34 4.97 5.05 21.97 0.36 0.22 # 0.05 0.00 33.99 7.51 2.53 18.94 19.26 34.55 0.04 0.03 0.00 0.00 # 6.60 1.10 0.85 3.44 2.38 10.71] |
構築された変換器によりX_train
を変換すると、すべての特徴量について最小値が0、最大値が1となる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
X_train_scaled = scaler.transform(X_train) print("-----scaled training data characteristics") print("mins :\n{}".format(X_train_scaled.min(axis=0))) print("maxs :\n{}".format(X_train_scaled.max(axis=0))) print("ranges:\n{}".format(X_train_scaled.max(axis=0) - X_train_scaled.min(axis=0))) # mins : # [0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 # 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 # 0.00 0.00] # maxs : # [1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 # 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 # 1.00 1.00] # ranges: # [1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 # 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 # 1.00 1.00] |
同じ変換器でテストデータも変換すると、変換後の特徴量の最小値・最大値は0、1になっていない。これはテストデータの最大値・最小値が必ずしも訓練データのそれらと一致しないので当然である。また、テストデータの最大値が訓練データの最大値よりも大きい場合は、テストデータの最大値が1を超えることになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
X_test_scaled = scaler.transform(X_test) print("-----scaled test data characteristics") print("mins :\n{}".format(X_test_scaled.min(axis=0))) print("maxs :\n{}".format(X_test_scaled.max(axis=0))) print("ranges:\n{}".format(X_test_scaled.max(axis=0) - X_test_scaled.min(axis=0))) # mins : # [ 0.03 0.02 0.03 0.01 0.14 0.04 0.00 0.00 0.15 -0.01 -0.00 0.01 # 0.00 0.00 0.04 0.01 0.00 0.00 -0.03 0.01 0.03 0.06 0.02 0.01 # 0.11 0.03 0.00 0.00 -0.00 -0.00] # maxs : # [0.96 0.82 0.96 0.89 0.81 1.22 0.88 0.93 0.93 1.04 0.43 0.50 0.44 0.28 # 0.49 0.74 0.77 0.63 1.34 0.39 0.90 0.79 0.85 0.74 0.92 1.13 1.07 0.92 # 1.21 1.63] # ranges: # [0.92 0.79 0.92 0.88 0.67 1.18 0.88 0.93 0.78 1.04 0.43 0.49 0.44 0.28 # 0.45 0.73 0.77 0.63 1.37 0.38 0.87 0.74 0.83 0.74 0.81 1.11 1.07 0.92 # 1.21 1.63] |
テストデータで改めてfit()
メソッドを実行してテストデータに適用するとレンジが0~1になるが、そうすると訓練データとテストデータで異なる変換を行うことになり、結果が歪んでしまう。
preprocessingの各種モデル
sklearn.preprocessing
には多様な変換器が準備されているが、それらを目的ごとのカテゴリーに分けて整理する。
scaler~スケール変換
データの大きさやレンジを変換してそろえる。
MinMaxScaler
- 各特徴量が0~1の範囲になるよう正規化する(線形変換)。
StandardScaler
- 各特徴量の標本平均と標本分散を使って標準化する(線形変換)。
RobustScaler
- 各特徴量の中央値と4分位数を使って標準化する(線形変換)。
normalization~正則化
特徴量ベクトルのノルムをそろえる。レンジをそろえる目的のscalerに比べて、元のデータ分布の相似性はなくなる。
Normalizer
- 特徴量ベクトルのノルムを1にそろえる。
binalize~2値化
特徴量データを0/1の2値に分ける。
encoder~カテゴリーデータのエンコード
カテゴリーで与えられたデータ(性別、曜日など)をモデルで扱うために数値化する。
LabelEncoder
- 1次元配列で与えられた特徴量クラスデータを、数値ラベルに変換する。
OrdinalEncoder
- 2次元配列で与えられた特徴量クラスデータを、数値ラベルに変換する。
OneHotEncoder
- 2次元配列で与えられた特徴量クラスデータを、特徴量ごとのインジケーター列に変換する。
スケール変換の頑健性
MinMaxScalerは計算過程が簡明だが、飛び離れた異常値がわずかでもあるとそれが全体のレンジを規定し、本来適用したいデータの値が歪んでしまう。StandardScalerやRobustScalerはこのような異常値に対して頑健な変換を行う。これら3つの頑健性についてはこちらで確認している。