Примечание
Перейти в конец чтобы скачать полный пример кода или запустить этот пример в браузере через JupyterLite или Binder.
Важность масштабирования признаков#
Масштабирование признаков через стандартизацию, также называемую Z-нормализацией, является важным этапом предобработки для многих алгоритмов машинного обучения. Оно включает перемасштабирование каждого признака так, чтобы он имел стандартное отклонение 1 и среднее значение 0.
Даже если модели на основе деревьев (почти) не подвержены влиянию масштабирования, многие другие алгоритмы требуют нормализации признаков, часто по разным причинам: для облегчения сходимости (например, логистическая регрессия без штрафа), чтобы создать совершенно другую модель по сравнению с моделью на немасштабированных данных (например, модели KNeighbors). Последнее демонстрируется в первой части данного примера.
Во второй части примера мы показываем, как метод главных компонент (PCA)
зависит от нормализации признаков. Чтобы проиллюстрировать это, мы сравниваем
главные компоненты, найденные с использованием PCA на не масштабированных данных с теми, которые получены при использовании
StandardScaler сначала масштабировать данные.
В последней части примера показано влияние нормализации на точность модели, обученной на данных, уменьшенных с помощью PCA.
# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause
Загрузка и подготовка данных#
Используемый набор данных - это Набор данных для распознавания вина доступен на UCI. Этот набор данных имеет непрерывные признаки, которые разнородны по масштабу из-за различных свойств, которые они измеряют (например, содержание алкоголя и яблочная кислота).
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
X, y = load_wine(return_X_y=True, as_frame=True)
scaler = StandardScaler().set_output(transform="pandas")
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.30, random_state=42
)
scaled_X_train = scaler.fit_transform(X_train)
Влияние масштабирования на модели k-ближайших соседей#
Для визуализации границы решения
KNeighborsClassifier, в этом разделе мы выбираем
подмножество из 2 признаков, которые имеют значения с разными порядками величины.
Имейте в виду, что использование подмножества признаков для обучения модели может, вероятно, исключить признаки с высоким прогностическим влиянием, что приведет к границе решений, которая значительно хуже по сравнению с моделью, обученной на полном наборе признаков.
import matplotlib.pyplot as plt
from sklearn.inspection import DecisionBoundaryDisplay
from sklearn.neighbors import KNeighborsClassifier
X_plot = X[["proline", "hue"]]
X_plot_scaled = scaler.fit_transform(X_plot)
clf = KNeighborsClassifier(n_neighbors=20)
def fit_and_plot_model(X_plot, y, clf, ax):
clf.fit(X_plot, y)
disp = DecisionBoundaryDisplay.from_estimator(
clf,
X_plot,
response_method="predict",
alpha=0.5,
ax=ax,
)
disp.ax_.scatter(X_plot["proline"], X_plot["hue"], c=y, s=20, edgecolor="k")
disp.ax_.set_xlim((X_plot["proline"].min(), X_plot["proline"].max()))
disp.ax_.set_ylim((X_plot["hue"].min(), X_plot["hue"].max()))
return disp.ax_
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(12, 6))
fit_and_plot_model(X_plot, y, clf, ax1)
ax1.set_title("KNN without scaling")
fit_and_plot_model(X_plot_scaled, y, clf, ax2)
ax2.set_xlabel("scaled proline")
ax2.set_ylabel("scaled hue")
_ = ax2.set_title("KNN with scaling")

Здесь граница решения показывает, что подгонка масштабированных или немасштабированных данных приводит
к совершенно разным моделям. Причина в том, что переменная "proline" имеет
значения, которые варьируются от 0 до 1000, тогда как переменная "hue" варьируется
от 1 до 10. Из-за этого расстояния между образцами в основном
зависят от различий в значениях "proline", тогда как значения "hue" будут
сравнительно проигнорированы. Если использовать
StandardScaler для нормализации этой базы данных,
оба масштабированных значения лежат примерно между -3 и 3, и структура соседей
будет затронута более или менее одинаково обеими переменными.
Влияние масштабирования на уменьшение размерности PCA#
Уменьшение размерности с использованием PCA заключается в нахождении признаков, которые максимизируют дисперсию. Если один признак варьируется больше, чем другие, только из-за их соответствующих масштабов,
PCA определит, что такой признак
доминирует в направлении главных компонент.
Мы можем исследовать первые главные компоненты, используя все исходные признаки:
import pandas as pd
from sklearn.decomposition import PCA
pca = PCA(n_components=2).fit(X_train)
scaled_pca = PCA(n_components=2).fit(scaled_X_train)
X_train_transformed = pca.transform(X_train)
X_train_std_transformed = scaled_pca.transform(scaled_X_train)
first_pca_component = pd.DataFrame(
pca.components_[0], index=X.columns, columns=["without scaling"]
)
first_pca_component["with scaling"] = scaled_pca.components_[0]
first_pca_component.plot.bar(
title="Weights of the first principal component", figsize=(6, 8)
)
_ = plt.tight_layout()

