Важность масштабирования признаков#

Масштабирование признаков через стандартизацию, также называемую 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")
KNN without scaling, 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()
Weights of the first principal component

Действительно, мы обнаруживаем, что признак "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()
Unscaled training dataset after PCA, Standardized training dataset after PCA

Из графика выше мы наблюдаем, что масштабирование признаков перед уменьшением размерности приводит к компонентам с одинаковым порядком величины. В этом случае это также улучшает разделимость классов. Действительно, в следующем разделе мы подтверждаем, что лучшая разделимость положительно сказывается на общей производительности модели.

Влияние масштабирования на производительность модели#

Сначала мы покажем, как оптимальная регуляризация 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 секунды)

Связанные примеры

Масштабирование параметра регуляризации для SVC

Масштабирование параметра регуляризации для SVC

Сравнение линейных байесовских регрессоров

Сравнение линейных байесовских регрессоров

Ядерный PCA

Ядерный PCA

Классификация методом ближайших соседей

Классификация методом ближайших соседей

Галерея, созданная Sphinx-Gallery