사이킷런을 이용한 분위수 회귀

Beginner

This tutorial is from open-source community. Access the source code

소개

이 튜토리얼에서는 scikit-learn 을 사용하여 분위수 회귀를 수행하는 방법을 보여줍니다. 두 개의 합성 데이터 세트를 생성하여 분위수 회귀가 어떻게 중요하지 않은 조건부 분위수를 예측할 수 있는지 보여줄 것입니다. QuantileRegressor 클래스를 사용하여 중앙값과 5% 및 95% 로 고정된 낮은 분위수 및 높은 분위수를 추정할 것입니다. QuantileRegressorLinearRegression과 비교하고 평균 절대 오차 (MAE) 및 평균 제곱 오차 (MSE) 를 사용하여 성능을 평가할 것입니다.

VM 팁

VM 시작이 완료되면 왼쪽 상단 모서리를 클릭하여 Notebook 탭으로 전환하여 연습을 위한 Jupyter Notebook에 접근할 수 있습니다.

때때로 Jupyter Notebook 이 완전히 로드되기까지 몇 초 정도 기다려야 할 수 있습니다. Jupyter Notebook 의 제한으로 인해 작업의 유효성 검사를 자동화할 수 없습니다.

학습 중 문제가 발생하면 Labby 에게 문의하십시오. 세션 후 피드백을 제공하면 문제를 신속하게 해결해 드리겠습니다.

데이터셋 생성

단일 특징 x와 선형 관계를 사용하여 동일한 기대값을 갖는 두 개의 합성 데이터셋을 생성할 것입니다. 이 데이터셋에 이종분산 정규 노이즈와 비대칭 파레토 노이즈를 추가할 것입니다.

import numpy as np

rng = np.random.RandomState(42)
x = np.linspace(start=0, stop=10, num=100)
X = x[:, np.newaxis]
y_true_mean = 10 + 0.5 * x

## 이종분산 정규 노이즈
y_normal = y_true_mean + rng.normal(loc=0, scale=0.5 + 0.5 * x, size=x.shape[0])

## 비대칭 파레토 노이즈
a = 5
y_pareto = y_true_mean + 10 * (rng.pareto(a, size=x.shape[0]) - 1 / (a - 1))

데이터셋 시각화

데이터셋과 잔차 y - mean(y)의 분포를 시각화할 것입니다.

import matplotlib.pyplot as plt

_, axs = plt.subplots(nrows=2, ncols=2, figsize=(15, 11), sharex="row", sharey="row")

axs[0, 0].plot(x, y_true_mean, label="True mean")
axs[0, 0].scatter(x, y_normal, color="black", alpha=0.5, label="Observations")
axs[1, 0].hist(y_true_mean - y_normal, edgecolor="black")

axs[0, 1].plot(x, y_true_mean, label="True mean")
axs[0, 1].scatter(x, y_pareto, color="black", alpha=0.5, label="Observations")
axs[1, 1].hist(y_true_mean - y_pareto, edgecolor="black")

axs[0, 0].set_title("이종분산 정규 분포를 갖는 대상 데이터셋")
axs[0, 1].set_title("비대칭 파레토 분포를 갖는 대상 데이터셋")
axs[1, 0].set_title(
    "이종분산 정규 분포를 갖는 대상 데이터셋의 잔차 분포"
)
axs[1, 1].set_title("비대칭 파레토 분포를 갖는 대상 데이터셋의 잔차 분포")
axs[0, 0].legend()
axs[0, 1].legend()
axs[0, 0].set_ylabel("y")
axs[1, 0].set_ylabel("빈도")
axs[0, 1].set_xlabel("x")
axs[0, 0].set_xlabel("x")
axs[1, 0].set_xlabel("잔차")
_ = axs[1, 1].set_xlabel("잔차")

분위수 회귀

QuantileRegressor 클래스를 사용하여 중앙값과 5% 및 95% 로 고정된 하위 및 상위 분위수를 추정할 것입니다. 5% 와 95% 의 분위수를 사용하여 중앙 90% 구간을 벗어나는 학습 샘플의 이상치를 찾을 것입니다.

from sklearn.linear_model import QuantileRegressor

## 이 줄은 이전 SciPy 버전과의 호환성 문제를 방지하기 위한 것입니다.
## 최신 SciPy 버전에서는 `solver="highs"` 를 사용해야 합니다.
solver = "highs" if sp_version >= parse_version("1.6.0") else "interior-point"

quantiles = [0.05, 0.5, 0.95]
predictions = {}
out_bounds_predictions = np.zeros_like(y_true_mean, dtype=np.bool_)
for quantile in quantiles:
    qr = QuantileRegressor(quantile=quantile, alpha=0, solver=solver)
    y_pred = qr.fit(X, y_normal).predict(X)
    predictions[quantile] = y_pred

    if quantile == min(quantiles):
        out_bounds_predictions = np.logical_or(
            out_bounds_predictions, y_pred >= y_normal
        )
    elif quantile == max(quantiles):
        out_bounds_predictions = np.logical_or(
            out_bounds_predictions, y_pred <= y_normal
        )

