機械学習データ前処理入門

機械学習のためのデータ前処理:なぜスケーリングが必要?正規化と標準化の違い【Pandas+Scikit-learn入門】

Tags: 機械学習, データ前処理, スケーリング, 正規化, 標準化, Python

機械学習モデルを構築する際、データの準備は非常に重要なステップです。特に数値データを含む場合、「スケーリング」と呼ばれる前処理が必要になることがあります。このステップは、モデルの性能に大きく影響を与える可能性があるため、その目的と具体的な手法を理解することは、機械学習を実務に応用する上で欠かせません。

この記事では、数値データのスケーリングが必要な理由から始め、代表的な手法である正規化(Normalization)と標準化(Standardization)について、それぞれの概念、目的、そしてPython(Pandas、Scikit-learn)を用いた実装方法を解説します。

なぜ数値データのスケーリングが必要なのか?

私たちが扱うデータには、様々な種類の数値が含まれています。例えば、顧客データであれば「購入金額」(数百円から数十万円)、企業のデータであれば「従業員数」(数人から数万人)や「設立からの年数」(数年から数十年)などが考えられます。これらの数値は、それぞれスケール(値の範囲)が大きく異なります。

多くの機械学習アルゴリズム、特に距離を計算してデータの関係性を判断するようなアルゴリズム(例:サポートベクターマシン、K近傍法、主成分分析など)は、数値の大小やスケールの違いに影響を受けやすい性質があります。

例えば、購入金額の列が0〜1,000,000、設立年数の列が0〜100のような範囲を持つ場合、購入金額のわずかな差が、設立年数の大きな差よりも、モデルの計算において「重要」であるかのように扱われてしまう可能性があります。これは、単に数値の絶対的な大きさが異なるだけであり、本来意図しないバイアス(偏り)をモデルに与えてしまうことに繋がります。

スケーリングは、このようにスケールが異なる複数の数値特徴量を、同程度の範囲や分布に調整することで、モデルが公平に各特徴量を評価できるようにするための前処理です。これにより、アルゴリズムが数値のスケールに惑わされず、データの本来的なパターンや関係性をより正確に学習できるようになります。

スケーリングの代表的な手法:正規化と標準化

数値データをスケーリングする手法にはいくつか種類がありますが、特によく用いられるのが「正規化(Normalization)」と「標準化(Standardization)」です。これらは目的や計算方法が異なります。

正規化(Normalization)

正規化は、データの値を特定の範囲(多くの場合0から1の間)に収める手法です。最も一般的なのはMin-Maxスケーリングです。

目的: データの最小値を0、最大値を1として、それ以外の値を0と1の間の値に変換します。これにより、全ての数値特徴量の範囲が揃えられます。

計算方法: $$ X_{\text{normalized}} = \frac{X - X_{\text{min}}}{X_{\text{max}} - X_{\text{min}}} $$ ここで、$X$は元の値、$X_{\text{min}}$はその特徴量の最小値、$X_{\text{max}}$は最大値です。

どのような場合に有効か: データの範囲を厳密に指定したい場合や、最小値と最大値が明確で外れ値の影響を受けにくいデータに適しています。ニューラルネットワークなどで、入力値を特定の範囲に収めることが推奨される場合にも利用されます。

Pythonによる実装例(Scikit-learnのMinMaxScaler)

ここでは、数値データを含む簡単なデータフレームを例に、正規化の手順を示します。

import pandas as pd
from sklearn.preprocessing import MinMaxScaler

# サンプルデータの作成
data = {'金額': [1000, 5000, 20000, 300, 150000],
        '年齢': [25, 40, 55, 22, 60],
        '従業員数': [10, 50, 200, 5, 5000]}
df = pd.DataFrame(data)

print("--- 元のデータ ---")
print(df)

# 正規化(Min-Maxスケーリング)の適用
# MinMaxScalerを初期化
scaler = MinMaxScaler()

# データにスケーリングを適用(訓練データでfit_transformを使用)
# カラム名を保持するため、Pandas DataFrameとして再構築します
df_normalized = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)

print("\n--- 正規化後のデータ ---")
print(df_normalized)

このコードでは、MinMaxScalerを使用して各列のデータを0から1の間に変換しています。fit_transformメソッドは、データの最小値・最大値を学習(fit)し、その情報を使ってデータを変換(transform)します。

標準化(Standardization)

標準化は、データの平均が0、標準偏差が1になるように変換する手法です。Z-score標準化とも呼ばれます。

目的: データの平均を0に、散らばり具合(標準偏差)を1に揃えることで、データの分布の形状を保ちつつ、異なる特徴量間のスケールを比較可能な状態にします。

計算方法: $$ X_{\text{standardized}} = \frac{X - \mu}{\sigma} $$ ここで、$X$は元の値、$\mu$はその特徴量の平均値、$\sigma$は標準偏差です。