Действительно, мы обнаруживаем, что признак "proline" доминирует в направлении первой главной компоненты без масштабирования, будучи примерно на два порядка величины выше других признаков. Это контрастирует с наблюдением первой главной компоненты для масштабированной версии данных, где порядки величины примерно одинаковы для всех признаков.
Мы можем визуализировать распределение главных компонент в обоих случаях:
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(10, 5))
target_classes = range(0, 3)
colors = ("blue", "red", "green")
markers = ("^", "s", "o")
for target_class, color, marker in zip(target_classes, colors, markers):
ax1.scatter(
x=X_train_transformed[y_train == target_class, 0],
y=X_train_transformed[y_train == target_class, 1],
color=color,
label=f"class {target_class}",
alpha=0.5,
marker=marker,
)
ax2.scatter(
x=X_train_std_transformed[y_train == target_class, 0],
y=X_train_std_transformed[y_train == target_class, 1],
color=color,
label=f"class {target_class}",
alpha=0.5,
marker=marker,
)
ax1.set_title("Unscaled training dataset after PCA")
ax2.set_title("Standardized training dataset after PCA")
for ax in (ax1, ax2):
ax.set_xlabel("1st principal component")
ax.set_ylabel("2nd principal component")
ax.legend(loc="upper right")
ax.grid()
_ = plt.tight_layout()

Из графика выше мы наблюдаем, что масштабирование признаков перед уменьшением размерности приводит к компонентам с одинаковым порядком величины. В этом случае это также улучшает разделимость классов. Действительно, в следующем разделе мы подтверждаем, что лучшая разделимость положительно сказывается на общей производительности модели.
Влияние масштабирования на производительность модели#
Сначала мы покажем, как оптимальная регуляризация
LogisticRegressionCV зависит от масштабирования или
не масштабирования данных:
import numpy as np
from sklearn.linear_model import LogisticRegressionCV
from sklearn.pipeline import make_pipeline
Cs = np.logspace(-5, 5, 20)
unscaled_clf = make_pipeline(
pca, LogisticRegressionCV(Cs=Cs, use_legacy_attributes=False, l1_ratios=(0,))
)
unscaled_clf.fit(X_train, y_train)
scaled_clf = make_pipeline(
scaler,
pca,
LogisticRegressionCV(Cs=Cs, use_legacy_attributes=False, l1_ratios=(0,)),
)
scaled_clf.fit(X_train, y_train)
print(f"Optimal C for the unscaled PCA: {unscaled_clf[-1].C_:.4f}\n")
print(f"Optimal C for the standardized data with PCA: {scaled_clf[-1].C_:.2f}")
Optimal C for the unscaled PCA: 0.0004
Optimal C for the standardized data with PCA: 20.69
Необходимость регуляризации выше (меньшие значения C) для данных, которые
не масштабировались перед применением PCA. Теперь мы оцениваем влияние масштабирования на
точность и среднюю логарифмическую потерю оптимальных моделей:
from sklearn.metrics import accuracy_score, log_loss
y_pred = unscaled_clf.predict(X_test)
y_pred_scaled = scaled_clf.predict(X_test)
y_proba = unscaled_clf.predict_proba(X_test)
y_proba_scaled = scaled_clf.predict_proba(X_test)
print("Test accuracy for the unscaled PCA")
print(f"{accuracy_score(y_test, y_pred):.2%}\n")
print("Test accuracy for the standardized data with PCA")
print(f"{accuracy_score(y_test, y_pred_scaled):.2%}\n")
print("Log-loss for the unscaled PCA")
print(f"{log_loss(y_test, y_proba):.3}\n")
print("Log-loss for the standardized data with PCA")
print(f"{log_loss(y_test, y_proba_scaled):.3}")
Test accuracy for the unscaled PCA
35.19%
Test accuracy for the standardized data with PCA
96.30%
Log-loss for the unscaled PCA
0.957
Log-loss for the standardized data with PCA
0.0825
Наблюдается явная разница в точности предсказаний, когда данные
масштабируются перед PCA, так как он значительно превосходит не масштабированную версию. Это соответствует интуиции, полученной из графика в предыдущем разделе, где компоненты становятся линейно разделимыми при масштабировании перед использованием PCA.
Обратите внимание, что в этом случае модели с масштабированными признаками работают лучше, чем модели с не масштабированными признаками, потому что все переменные ожидаются как предсказательные, и мы скорее избегаем, чтобы некоторые из них были сравнительно проигнорированы.
Если переменные в меньших масштабах не были предсказательными, можно наблюдать снижение производительности после масштабирования признаков: шумные признаки будут вносить больший вклад в предсказание после масштабирования, и поэтому масштабирование увеличит переобучение.
Наконец, мы наблюдаем, что достигается меньшая логарифмическая ошибка с помощью шага масштабирования.
Общее время выполнения скрипта: (0 минут 0.934 секунды)
Связанные примеры