機械学習の前処理でのデータの正規化/標準化

前処理の目的

  • 体重と身長など複数の異なる特徴量を生で扱ってしまうと、出力に対して使いやすい(大まかにFitしやすい)方を優先してしまう問題がある。

内容

  • 問題解決の単純な方法は異なるカテゴリのデータを同じ範囲のデータに変換する。
  • 変換は入力だけでなく、出力も変換する。
  • また、一般的には各特徴量毎に変換する。ただし、画像のように2次元(行/列)ではあるが、意味が全ての次元で等しい場合には、全体で変換することも考えられる。

具体的な変換方法

  • 一般的には正規化と標準化(ZScore化とも言われる)が良く使われる。
  • 正規化は、変換後のデータの最小値, 最大値を0, 1変換する。背景にデータが一様分布に従う場合を想定している。
  • 標準化は、平均0, 標準偏差1に変換する。背景にデータが正規化分布に従うことを想定している。

実践

  • 2次元データ(n_batch, n_feature)のデータであればSklearnのpreprocessingモジュールで簡単に変換できる。
  • 3次元データ、例えば(n_batch, n_object_type, n_object_feature)のような場合にはSklearnが使えない。その場合は下記のような関数を使う。
import sys
import numpy as np
import logging

logger = logging.getLogger(__name__)


class Scaler():
    '''
    Sklearn-API like simple scaler for data preprocessing.
    '''
    def __init__(self, mode):
        if not(mode in ['maxmin', 'standard']):
            logger.error("The mode must be 'maxmin' or 'standard'.")
            sys.exit(1)
        self.mode = mode
        self.maxs = None
        self.mins = None
        self.means = None
        self.stds = None

    def fit(self, data, axis=0):
        '''
        Args:
          data: Ndim numpy array .
          axis: scaling axis, usually batch dimension.
        '''
        if self.mode == 'maxmin':
            self.maxs = data.max(axis=axis)
            self.mins = data.min(axis=axis)
        elif self.mode == 'standard':
            self.means = data.mean(axis=axis)
            self.stds = data.std(axis=axis)
        return

    def transform(self, data):
        if self.mode == 'maxmin':
            d = (data - self.mins)/(self.maxs - self.mins)
        elif self.mode == 'standard':
            d = (data - self.means)/(self.stds)
        return d

    def fit_transform(self, data, axis=0):
        self.fit(data, axis)
        d = self.transform(data)
        return d

    def inverse_transform(self, data):
        if self.mode == 'maxmin':
            d = data*(self.maxs - self.mins) + self.mins
        elif self.mode == 'standard':
            d = (data - self.means)/(self.stds)
            d = data*self.stds + self.means
        return d        

    
if __name__ == '__main__':
    d0 = np.random.randn(100,4,2)

    s = Scaler('maxmin')
    s.fit(d0)
    d1 = s.transform(d0)
    d2 = s.inverse_transform(d1)

    s = Scaler('standard')
    s.fit(d0)
    d1 = s.transform(d0)
    d2 = s.inverse_transform(d1)