なぜ機械学習の精度が変わる?StandardScalerによる「標準化」の重要性と使い方を解説

Python

械学習のモデル構築に取り組んでいると、「思ったように精度が上がらない…」という壁にぶつかることはありませんか?特徴量を追加したり、モデルのハイパーパラメータを調整したり、様々な試行錯誤をされていることでしょう。

しかし、もしデータの前処理、特に「標準化」というステップを見過ごしているなら、それが精度の伸び悩みの原因かもしれません。

この記事では、機械学習の前処理において極めて重要な役割を担う、Scikit-learnのStandardScalerに焦点を当てます。

この記事を最後まで読めば、あなたは以下の状態になれます。

  • なぜデータを「標準化」する必要があるのか、その重要性を根本から理解できる。
  • StandardScalerfittransformの役割の違いを明確に説明できるようになる。
  • 訓練データとテストデータに対して、StandardScalerを正しく使い分ける方法が身につく。
  • 「データリーケージ」という、初心者が陥りがちな罠を回避できるようになる。

「前処理はなんとなくやっていた」という方から、「これから機械学習を本格的に学びたい」という方まで、モデルの精度を一段階引き上げるための確かな知識を提供します。ぜひ、最後までお付き合いください。

はじめに:そのモデルの精度、データの前処理で改善できるかもしれません

結論から言うと、機械学習モデルのパフォーマンスは、使用するアルゴリズムだけでなく、入力されるデータの質に大きく依存します。 そして、その質を高めるための工程が「データの前処理」です。

例えば、あるECサイトの顧客データについて考えてみましょう。特徴量として「年齢(20〜60歳)」と「年間購入額(5,000〜2,000,000円)」があったとします。この二つの特徴量は、単位も数値のスケール(範囲)も全く異なります。

多くの機械学習アルゴリズムは、このようなスケールの違いがあると、数値の大きい「年間購入額」の方をより重要な特徴量だと誤って解釈してしまう傾向があります。年齢という重要な情報が、数値の大きさだけで軽視されてしまうのです。

このような問題を解決するのが、今回紹介するStandardScalerを用いた「標準化(Standardization)」です。StandardScalerは、乱雑に散らばった各特徴量のスケールを一定の基準に揃えることで、モデルが各特徴量を公平に評価できるようにする、いわば「データの交通整理人」のような存在なのです。

なぜ重要?機械学習における「標準化」の必要性

それでは、なぜこの「標準化」がそれほどまでに重要なのでしょうか。その理由をもう少し深く掘り下げてみましょう。

スケールの違いがモデルに与える悪影響とは?

結論として、特徴量ごとの「単位」や「大きさ」がバラバラだと、アルゴリズムが各特徴量の重要度を正しく学習できないためです。

特に、以下の二種類のアルゴリズムは、特徴量のスケールの影響を大きく受けます。

  1. 距離ベースのアルゴリズム
    • 代表例: k-NN(k近傍法)、SVM(サポートベクターマシン)、K-Means(クラスタリング)など
    • これらのアルゴリズムは、データポイント間の「距離」を計算して類似性を判断します。先ほどの例で言えば、「年齢」と「年間購入額」の距離を計算しようとすると、数値の絶対値が大きい「年間購入額」の変化が距離計算の大部分を占めてしまいます。結果として、モデルは「年間購入額」ばかりを重視し、「年齢」の持つ情報をほとんど無視してしまうのです。
  2. 勾配降下法を用いるアルゴリズム
    • 代表例: 線形回帰、ロジスティック回帰、ニューラルネットワークなど
    • これらのアルゴリズムは、「勾配降下法」という手法を使って、モデルの誤差が最小になるようなパラメータを少しずつ探索していきます。特徴量のスケールが揃っていないと、パラメータの探索空間が歪んだ形(細長い楕円のような形)になってしまいます。そのため、最適解への道のりが非常に遠回りになり、学習がなかなか収束しなかったり、局所的な最適解に陥ってしまったりする問題が発生しやすくなります。

