sklearn.preprocessing

使い方

機械学習のうち、ニューラルネットワークやSVMなどのモデルは、データの値の大きさやレンジが異なる場合、過学習になったり精度が悪くなることがあり、データを揃えるための前処理が必要になる(SVMの例ニューラルネットワークの例)。

scikit-learnのpreprocessingモジュールには、データの前処理を行う各種のクラスが準備されている。一般的な使い方は以下の通り。

  1. データを訓練データとテストデータに分ける
  2. 各preprocessorのfit()メソッドに訓練データを与えて変換用のパラメータを準備する(変換モデルを構築する)
    • fit()メソッドは、各列が特徴量、各行がデータレコードである2次元配列を想定している
  3. 変換器のtransform()メソッドに訓練データを与えて前処理を施す
  4. 同じ変換器のtransform()メソッドにテストデータを与えて前処理をほどこす

なお、fit()メソッドとtransform()メソッドをそれぞれ分けて行うほか、fit().transform()とメソッドチェーンで実行してもよい。またpreprocessorにはこれらを一体化したfit_transform()というメソッドも準備されている。

実行例

preprocessingのscaler系のクラスの1つ、MinMaxScalerを例にして、その挙動を追ってみる。

まず必要なライブラリーやクラスをインポートし、Breast cancerデータを読み込み、データを訓練データとテストデータに分ける。cancerデータは30の特徴量を列とし、569のレコードを持つが、それを3:1に分け、426セットの訓練データと143セットのテストデータとしている。

次にMinMaxScalerのインスタンスを生成し、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_の各要素の逆数となっている。

構築された変換器によりX_trainを変換すると、すべての特徴量について最小値が0、最大値が1となる。

同じ変換器でテストデータも変換すると、変換後の特徴量の最小値・最大値は0、1になっていない。これはテストデータの最大値・最小値が必ずしも訓練データのそれらと一致しないので当然である。また、テストデータの最大値が訓練データの最大値よりも大きい場合は、テストデータの最大値が1を超えることになる。

テストデータで改めて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つの頑健性についてはこちらで確認している。

 

OneHotEncoder

概要

OneHotEncoderは、あるクラスデータの特徴量をエンコードする。LabelEncoderOrdinalEncoderが特徴量内のクラスに一連の数値を振るのに対して、OneHotEncoderはクラスの数だけ列を確保し、データごとに該当するクラスのみに1を立てる。エンコードされたデータは、該当するクラスのみに反応するインデックス引数となる。

使い方

fit()~インデックス列の生成

以下の例は、2つのクラス特徴量を持つ6個のデータセットをOneHotEncoderで変換。

  • sklearn.prreprocessingからOneHotEncoderをインポート
  • エンコーダーのインスタンスを生成
    • デフォルトではスパース行列になるので、オプションでsparse=Falseを指定
  • fit()メソッドでデータをフィッティングし、変換器を準備
  • この段階でcategories_プロパティーには各特徴量ごとのインデックス構成がセットされる

以下の例では、1つ目の特徴量は3つのクラス、2つ目の特徴量は2つのクラスを持つので、3要素、2要素の配列を要素に持つリストがcategories_にセットされる。

transform()~インデックスデータへの変換

fit()メソッドで準備された変換器によってデータを変換する。変換後のデータは特徴量のクラス数分の列を持つ2次元のndarrayで返される。なおfittransformを一度に行うfit_transform()メソッドも準備されている。

出力の右3列は3つの都市、それに続く2列は性別に対応していて、たとえば1行目のデータの都市はcategories_[0]の3番目'Tokyo'、性別はcategories_[1]の2番目の'Male'であることがあらわされている。

DataFrameによる操作

OneHotEncoderpandas.DataFrameも扱える。ただしtransfrom()fit_transform()メソッドの戻り値はndarrayなので、以下の例ではこれをDataFrameの形にしている。このときcolumns引数にエンコーダーのインスタンスのcategories_プロパティーを使うと個別のクラス名まで打ち込まずに済んで便利。

数値データとクラスデータが混在する場合

DataFrameの準備

以下の例では、2つのクラス特徴量と2つの数値特徴量を持つデータセットをDataFrameとして扱う。

クラスデータのヘッダーの準備

クラスデータを複数のインデックスデータの列にするための準備。

  • 特徴量のうち、クラスデータのものと数値データのもののヘッダーを分けておく
  • クラスデータ用のDataFrameを準備して、元データからクラスデータの列だけを切り出し
  • エンコーダーを生成してfit_trans()を実行
  • 実行後にエンコーダーのcategories_に保持されているクラスリストを取得

このクラスリストが変換後のデータのヘッダーになる。

