W3docs

Масштабирование признаков в Python

Масштабирование признаков в Python с scikit-learn: StandardScaler, MinMaxScaler, RobustScaler и MaxAbsScaler — с примерами и выводом.

Модели машинного обучения обучаются на наборах данных, в которых каждый признак может охватывать совершенно разные числовые диапазоны. Яркость пикселей может варьироваться от 0 до 255, тогда как доход — от 0 до 1 000 000. Когда масштабы признаков различаются на порядки, алгоритмы, основанные на расстояниях (k-NN, SVM, k-means) или на градиентном спуске (логистическая регрессия, нейронные сети), могут придавать непропорционально большой вес крупнейшим признакам. Масштабирование признаков приводит все признаки к сопоставимому диапазону, чтобы модель могла одинаково учиться на всех них.

В этой главе объясняется, почему масштабирование важно, рассматриваются четыре масштабировщика из scikit-learn, показаны их результаты на реальных данных и демонстрируется правильный способ интеграции масштабирования в конвейер машинного обучения без утечки данных из тестовой выборки.

Почему важно масштабирование признаков

Рассмотрим простой набор данных с двумя признаками — age (20–60) и income (20 000–100 000). Значения столбца income примерно в 1 000 раз больше значений age. Для алгоритмов, измеряющих евклидово расстояние, изменение income на одну единицу полностью перекрывает изменение age на одну единицу.

Конкретные случаи, когда масштабирование необходимо:

  • Алгоритмы на основе расстоянийK ближайших соседей и кластеризация K-средних вычисляют расстояния между точками данных. При немасштабированных признаках метрика расстояния теряет смысл.
  • Оптимизаторы на основе градиентного спускалогистическая регрессия и линейная регрессия сходятся значительно быстрее, когда все признаки находятся в схожих диапазонах.
  • Регуляризованные модели — Ridge, Lasso и ElasticNet одинаково штрафуют большие коэффициенты. Признак с большим масштабом искусственно притягивает малый коэффициент, скрывая его истинную значимость.

Когда масштабирование НЕ нужно: древесные модели (деревья решений, случайный лес, градиентный бустинг) разбивают данные по отдельным порогам признаков. Абсолютный масштаб признака не влияет на то, где находится лучшее разбиение, поэтому масштабирование не влияет на точность этих моделей.

Четыре масштабировщика scikit-learn

StandardScaler

StandardScaler (также называемый z-оценочной нормализацией) преобразует каждый признак так, чтобы его среднее значение стало равным 0, а стандартное отклонение — 1.

Формула для каждого признака:

z = (x − mean) / std

Это выбор по умолчанию, если нет веских причин предпочесть другой вариант. Хорошо работает для данных с нормальным распределением и ожидается большинством регуляризованных линейных моделей.

from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import numpy as np

data = load_iris()
X = data.data          # shape (150, 4)
y = data.target

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)   # fit + transform on training data
X_test_scaled  = scaler.transform(X_test)        # transform only (no re-fit)

# Inspect the result
print("Training set — mean per feature (should be ~0):")
print(np.round(X_train_scaled.mean(axis=0), 4))

print("\nTraining set — std per feature (should be ~1):")
print(np.round(X_train_scaled.std(axis=0), 4))

Вывод:

Training set — mean per feature (should be ~0):
[ 0. -0. -0. -0.]

Training set — std per feature (should be ~1):
[1. 1. 1. 1.]

Обратите внимание, что fit_transform вызывается только на обучающей выборке. Вызов его на тестовой выборке также привёл бы к утечке статистики тестовых данных в параметры масштабировщика — см. раздел Утечка данных ниже.

MinMaxScaler

MinMaxScaler пересчитывает каждый признак в фиксированный диапазон, по умолчанию [0, 1].

Формула для каждого признака:

x_scaled = (x − min) / (max − min)

Используйте MinMaxScaler, когда вам нужны значения в ограниченном диапазоне — например, при подаче данных в нейронную сеть с сигмоидальными выходными нейронами или когда нижележащий алгоритм ожидает неотрицательных входных данных.

from sklearn.preprocessing import MinMaxScaler

mm_scaler = MinMaxScaler()   # default feature_range=(0, 1)
X_train_mm = mm_scaler.fit_transform(X_train)
X_test_mm  = mm_scaler.transform(X_test)

print("Min per feature (should be 0):", np.round(X_train_mm.min(axis=0), 4))
print("Max per feature (should be 1):", np.round(X_train_mm.max(axis=0), 4))

Вывод:

Min per feature (should be 0): [0. 0. 0. 0.]
Max per feature (should be 1): [1. 1. 1. 1.]

Важно: MinMaxScaler очень чувствителен к выбросам. Одно экстремальное значение сжимает все остальные значения в узкую полосу на одном конце диапазона. Если ваши данные содержат выбросы, рассмотрите вместо него RobustScaler.

RobustScaler

RobustScaler использует медиану и межквартильный размах (IQR) вместо среднего и стандартного отклонения, что делает его устойчивым к выбросам.

Формула для каждого признака:

x_scaled = (x − median) / IQR

где IQR = Q3 − Q1 (75-й процентиль минус 25-й процентиль).

from sklearn.preprocessing import RobustScaler

rb_scaler = RobustScaler()
X_train_rb = rb_scaler.fit_transform(X_train)
X_test_rb  = rb_scaler.transform(X_test)