一方で、決定木やランダムフォレスト、XGBoostといったツリーベースのアルゴリズムは、各特徴量を独立して分岐の条件に使うため、スケールの違いによる直接的な影響は受けにくいとされています。しかし、これらのモデルでも、標準化によって性能が改善するケースもあるため、迷ったらまず標準化を試してみるのが良いプラクティスです。

標準化(Standardization)が解決してくれること

結論は、全特徴量を「平均0、標準偏差1」という共通のモノサシに揃え、モデルの学習を効率化し、精度を向上させることです。

StandardScalerが行う標準化は、具体的には以下の式で計算されます。

z=σx−μ​

ここで、

  • z は標準化後の値
  • x は元のデータの値
  • μ(ミュー)は元のデータの平均値
  • σ(シグマ)は元のデータの標準偏差

を指します。この計算により、元のデータがどのような分布やスケールであっても、変換後のデータは平均が0標準偏差が1となる正規分布に近い形に変換されます。これにより、全ての変数が同じ土俵で評価されるようになり、アルゴリズムは各特徴量の本質的な重要度を学習できるようになるのです。

よくある疑問:「正規化(Normalization)」との違いは?

結論から言うと、標準化は外れ値の影響を受けにくく、正規化はデータを0から1の範囲にきっちり収めたい場合に使うという使い分けが一般的です。

前処理の話で「標準化」とともによく登場するのが「正規化(Normalization)」です。この二つは混同されがちですが、目的と計算方法が異なります。

  • 標準化 (Standardization)
    • 手法: StandardScaler
    • 変換後: 平均が0、標準偏差が1になるように変換。
    • 特徴: 元のデータの分布の形状を維持しやすい。外れ値(極端に大きい、または小さい値)が存在しても、平均と標準偏差への影響が限定的であるため、比較的ロバスト(頑健)です。
    • おすすめのケース: 外れ値を含む可能性があるデータや、PCAなどの分散を重視する手法を用いる場合。
  • 正規化 (Normalization)
    • 手法: MinMaxScaler
    • 変換後: データの最小値が0、最大値が1になるように変換。
    • 特徴: 全てのデータが0〜1の範囲にきっちりと収まります。しかし、一つでも巨大な外れ値があると、他の多くのデータが非常に狭い範囲に押し込められてしまい、データの大小関係が分かりにくくなるデメリットがあります。
    • おすすめのケース: 画像処理(ピクセルの値を0〜1に収める)や、ニューラルネットワークなど、入力値の範囲が明確に決まっている方が学習が安定するアルゴリズム。

どちらを使うべきか迷った場合は、まずは外れ値に強い標準化 (StandardScaler) を試すのが安全な選択肢と言えるでしょう。

Scikit-learnのStandardScalerとは?

ここからは、実際にPythonのコードを使ってStandardScalerの挙動を見ていきましょう。

StandardScalerの基本的な仕組み

改めて結論を述べると、StandardScalerは、各データからその特徴量の平均を引き、標準偏差で割ることで標準化を実現するクラスです。

この「平均」と「標準偏差」をデータから計算するステップと、その計算結果を使って実際にデータを変換するステップが、StandardScalerを理解する上で最も重要なポイントとなります。

準備:ライブラリのインポートとサンプルデータ

まずは、今回使用するライブラリと、動作確認用の簡単なサンプルデータを用意します。pandasを使ってDataFrameを作成し、それを使って解説を進めます。

# 必要なライブラリをインポート
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler

# サンプルデータを作成
# 異なるスケールを持つ2つの特徴量('feature1', 'feature2')を用意
data = {
    'feature1': [10, 20, 30, 40, 50],
    'feature2': [1000, 2000, 1500, 3000, 2500]
}
df = pd.DataFrame(data)

print("--- 元のデータ ---")
print(df)
print("\n--- 元のデータの基本統計量 ---")
print(df.describe())

実行結果:

--- 元のデータ ---
   feature1  feature2