クラスデータと数値データの合体

以下の処理では、変換されたクラスデータ列と元の数値データ列を合わせて最終的なデータセットとしている

  • クラスリストをヘッダーとして、変換後のクラスデータ(ndarray)をDataFrameとして読み込み
  • 上記DataFrameに元データの数値データを追加

この処理によって元データセットから特徴量の順番が変わるが、学習過程で特徴量の順番は影響しない。

inverse_transform()

上でdf_X_trans = df_X_class_trans.copy()としたので、df_X_class_transは保存されている。このデータをエンコーダーのinverse_transform()に与えると、複数列で表現されていたクラスが元の表現で得られる。

新しいデータの変換

訓練済みモデルにデータを与えて予測する場合、前処理のエンコーディングでは、フィッティング済みのエンコーダーに新しいデータを与えて変換する。

未知のクラスへの対処

フィッティング時になかったクラスに遭遇した場合の動作は、エンコーダーのインスタンス生成時に指定する。

OneHotEncoder(handle_unknown='error'/'ignore')

デフォルトは'error'で、未知のクラスに遭遇するとエラーを投げる。'ignore'を指定すると未知のクラスの場合はその特徴量のすべてのクラスラベルが0になる。

以下の例では、2行目のデータにフィッティングでは含まれていなかった”Nagoya”があるため、変換後のデータの2行目の1~3列が0となっている。

この変換データをinverse_transform()で逆変換すると、未知のクラスであったところは'None'に変換される。

 

 

OrdinalEncoder

概要

sklearn.preprocessingOrdinalEncoderは、2次元のデータ(行数×列数=データ数×特徴量数)を須知ラベルデータに変換する。

  • コンストラクターでencoderのインスタンスを生成
  • fit()メソッドに2次元の元データを与える(元データは2次元のリスト、ndarray、DataFrameは可)
  • 元データの特徴量ごと(列ごと)にデータが数値ラベル化される
  • 特徴量のカテゴリー数がn_classのとき、特徴量データが0~n_class−1の整数ラベルに変換される
  • 1次元のデータを変換する場合も2次元に変形する必要がある
  • 変換は全ての列が対象となり、定量的な数値データが含まれていてもそれらが数値ラベルに変換される

使い方

fit~ラベルの設定

以下の例では、3つの特徴量を持つ6つのデータを例題としている。特徴量は3つともクラスデータで、fit()メソッドで変換器の準備をする。

  • エンコーダーにおけるfit()は、特徴量ごとにクラスデータのラベルを設定し、変換器を準備する
  • フィッティングの後、categories_プロパティーにリストがセットされる
  • categories_ndarrayを要素とするリストで、各配列には特徴量ごとの重複を除いたクラス名が格納される
  • 各特徴量のクラスはcategories_各要素の配列の先頭から数値ラベル0, 1, 2, …に対応している。

transform~ラベルへの変換

この変換器のtransform()メソッドで元データを変換すると、元データと同じ次元・次数の2次元配列が得られ、各クラスデータが数値データに変換された結果が格納されている。

なお、OrdinalEncoderにもfit_transform()メソッドが準備されている。

1次元のデータを変換する場合でも、1×1の2次元とする必要があり、結果も2次元の配列で返される。

inverse_transform()で数値ラベルをクラスデータに逆変換可能。

categories_パラメーターについて

なおコンストラクターのcategories_パラメーターを指定できるが、これはあらかじめ特徴量のクラスデータがわかっている場合に、これらを全特徴量について指定する。この際、元データに含まれないクラスを含めてもよい。

数値データとクラスデータが混在する場合

クラスデータと数値データが混在する場合にOrdinalEncoderで変換すると、すべてのデータがクラスデータとみなされ、数値データもラベルに変換されてしまう。

以下の例では、最後の列の実数データも、1, 1.5, …, 5に対して0, 1, …, 5のラベルに変換されている。

このような場合は、クラスデータのみ取り出して変換させる。OrdinalEncoderpandas.DataFrameを扱うことができるので、列操作のために元データをDataFrameとする。

今回の例では、最初の3列がクラスデータなので、一時的なDataFrameにそれらを切出してOrdinalEncoderを適用する。transform()の結果はndarrayで戻るので、それを元のDataFrameの列に入れ替えている。

最後の列はそのままで、その前の3列がラベルデータに変換されている。

 

LabelEncoder

概要

sklearn.preprocessingLabelEncoderは、クラスデータ(カテゴリーデータ)を数値ラベルに変換する。

  • コンストラクターは引数をとらない
  • fit()メソッドに特徴量を要素とする1次元配列(特徴量数)の元データを与える
  • 特徴量のクラス数がn_classのとき、特徴量データが0~n_class−1の整数ラベルに変換される
  • 特徴量が定量的な数値データであっても整数ラベルに変換される

