Spec-Zone.ru › scikit-learn

Примечание

Перейти в конец, чтобы скачать полный код примера или запустить этот пример в браузере с помощью JupyterLite или Binder.

Влияние преобразования целевых значений в модели регрессии

В этом примере мы рассмотрим TransformedTargetRegressor. На двух примерах мы покажем преимущества преобразования целевых значений перед обучением модели линейной регрессии. В первом примере используются синтетические данные, а во втором — набор данных о жилье в Эймсе.

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

Синтетический пример

Генерируется синтетический случайный набор данных для регрессии. Целевые значения y изменяются следующим образом:

  1. все целевые значения сдвигаются так, чтобы они были неотрицательными (к ним прибавляется абсолютное значение наименьшего y);
  2. применяется экспоненциальная функция, чтобы получить нелинейные целевые значения, которые невозможно аппроксимировать простой линейной моделью.

Поэтому перед обучением модели линейной регрессии и использованием её для прогнозирования целевые значения будут преобразованы с помощью логарифмической функции (np.log1p) и экспоненциальной функции (np.expm1).

import numpy as np

from sklearn.datasets import make_regression

X, y = make_regression(n_samples=10_000, noise=100, random_state=0)
y = np.expm1((y + abs(y.min())) / 200)
y_trans = np.log1p(y)

Ниже показаны функции плотности вероятности целевых значений до и после применения логарифмических функций.

import matplotlib.pyplot as plt

from sklearn.model_selection import train_test_split

f, (ax0, ax1) = plt.subplots(1, 2)

ax0.hist(y, bins=100, density=True)
ax0.set_xlim([0, 2000])
ax0.set_ylabel("Probability")
ax0.set_xlabel("Target")
ax0.set_title("Target distribution")

ax1.hist(y_trans, bins=100, density=True)
ax1.set_ylabel("Probability")
ax1.set_xlabel("Target")
ax1.set_title("Transformed target distribution")

f.suptitle("Synthetic data", y=1.05)
plt.tight_layout()

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
Synthetic data, Target distribution, Transformed target distribution

Сначала к исходным целевым значениям применяется линейная модель. Из-за нелинейности обученная модель не будет давать точные прогнозы. Затем для линеаризации целевых значений используется логарифмическая функция, что позволяет повысить точность прогнозирования даже при использовании аналогичной линейной модели, о чём свидетельствует медианная абсолютная ошибка (MedAE).

from sklearn.metrics import median_absolute_error, r2_score


def compute_score(y_true, y_pred):
    return {
        "R2": f"{r2_score(y_true, y_pred):.3f}",
        "MedAE": f"{median_absolute_error(y_true, y_pred):.3f}",
    }
from sklearn.compose import TransformedTargetRegressor
from sklearn.linear_model import RidgeCV
from sklearn.metrics import PredictionErrorDisplay

f, (ax0, ax1) = plt.subplots(1, 2, sharey=True)

ridge_cv = RidgeCV().fit(X_train, y_train)
y_pred_ridge = ridge_cv.predict(X_test)

ridge_cv_with_trans_target = TransformedTargetRegressor(
    regressor=RidgeCV(), func=np.log1p, inverse_func=np.expm1
).fit(X_train, y_train)
y_pred_ridge_with_trans_target = ridge_cv_with_trans_target.predict(X_test)

PredictionErrorDisplay.from_predictions(
    y_test,
    y_pred_ridge,
    kind="actual_vs_predicted",
    ax=ax0,
    scatter_kwargs={"alpha": 0.5},
)
PredictionErrorDisplay.from_predictions(
    y_test,
    y_pred_ridge_with_trans_target,
    kind="actual_vs_predicted",
    ax=ax1,
    scatter_kwargs={"alpha": 0.5},
)

# Add the score in the legend of each axis
for ax, y_pred in zip([ax0, ax1], [y_pred_ridge, y_pred_ridge_with_trans_target]):
    for name, score in compute_score(y_test, y_pred).items():
        ax.plot([], [], " ", label=f"{name}={score}")
    ax.legend(loc="upper left")

ax0.set_title("Ridge regression \n without target transformation")
ax1.set_title("Ridge regression \n with target transformation")
f.suptitle("Synthetic data", y=1.05)
plt.tight_layout()
Synthetic data, Ridge regression   without target transformation, Ridge regression   with target transformation

Набор данных из реального мира

Аналогичным образом набор данных о жилье в Эймсе используется для демонстрации влияния преобразования целевых значений перед обучением модели. В этом примере необходимо спрогнозировать цену продажи каждого дома.