0        10      1000
1        20      2000
2        30      1500
3        40      3000
4        50      2500

--- 元のデータの基本統計量 ---
       feature1     feature2
count       5.0          5.0
mean       30.0       2000.0  <- 平均 (mean)
std        15.811388  790.569415 <- 標準偏差 (std)
min        10.0       1000.0
25%        20.0       1500.0
50%        30.0       2000.0
75%        40.0       2500.0
max        50.0       3000.0Code language: CSS (css)

feature1は平均30、feature2は平均2000と、スケールが大きく異なることがわかります。このデータをStandardScalerで変換していきましょう。

【最重要】StandardScalerの正しい使い方とコード例

StandardScalerには、fit(), transform(), そして fit_transform() という3つの重要なメソッドがあります。これらの違いを理解することが、StandardScalerを使いこなすための鍵となります。

fit() の役割:データの「平均」と「標準偏差」を学習する

結論として、fit()変換に必要なルール(この場合は各特徴量の平均値と標準偏差)をデータから計算・学習するだけで、データ自体は変換しません。

fit()はいわば「準備」のステップです。データにどのような変換を施すべきか、その設計図を作る作業と考えることができます。

# 1. StandardScalerのインスタンスを作成
scaler = StandardScaler()

# 2. fit()メソッドで、データの平均と標準偏差を学習させる
scaler.fit(df)

# fit()で学習した内容を確認
print(f"学習した平均 (mean_): {scaler.mean_}")
print(f"学習した標準偏差 (scale_): {scaler.scale_}")

実行結果:

学習した平均 (mean_): [  30. 2000.]
学習した標準偏差 (scale_): [  14.14213562  707.10678119]Code language: CSS (css)

scaler.mean_scaler.scale_(標準偏差)に、先ほどdf.describe()で確認した値が計算されて格納されていることがわかります。この時点では、dfの中身は何も変わっていないことに注意してください。scalerオブジェクトが、変換ルールを内部に記憶しただけです。

df.describe()で計算される標準偏差は不偏標準偏差、StandardScalerで計算されるのは標本標準偏差のため、値が若干異なりますが、ここでは同じものとして扱います。

transform() の役割:学習したルールでデータを変換する

結論は、transform()は**fit()で学習・記憶したルールを使って、実際にデータを標準化する**メソッドです。

fit()で作った設計図をもとに、実際に変換作業を行うのがtransform()です。重要なのは、transform()は単体では使えず、必ず事前にfit()でルールを学習させておく必要があるということです。

# 3. transform()メソッドで、学習したルールに基づいてデータを変換する
df_scaled = scaler.transform(df)

print("--- transform()で変換後のデータ ---")
print(df_scaled)
print(f"\nデータ型: {type(df_scaled)}")

実行結果:

--- transform()で変換後のデータ ---
[[-1.41421356 -1.41421356]
 [-0.70710678  0.        ]
 [ 0.          -0.70710678]
 [ 0.70710678  1.41421356]
 [ 1.41421356  0.70710678]]

データ型: <class 'numpy.ndarray'>Code language: HTML, XML (xml)

transform()を実行すると、元のDataFrameがNumPyの配列(ndarray)に変換され、各値が標準化されていることがわかります。

この変換されたデータの平均と標準偏差を確認してみましょう。

# 変換後のデータの平均と標準偏差を計算
print(f"\n変換後の平均: {df_scaled.mean(axis=0)}")
print(f"変換後の標準偏差: {df_scaled.std(axis=0)}")

実行結果:

変換後の平均: [0. 0.]
変換後の標準偏差: [1. 1.]Code language: CSS (css)

axis=0は列ごとの計算を指定します。結果を見ると、各特徴量(列)の平均がほぼ0、標準偏差が1になっていることが確認できます。これで、スケールが異なる二つの特徴量が、同じ基準で評価できる状態になりました。

fit_transform() の役割:学習と変換を一度に行う