どのような場合に有効か: データの分布が正規分布に近い場合や、外れ値の影響を受けやすい場合(外れ値を含むとMin-Maxスケーリングでは大部分のデータが狭い範囲に圧縮されてしまう)、そして多くの線形モデル(線形回帰、ロジスティック回帰など)や、主成分分析(PCA)などの手法でよく使用されます。

Pythonによる実装例(Scikit-learnのStandardScaler)

import pandas as pd
from sklearn.preprocessing import StandardScaler

# サンプルデータは正規化と同じものを使用
data = {'金額': [1000, 5000, 20000, 300, 150000],
        '年齢': [25, 40, 55, 22, 60],
        '従業員数': [10, 50, 200, 5, 5000]}
df = pd.DataFrame(data)

print("--- 元のデータ ---")
print(df)

# 標準化(Z-score標準化)の適用
# StandardScalerを初期化
scaler = StandardScaler()

# データにスケーリングを適用(訓練データでfit_transformを使用)
# カラム名を保持するため、Pandas DataFrameとして再構築します
df_standardized = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)

print("\n--- 標準化後のデータ ---")
print(df_standardized)

このコードでは、StandardScalerを使用して各列のデータが平均0、標準偏差1になるように変換しています。fit_transformの動作はMinMaxScalerと同様です。標準化後の値は、元の値が平均からどれだけ離れているかを示し、負の値も取ります。

どちらの手法を選ぶべきか?

正規化と標準化のどちらを選ぶかは、使用する機械学習アルゴリズムやデータの性質によって異なります。

一般的には、多くのアルゴリズムで標準化がデフォルトの選択肢となることが多いですが、最終的にはいくつかの手法を試して、最もモデルの性能が向上するものを選ぶことが推奨されます。

スケーリングを適用する際の注意点

スケーリング処理を行う上で最も重要な注意点は、「訓練データでスケーラーを学習させ、その学習したスケーラーを使って訓練データとテストデータの両方を変換する」という点です。

機械学習モデルの性能評価では、未知のデータ(テストデータ)に対する予測精度を確認します。テストデータは、モデル構築時には参照すべきではありません。これはスケーリングにおいても同様で、テストデータの平均値や標準偏差、最小値・最大値を使ってスケーラーを学習させてしまうと、テストデータの情報がモデル構築プロセスに漏洩(データリーケージ)することになり、正しい性能評価ができなくなります。

正しい手順は以下のようになります。

  1. データを訓練データとテストデータに分割する。
  2. 訓練データのみを使用してスケーラー(MinMaxScalerStandardScalerなど)をfit(学習)させる。
  3. fitで学習したスケーラーを使って、訓練データとテストデータの両方transform(変換)する。

これにより、テストデータは訓練データと同じ基準でスケーリングされ、公正な評価が可能になります。

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

# サンプルデータの作成(特徴量Xと目的変数yに分ける想定)
data = {'金額': [1000, 5000, 20000, 300, 150000],
        '年齢': [25, 40, 55, 22, 60],
        '従業員数': [10, 50, 200, 5, 5000],
        '購入有無': [0, 1, 1, 0, 1]} # 例:目的変数
df = pd.DataFrame(data)

# 特徴量と目的変数に分割
X = df[['金額', '年齢', '従業員数']]
y = df['購入有無']

# データを訓練用とテスト用に分割
# random_stateを固定することで、毎回同じ分割結果になります
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

print("--- 分割前のデータ ---")
print(X)
print("\n--- 訓練用特徴量データ ---")
print(X_train)
print("\n--- テスト用特徴量データ ---")
print(X_test)

# 標準化(Z-score標準化)の適用
scaler = StandardScaler()

# 訓練データでスケーラーを学習(fit)し、訓練データを変換(transform)
X_train_scaled = scaler.fit_transform(X_train)

# テストデータに、訓練データで学習したスケーラーを適用(transformのみ!)
X_test_scaled = scaler.transform(X_test)

print("\n--- 訓練用特徴量データ(標準化後) ---")
# numpy配列になるためDataFrameに戻して表示
print(pd.DataFrame(X_train_scaled, columns=X_train.columns, index=X_train.index))
print("\n--- テスト用特徴量データ(標準化後) ---")
print(pd.DataFrame(X_test_scaled, columns=X_test.columns, index=X_test.index))

このコード例のように、X_trainに対してはfit_transformを使い、X_testに対してはtransformのみを使うのが正しいスケーリングのプロセスです。

まとめ

この記事では、機械学習における数値データのスケーリングの重要性、そして代表的な手法である正規化(Min-Maxスケーリング)と標準化(Z-score標準化)について解説しました。

データ前処理は、機械学習モデルの性能を左右する重要なステップです。特に数値データに対するスケーリングは多くのケースで必要となりますので、ぜひこの記事の内容を参考に、実際のデータで試してみてください。他のデータ前処理手法についても、今後解説していく予定です。