使い方

LabelEncoderを使うには、まずそのインスタンスを生成し、fit()メソッドで数値ラベルを生成する。fit()メソッドを実行すると、元データのクラスの重複を除いたクラスリストがclasses_プロパティーに保存され、transform()メソッドで任意のデータを変換する変換器が準備される。

準備された変換器で、変換したいデータにtransform()メソッドを適用して、変換された数値ラベルを得る。

このラベルデータにinverse_transform()を適用すると、数値ラベルが元のクラスデータに逆変換される。

transform()の引数に元データに存在しないクラスデータが含まれていた場合、エラーとなる。

注意

LabelEncoderは、元データに定量的な数値データを与えた場合でもこれらを数値ラベルに変換する。

transform()の引数に元データに存在しない数値が含まれている場合はエラーとなる。

 

Normalizer

概要

sklearn.preprocessorsモジュールのNormalizerは、特徴量ベクトルのノルムが1になるようにする。具体的には、データごとに特徴量Fiを以下の式によってFi*に変換する。

(1)    \begin{equation*} {F_i}^* = \frac{\sum F_i}{\left( \sum {|F_i|}^p \right) ^\frac{1}{p}} \end{equation*}

ノルムのタイプはコンストラクターの引数で指定する。デフォルトは'l2'で、その他に'l1''max'を指定可能。

Normalizer(norm='l2')

挙動

それぞれ異なる正規分布に従う2つの特徴量について、Normalizerを適用したときの挙動を以下に示す。

scalerのような相似性の変換ではないので左下の変換後のヒストグラムは変換前の形状と異なっている。

データの空間的な分布は、デフォルトのL2ノルムの指定によって全データが半径1の円周上に位置するよう変換される。

変換後のデータを拡大してみると以下の通りで、原点を中心とした半径1の円周上に各点が並んでいる。

他の2つ、L1ノルムと最大値ノルムを指定して実行した結果が下記の通りで、それぞれのノルムに応じた線上に各点が並んでいる。

コードは以下の通りで、データに対してfit()メソッドでスケールパラメーターを決定し、transform()メソッドで変換を行うところを、これらを連続して実行するfit_transform()メソッドを使っている。

特徴

Normalizerは特徴量ベクトルの方向だけが重要な場合に用いる。たとえば空間内の特定の方向範囲にあるクラスターの分離などかと思うが、抽象的なものになると想像がつかない。実際、サイト上で見ても、Normalizerの意義とデータの性質に基づいて適用しているケースは、検索上位には出てこない。

なおNormalizerによる変換は不可逆であり、scalerのようなinverse_transform()を持たない。

 

イテレーターは再利用不可

イテレーターで生成されたオブジェクトを変数にセットして実行できるが、これをそのまま再度利用することはできない。

イテレーターはインスタンス生成時に__init__()メソッドにより初期化され、その後イテレーターとして使用が終わった直後の値を保持している。正確には、再利用が禁止されているのではなく初期状態から再度実行することができない、ということになる。

たとえば次の例を見ると、最初の実行が終わったのちに再度利用することは不可能ではない。ただし結果を見るとわかるように、2度目の実行の初期値が1度目の終了判定時の値4ではなく5から始まっている。

おそらくcountイテレーターの__next__()メソッドの最初で内部カウンターをインクリメントしていると考えられる。

このようにイテレーターの再利用は予想外の動作をすることがあるので控えた方がよさそうだ。

 

preprocessor – 異常値に対する頑健性

機械学習モデルにデータを適用するための前処理としていくつかのアルゴリズムによっては、異常値の影響を受けやすいことがある。

たとえば下図の左のような分布のデータがあるとする(平均が1、分散が1の正規分布に従う500個のランダムデータ)。そしてこのデータに値20の異常値が10個発生したとすると、全体の分布は右のようになる。

このデータに対して、MinMaxScalerStandardScalerRobustScalerで変換した結果を以下に示す。ただしStandardScalerRobustScalerについては、異常値は表示させず元の正規分布に係る範囲のみを表示している。

まず左側のMinMaxScalerについては、異常値を含めてレンジが0~1となるので、本体の正規分布のデータが0付近の小さな値に集中する。このため、本来学習の精度に効いてくるべき本体部分のデータの分離が十分でない可能性が出てくる。

真ん中のStandardScalerと右側のRobustScalerについては、本体部分の形は元の正規分布の形と大きく変わらず、頑健であることがわかる。

ここで異常値の個数を10個から20個に増やして、同じく3種類の変換を施してみる。