結論は、fit_transform()訓練データに対して「学習(fit)」と「変換(transform)」を一度に実行してくれる便利なメソッドです。

これまで見てきたfit()transform()の2ステップを、1つのコマンドでまとめて実行できます。

# --- fit_transform() を使った場合 ---

# サンプルデータを再度用意
data2 = {
    'feature1': [10, 20, 30, 40, 50],
    'feature2': [1000, 2000, 1500, 3000, 2500]
}
df2 = pd.DataFrame(data2)

# 新しいStandardScalerのインスタンスを作成
scaler2 = StandardScaler()

# fit_transform()で学習と変換を同時に実行
df2_scaled = scaler2.fit_transform(df2)

print("--- fit_transform()で変換後のデータ ---")
print(df2_scaled)

print(f"\n変換後の平均: {df2_scaled.mean(axis=0)}")
print(f"変換後の標準偏差: {df2_scaled.std(axis=0)}")

実行結果:

--- fit_transform()で変換後のデータ ---
[[-1.41421356 -1.41421356]
 [-0.70710678  0.        ]
 [ 0.          -0.70710678]
 [ 0.70710678  1.41421356]
 [ 1.41421356  0.70710678]]

変換後の平均: [0. 0.]
変換後の標準偏差: [1. 1.]Code language: CSS (css)

結果は、fit()transform()を別々に行った場合と全く同じです。コードが簡潔になるため、訓練データを標準化する際には、このfit_transform()を使うのが一般的です。

【実践】機械学習フローにおけるStandardScalerの注意点

fit, transform, fit_transformの違いを理解したところで、いよいよ実践です。実際の機械学習プロジェクトでは、データを「訓練データ」と「テストデータ」に分割してモデルを評価します。この分割したデータに対してStandardScalerを適用する際に、絶対に守らなければならないルールがあります。

やってはいけない!データ全体を先に標準化するNG例

結論から言うと、テストデータを含む全てのデータを使ってfitしてしまうと、テストデータの情報が訓練データに漏れてしまい(データリーケージ)、モデルの性能を正しく評価できなくなるためです。

テストデータは、モデルにとっては「未知のデータ」でなければなりません。しかし、先にデータ全体でfitを行うと、テストデータの平均値や標準偏差まで「学習」してしまいます。これは、未来のデータ(答え)をカンニングしてモデルを訓練するようなもので、モデルの汎化性能(未知のデータに対する予測能力)を過大評価してしまう原因となります。

# --- NG例:データ分割前に標準化してしまう ---
from sklearn.model_selection import train_test_split

# サンプルデータ
X = np.array([[1, 100], [2, 200], [3, 300], [4, 400], [5, 500], [6, 600]])
y = np.array([0, 0, 0, 1, 1, 1])

# 1. (NG) データ全体でfitしてしまう
scaler_ng = StandardScaler()
X_scaled_ng = scaler_ng.fit_transform(X) # テストデータの情報も使って学習してしまっている!

# 2. その後でデータを分割
X_train, X_test, y_train, y_test = train_test_split(X_scaled_ng, y, test_size=0.33, random_state=42)

# これはやってはいけない例です!

この方法では、X_testを作る元となったデータも標準化の計算(平均・標準偏差の算出)に使われてしまっています。

これが正解!訓練データとテストデータで使い方を分ける

結論は、訓練データにはfit_transform()を、テストデータにはtransform()を使うのが鉄則です。

正しい手順は以下の通りです。

  1. まず、生データのまま訓練データとテストデータに分割する。
  2. 訓練データのみを使ってfit()(またはfit_transform())を実行し、標準化のルール(平均・標準偏差)を学習させる。
  3. 学習したそのルールを使って、訓練データとテストデータの両方にtransform()を適用する。

これにより、テストデータは「訓練データから作られたルール」に従って変換されるため、未知のデータとしての前提が保たれます。

# --- OK例:データ分割後に正しく標準化する ---
from sklearn.model_selection import train_test_split

