Spec-Zone.ru › scikit-learn

Примечание

Перейти в конец, чтобы скачать полный код примера или запустить этот пример в браузере через 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, в этом разделе мы выберем два признака со значениями, различающимися по порядку величины.

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

import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

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,
    )
    cmap = ListedColormap(disp.multiclass_colors_)
    disp.ax_.scatter(
        X_plot["proline"], X_plot["hue"], c=y, cmap=cmap, 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 до 1 000, тогда как значения переменной «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,),  # TODO(1.10): remove because it is default now
        scoring="neg_log_loss",  # TODO(1.11): remove because it is default now
    ),
)
unscaled_clf.fit(X_train, y_train)

scaled_clf = make_pipeline(
    scaler,
    pca,
    LogisticRegressionCV(
        Cs=Cs,
        use_legacy_attributes=False,
        l1_ratios=(0,),  # TODO(1.10): remove because it is default now
        scoring="neg_log_loss",  # TODO(1.11): remove because it is default now,
    ),
)
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.0000

Optimal C for the standardized data with PCA: 6.16

Для данных, которые не масштабировались перед применением PCA, необходимость в регуляризации выше (то есть значения C ниже). Теперь оценим, как масштабирование влияет на точность и среднюю логарифмическую потерю оптимальных моделей:

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
1.18

Log-loss for the standardized data with PCA
0.0739

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

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

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

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

Общее время выполнения скрипта: (0 минут 1.622 секунды)

Launch binder
Launch JupyterLite

Download Jupyter notebook: plot_scaling_importance.ipynb

Download Python source code: plot_scaling_importance.py

Download zipped: plot_scaling_importance.zip

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

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

PCA с ядром

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

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

© 2007–2026 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.9/auto_examples/preprocessing/plot_scaling_importance.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API