plt.plot(X, y_true_mean, color="black", linestyle="dashed", label="True mean")

for quantile, y_pred in predictions.items():
    plt.plot(X, y_pred, label=f"Quantile: {quantile}")

plt.scatter(
    x[out_bounds_predictions],
    y_normal[out_bounds_predictions],
    color="black",
    marker="+",
    alpha=0.5,
    label="Outside interval",
)
plt.scatter(
    x[~out_bounds_predictions],
    y_normal[~out_bounds_predictions],
    color="black",
    alpha=0.5,
    label="Inside interval",
)

plt.legend()
plt.xlabel("x")
plt.ylabel("y")
_ = plt.title("이종분산 정규 분포 대상의 분위수")

quantiles = [0.05, 0.5, 0.95]
predictions = {}
out_bounds_predictions = np.zeros_like(y_true_mean, dtype=np.bool_)
for quantile in quantiles:
    qr = QuantileRegressor(quantile=quantile, alpha=0, solver=solver)
    y_pred = qr.fit(X, y_pareto).predict(X)
    predictions[quantile] = y_pred

    if quantile == min(quantiles):
        out_bounds_predictions = np.logical_or(
            out_bounds_predictions, y_pred >= y_pareto
        )
    elif quantile == max(quantiles):
        out_bounds_predictions = np.logical_or(
            out_bounds_predictions, y_pred <= y_pareto
        )

plt.plot(X, y_true_mean, color="black", linestyle="dashed", label="True mean")

for quantile, y_pred in predictions.items():
    plt.plot(X, y_pred, label=f"Quantile: {quantile}")

plt.scatter(
    x[out_bounds_predictions],
    y_pareto[out_bounds_predictions],
    color="black",
    marker="+",
    alpha=0.5,
    label="Outside interval",
)
plt.scatter(
    x[~out_bounds_predictions],
    y_pareto[~out_bounds_predictions],
    color="black",
    alpha=0.5,
    label="Inside interval",
)

plt.legend()
plt.xlabel("x")
plt.ylabel("y")
_ = plt.title("비대칭 파레토 분포 대상의 분위수")

QuantileRegressorLinearRegression 비교

QuantileRegressorLinearRegression을 비교하고 평균 절대 오차 (MAE) 와 평균 제곱 오차 (MSE) 를 사용하여 성능을 평가할 것입니다.

from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import cross_validate

linear_regression = LinearRegression()
quantile_regression = QuantileRegressor(quantile=0.5, alpha=0, solver=solver)

y_pred_lr = linear_regression.fit(X, y_pareto).predict(X)
y_pred_qr = quantile_regression.fit(X, y_pareto).predict(X)

print(f"""훈련 오차 (내부 샘플 성능)
    {linear_regression.__class__.__name__}:
    MAE = {mean_absolute_error(y_pareto, y_pred_lr):.3f}
    MSE = {mean_squared_error(y_pareto, y_pred_lr):.3f}
    {quantile_regression.__class__.__name__}:
    MAE = {mean_absolute_error(y_pareto, y_pred_qr):.3f}
    MSE = {mean_squared_error(y_pareto, y_pred_qr):.3f}
    """)

cv_results_lr = cross_validate(
    linear_regression,
    X,
    y_pareto,
    cv=3,
    scoring=["neg_mean_absolute_error", "neg_mean_squared_error"],
)
cv_results_qr = cross_validate(
    quantile_regression,
    X,
    y_pareto,
    cv=3,
    scoring=["neg_mean_absolute_error", "neg_mean_squared_error"],
)
print(f"""테스트 오차 (교차 검증 성능)
    {linear_regression.__class__.__name__}:
    MAE = {-cv_results_lr["test_neg_mean_absolute_error"].mean():.3f}
    MSE = {-cv_results_lr["test_neg_mean_squared_error"].mean():.3f}
    {quantile_regression.__class__.__name__}:
    MAE = {-cv_results_qr["test_neg_mean_absolute_error"].mean():.3f}
    MSE = {-cv_results_qr["test_neg_mean_squared_error"].mean():.3f}
    """)

요약

이 튜토리얼에서는 scikit-learn 을 사용하여 분위수 회귀를 수행하는 방법을 배웠습니다. 두 개의 합성 데이터 세트를 생성하여 분위수 회귀가 어떻게 중요하지 않은 조건부 분위수를 예측할 수 있는지 보여주었습니다. QuantileRegressor 클래스를 사용하여 중앙값과 5% 및 95% 로 고정된 하위 및 상위 분위수를 각각 추정했습니다. QuantileRegressorLinearRegression과 비교하고 평균 절대 오차 (MAE) 와 평균 제곱 오차 (MSE) 를 사용하여 성능을 평가했습니다.