from sklearn.datasets import fetch_openml
from sklearn.preprocessing import quantile_transform

ames = fetch_openml(name="house_prices", as_frame=True)
# Keep only numeric columns
X = ames.data.select_dtypes(np.number)
# Remove columns with NaN or Inf values
X = X.drop(columns=["LotFrontage", "GarageYrBlt", "MasVnrArea"])
# Let the price be in k$
y = ames.target / 1000
y_trans = quantile_transform(
    y.to_frame(), n_quantiles=900, output_distribution="normal", copy=True
).squeeze()

Для нормализации распределения целевых значений перед применением модели RidgeCV используется QuantileTransformer.

f, (ax0, ax1) = plt.subplots(1, 2)

ax0.hist(y, bins=100, density=True)
ax0.set_ylabel("Probability")
ax0.set_xlabel("Target")
ax0.set_title("Target distribution")

ax1.hist(y_trans, bins=100, density=True)
ax1.set_ylabel("Probability")
ax1.set_xlabel("Target")
ax1.set_title("Transformed target distribution")

f.suptitle("Ames housing data: selling price", y=1.05)
plt.tight_layout()
Ames housing data: selling price, Target distribution, Transformed target distribution
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)

Влияние преобразования слабее, чем для синтетических данных. Однако преобразование приводит к увеличению \(R^2\) и значительному снижению MedAE. График остатков (прогнозируемое целевое значение − истинное целевое значение в зависимости от прогнозируемого целевого значения) без преобразования целевых значений имеет изогнутую форму «перевёрнутой улыбки» из-за того, что значения остатков меняются в зависимости от прогнозируемого целевого значения. После преобразования целевых значений график становится более линейным, что указывает на лучшее соответствие модели.

from sklearn.preprocessing import QuantileTransformer

f, (ax0, ax1) = plt.subplots(2, 2, sharey="row", figsize=(6.5, 8))

ridge_cv = RidgeCV().fit(X_train, y_train)
y_pred_ridge = ridge_cv.predict(X_test)

ridge_cv_with_trans_target = TransformedTargetRegressor(
    regressor=RidgeCV(),
    transformer=QuantileTransformer(n_quantiles=900, output_distribution="normal"),
).fit(X_train, y_train)
y_pred_ridge_with_trans_target = ridge_cv_with_trans_target.predict(X_test)

# plot the actual vs predicted values
PredictionErrorDisplay.from_predictions(
    y_test,
    y_pred_ridge,
    kind="actual_vs_predicted",
    ax=ax0[0],
    scatter_kwargs={"alpha": 0.5},
)
PredictionErrorDisplay.from_predictions(
    y_test,
    y_pred_ridge_with_trans_target,
    kind="actual_vs_predicted",
    ax=ax0[1],
    scatter_kwargs={"alpha": 0.5},
)

# Add the score in the legend of each axis
for ax, y_pred in zip([ax0[0], ax0[1]], [y_pred_ridge, y_pred_ridge_with_trans_target]):
    for name, score in compute_score(y_test, y_pred).items():
        ax.plot([], [], " ", label=f"{name}={score}")
    ax.legend(loc="upper left")

ax0[0].set_title("Ridge regression \n without target transformation")
ax0[1].set_title("Ridge regression \n with target transformation")

# plot the residuals vs the predicted values
PredictionErrorDisplay.from_predictions(
    y_test,
    y_pred_ridge,
    kind="residual_vs_predicted",
    ax=ax1[0],
    scatter_kwargs={"alpha": 0.5},
)
PredictionErrorDisplay.from_predictions(
    y_test,
    y_pred_ridge_with_trans_target,
    kind="residual_vs_predicted",
    ax=ax1[1],
    scatter_kwargs={"alpha": 0.5},
)
ax1[0].set_title("Ridge regression \n without target transformation")
ax1[1].set_title("Ridge regression \n with target transformation")

f.suptitle("Ames housing data: selling price", y=1.05)
plt.tight_layout()
plt.show()
Ames housing data: selling price, Ridge regression   without target transformation, Ridge regression   with target transformation, Ridge regression   without target transformation, Ridge regression   with target transformation

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

Launch binder
Launch JupyterLite

Download Jupyter notebook: plot_transformed_target.ipynb

Download Python source code: plot_transformed_target.py

Download zipped: plot_transformed_target.zip

Похожие примеры

Изотоническая регрессия

Построение конвейера: объединение PCA и логистической регрессии

Построение графика прогнозов с перекрёстной проверкой

Дискретизация непрерывных признаков с помощью KBinsDiscretizer

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

Spec-Zone.ru

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