# サンプルデータ
X = np.array([[1, 100], [2, 200], [3, 300], [4, 400], [5, 500], [6, 600]])
y = np.array([0, 0, 0, 1, 1, 1])

# 1. まずデータを訓練用とテスト用に分割する
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)

print("--- 分割直後のデータ ---")
print("X_train:\n", X_train)
print("X_test:\n", X_test)

# 2. StandardScalerのインスタンスを作成
scaler_ok = StandardScaler()

# 3. (重要) 訓練データのみでfitし、変換する
X_train_scaled = scaler_ok.fit_transform(X_train)

# 4. (重要) テストデータは、訓練データで学習したscalerを使ってtransformのみ行う
X_test_scaled = scaler_ok.transform(X_test)

print("\n--- 正しく標準化されたデータ ---")
print("X_train_scaled:\n", X_train_scaled)
print("X_test_scaled:\n", X_test_scaled)

この手順こそが、データリーケージを防ぎ、モデルの性能を公平に評価するための唯一の正しい方法です。このルールは絶対に覚えておきましょう。

Pandas DataFrameで使うときのワンポイント

結論として、StandardScalerを通すとNumPy配列(ndarray)になってしまい、元のカラム名やインデックス情報が失われるため、必要に応じてDataFrameに戻す作業が必要です。

実務ではpandasのDataFrameを扱うことがほとんどです。StandardScalerにDataFrameを渡すと、返り値がNumPy配列になるという特性を理解しておく必要があります。

# サンプルDataFrame
df_sample = pd.DataFrame({
    'age': [25, 30, 35, 40, 45],
    'income': [400, 500, 450, 600, 550]
})

scaler_df = StandardScaler()

# fit_transformするとNumPy配列になる
df_scaled_array = scaler_df.fit_transform(df_sample)

print("--- スケーリング後の型 ---")
print(type(df_scaled_array))
print("\n--- スケーリング後のデータ(NumPy配列) ---")
print(df_scaled_array)

# カラム名とインデックスを再設定してDataFrameに戻す
df_scaled = pd.DataFrame(df_scaled_array, columns=df_sample.columns, index=df_sample.index)

print("\n--- 再度DataFrameに変換したデータ ---")
print(df_scaled)

このように、pd.DataFrame()のコンストラクタに、変換後のNumPy配列と、元のカラム名・インデックスを渡すことで、情報を保持したままのDataFrameを再作成できます。このひと手間を覚えておくと、後続の分析や可視化が非常にスムーズになります。

まとめ:StandardScalerを使いこなし、モデルの精度を高めよう

今回は、機械学習の精度向上に不可欠な前処理手法である「標準化」と、それを実現するScikit-learnのStandardScalerについて、その重要性から具体的な使い方、そして実践的な注意点までを詳しく解説しました。

最後に、この記事の重要なポイントをまとめます。

  • 標準化の重要性: 特徴量ごとのスケールの違いによるモデルの偏見を取り除き、アルゴリズムが公平に学習できるようにするために不可欠。特に距離ベースや勾配降下法を用いるモデルで効果が大きい。
  • fit()transform()の違い:
    • fit(): 変換ルール(平均・標準偏差)を学習する
    • transform(): 学習済みのルールでデータを変換する
    • fit_transform(): 学習と変換を同時に行う(訓練データに使う)。
  • データリーケージの防止: 必ずデータを訓練用とテスト用に分割してから標準化を行うこと。
  • 正しい適用手順:
    1. 訓練データには fit_transform() を使う。
    2. テストデータには、訓練データで学習したスケーラーを使って transform() のみを使う。

StandardScalerは、一見地味な存在かもしれませんが、その役割はモデルの性能を根底から支える非常に重要なものです。今日学んだ知識をあなたのプロジェクトで実践すれば、これまで超えられなかった精度の壁を突破するきっかけになるかもしれません。

データの前処理を制する者は、機械学習を制します。ぜひ、StandardScalerをあなたの強力な武器の一つに加えてください。

コメント

タイトルとURLをコピーしました