左側のMinMaxScalerについては、異常値の個数とは関係なくその値のみでレンジが決まり、元の分布が0付近に押し込められている状況は同じ。

真ん中のStandardScalerについては、10個の時に比べて少し分布の形が変わっていて、レンジが狭くなっている。

右側のRobustScalerについては、元の分布の形は大きくは変わっていない。

以上のことから、少なくとも3つの変換器について以下のような特徴があることがわかる。

  • MinMaxScalerは異常値によって本来分析したいデータのレンジが狭くなる可能性がある
  • StandardScalerは異常値の影響を受けにくいが、その大きさや頻度によって若干本体部分の分布が影響を受ける
  • RobustScalerは異常値の個数が極端に多くなければ、本来のデータの特性を頑健に保持する

なお、上記の作図のコードは以下の通り。

 

RobustScaler

概要

sklearn.preprocessingモジュールのRobustScalerは、各特徴量の中央値(medi)と第1-4分位数(q1i)、第3-4分位数(q3i)を用いて特徴量を標準化する。

(1)    \begin{equation*} {F_i}^* = \frac{F_i - med_i}{q_{3i} - q_{1i}} \end{equation*}

挙動

それぞれ異なる正規分布に従う2つの特徴量について、RobustScalerを適用したときの挙動を以下に示す。異なる大きさとレンジの特徴量が、変換後には原点を中心としてほぼ同じような広がりになっているのがわかる。

コードは以下の通りで、データに対してfit()メソッドでスケールパラメーターを決定し、transform()メソッドで変換を行うところを、これらを連続して実行するfit_transform()メソッドを使っている。

簡単なデータでRobustScalerの計算過程を確認しておく。以下の例では5個のデータにRobustScalerを適用している。これは1つの特徴量を持つ5個のデータを模していることになる。

インスタンス内に保持されたパラメーターのうち、center_は特徴量の標本平均、scale_が第3-4分位数-第1-4分位数となっていて、これらで各特徴量が標準化されているのが確認できる。

特徴

RobustScalerは異常値に対して頑健であり、StandardScalerより頑健性が高い。

 

StandardScaler

概要

sklearn.preprocessingモジュールのStandardScalerは、各特徴量の標本平均と標本分散を用いて特徴量を標準化する。

具体的には、特徴量Fiの標本平均(mi)と標本分散(vi)から以下の式により各特徴量FiFi*に変換する。

(1)    \begin{equation*} {F_i}^* = \frac{F_i -m_i}{\sqrt{v_i}} \end{equation*}

挙動

それぞれ異なる正規分布に従う2つの特徴量について、StandardScalerを適用したときの挙動を以下に示す。異なる大きさとレンジの特徴量が、変換後には原点を中心としてほぼ同じような広がりになっているのがわかる。

コードは以下の通りで、データに対してfit()メソッドでスケールパラメーターを決定し、transform()メソッドで変換を行うところを、これらを連続して実行するfit_transform()メソッドを使っている。

簡単なデータでStandardScalerの計算過程を確認しておく。以下の例では5個のデータにStandardScalerを適用している。これは1つの特徴量を持つ5個のデータを模していることになる。

インスタンス内に保持されたパラメーターのうち、mean_は特徴量の標本平均、var_は標本分散(不偏分散ではない)となっている。scale_はvar_の平方根。

各データの特徴量は次式で標準化されているのが計算で確認できる。

(2)    \begin{equation*} {F_i}^* = \frac{F_i - \rm{mean\_}}{\rm{scale\_}} = \frac{F_i - \rm{mean\_}}{\sqrt{\rm{var\_}}} \end{equation*}

特徴

StandardScalerは異常値の影響に対して比較的頑健である

 

MinMaxScaler

概要

sklearn.preprocessingモジュールのMinMaxScalerは、各特徴量が0~1の範囲に納まるように変換する。具体的には、特徴量Fiの最小値(mini)と最大値(maxi)から以下の式により各特徴量FiFi*に変換する。

(1)    \begin{equation*} {F_i}^* = \frac{F_i - min_i}{max_i - min_i} \end{equation*}

挙動

それぞれ異なる正規分布に従う2つの特徴量について、MinMaxScalerを適用したときの挙動を以下に示す。異なる大きさとレンジの特徴量が、変換後にはいずれも0~1の間に納まっているのが確認できる。

コードは以下の通りで、データに対してfit()メソッドでスケールパラメーターを決定し、transform()メソッドで変換を行うところを、これらを連続して実行するfit_transform()メソッドを使っている。

特徴

MinMaxScalerは簡明な方法だが、極端に値が離れた異常値が発生すると本来のデータがその影響を受ける場合がある。