print("Median per feature (should be ~0):")
print(np.round(np.median(X_train_rb, axis=0), 4))

Вывод:

Median per feature (should be ~0):
[0. 0. 0. 0.]

RobustScaler — правильный выбор, когда ваш набор данных содержит выбросы, которые нельзя или не нужно удалять.

MaxAbsScaler

MaxAbsScaler делит каждое значение на максимальное абсолютное значение в обучающей выборке, помещая каждый признак в диапазон [−1, 1] без сдвига данных.

Формула для каждого признака:

x_scaled = x / max(|x|)

Это единственный масштабировщик, который оставляет данные, центрированные вокруг нуля, в центрированном состоянии. Он специально предназначен для разреженных матриц (например, TF-IDF векторов из текста), где сдвиг среднего уничтожил бы разреженность.

from sklearn.preprocessing import MaxAbsScaler

ma_scaler = MaxAbsScaler()
X_train_ma = ma_scaler.fit_transform(X_train)
X_test_ma  = ma_scaler.transform(X_test)

print("Max absolute value per feature (should be 1):")
print(np.round(np.abs(X_train_ma).max(axis=0), 4))

Вывод:

Max absolute value per feature (should be 1):
[1. 1. 1. 1.]

Сравнение всех четырёх масштабировщиков

В таблице ниже приведены рекомендации по выбору масштабировщика:

МасштабировщикДиапазон выводаЧувствительность к выбросамЛучше всего подходит для
StandardScalerнеограниченный (mean=0, std=1)ДаДанные с нормальным распределением, регуляризованные линейные модели
MinMaxScaler[0, 1] (настраивается)ДаНейронные сети, пиксельные данные изображений
RobustScalerнеограниченный (median=0)НетДанные со значительными выбросами
MaxAbsScaler[−1, 1]ДаРазреженные матрицы (текстовые признаки)

Предотвращение утечки данных

Утечка данных происходит, когда информация из тестовой выборки влияет на этап предобработки. Типичная ошибка — подгонка масштабировщика на всём наборе данных до разбиения:

# WRONG — leaks test-set statistics into the scaler
scaler = StandardScaler()
X_all_scaled = scaler.fit_transform(X)           # uses test rows
X_train_bad, X_test_bad, _, _ = train_test_split(
    X_all_scaled, y, test_size=0.2, random_state=42
)

Правильный порядок всегда таков: сначала разбить данные, затем подогнать масштабировщик только на обучающих данных:

# CORRECT
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)   # fit on train only
X_test_scaled  = scaler.transform(X_test)        # apply the same transformation

Подробнее о самом разбиении на обучающую и тестовую выборки см. в разделе Train/Test Split.

Использование конвейера для безопасного масштабирования

Pipeline из scikit-learn — наиболее надёжный способ совместить масштабирование с моделью. Конвейер гарантирует, что fit_transform будет вызван на обучающих данных, а transform — только на отложенной выборке, даже при перекрёстной проверке.

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_iris
import numpy as np

X, y = load_iris(return_X_y=True)

pipe = Pipeline(steps=[
    ("scaler", StandardScaler()),
    ("model",  LogisticRegression(max_iter=200)),
])

scores = cross_val_score(pipe, X, y, cv=5, scoring="accuracy")
print("CV accuracy scores:", np.round(scores, 3))
print("Mean accuracy:      ", round(scores.mean(), 3))

Вывод:

CV accuracy scores: [0.967 1.    0.933 0.9   1.   ]
Mean accuracy:       0.96

Конвейер автоматически устраняет риск утечки данных во всех пяти фолдах перекрёстной проверки.

Подбор гиперпараметров масштабировщика

Вы можете подбирать выбор масштабировщика наряду с гиперпараметрами модели с помощью Grid Search:

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_iris
import numpy as np

X, y = load_iris(return_X_y=True)

pipe = Pipeline(steps=[
    ("scaler", StandardScaler()),
    ("model",  LogisticRegression(max_iter=300)),
])

param_grid = {
    "scaler": [StandardScaler(), MinMaxScaler(), RobustScaler()],
    "model__C": [0.1, 1.0, 10.0],
}

grid = GridSearchCV(pipe, param_grid, cv=5, scoring="accuracy")
grid.fit(X, y)

print("Best scaler:", grid.best_params_["scaler"].__class__.__name__)
print("Best C:     ", grid.best_params_["model__C"])
print("Best score: ", round(grid.best_score_, 3))

Вывод:

Best scaler: StandardScaler
Best C:      10.0
Best score:  0.973

Это позволяет данным самим подсказывать, какой масштабировщик лучше всего подходит для конкретной модели и набора данных.

Связанные главы

  • Train/Test Split — разбейте данные перед подгонкой любого масштабировщика
  • Categorical Data — закодируйте нечисловые признаки перед масштабированием
  • K-Nearest Neighbors — алгоритм на основе расстояний, требующий масштабированных признаков
  • K-Means Clustering — алгоритм кластеризации, чувствительный к масштабу признаков
  • Linear Regression — модель на основе градиентного спуска, быстрее сходящаяся при масштабировании
  • Logistic Regression — регуляризованный классификатор, которому выгоден StandardScaler
  • Cross Validation — используйте Pipeline, чтобы масштабирование не вызывало утечку данных при CV
  • Grid Search — подбирайте выбор масштабировщика наряду с гиперпараметрами модели
Was this page helpful?