简介
在本实验中,我们将学习如何使用 scikit-learn 库中的 TransformedTargetRegressor。我们将把它应用于两个不同的数据集,以观察在训练线性回归模型之前对目标值进行转换的好处。我们将使用合成数据和艾姆斯房屋数据集来说明转换目标值的影响。
虚拟机提示
虚拟机启动完成后,点击左上角切换到“笔记本”标签,以访问 Jupyter Notebook 进行练习。
有时,你可能需要等待几秒钟让 Jupyter Notebook 完成加载。由于 Jupyter Notebook 的限制,操作验证无法自动化。
如果你在学习过程中遇到问题,随时向 Labby 提问。课程结束后提供反馈,我们会及时为你解决问题。
导入必要的库并加载合成数据
我们首先导入必要的库并加载合成数据。我们生成一个合成随机回归数据集,并通过平移所有目标值来修改目标,使所有条目均为非负,然后应用指数函数以获得无法使用简单线性模型拟合的非线性目标。然后,我们在训练线性回归模型并将其用于预测之前,使用对数函数(np.log1p)和指数函数(np.expm1)来转换目标。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.compose import TransformedTargetRegressor
from sklearn.linear_model import RidgeCV
from sklearn.metrics import median_absolute_error, r2_score, PredictionErrorDisplay
## Generate synthetic data
X, y = make_regression(n_samples=10_000, noise=100, random_state=0)
## Modify the targets
y = np.expm1((y + abs(y.min())) / 200)
y_trans = np.log1p(y)
绘制目标分布
我们绘制应用对数函数前后目标的概率密度函数。
f, (ax0, ax1) = plt.subplots(1, 2)
ax0.hist(y, bins=100, density=True)
ax0.set_xlim([0, 2000])
ax0.set_ylabel("概率")
ax0.set_xlabel("目标")
ax0.set_title("目标分布")
ax1.hist(y_trans, bins=100, density=True)
ax1.set_ylabel("概率")
ax1.set_xlabel("目标")
ax1.set_title("转换后的目标分布")
f.suptitle("合成数据", y=1.05)
plt.tight_layout()
在原始目标上训练并评估线性回归模型
我们在原始目标上训练并评估一个线性回归模型。由于数据的非线性,训练得到的模型在预测时不会很精确。
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
ridge_cv = RidgeCV().fit(X_train, y_train)
y_pred_ridge = ridge_cv.predict(X_test)
score = {
"R2": f"{r2_score(y_test, y_pred_ridge):.3f}",
"MedAE": f"{median_absolute_error(y_test, y_pred_ridge):.3f}",
}
print("Linear Regression on original targets:")
for key, val in score.items():
print(f"{key}: {val}")
在转换后的目标上训练并评估线性回归模型
我们使用 TransformedTargetRegressor 在转换后的目标上训练并评估一个线性回归模型。对数函数使目标线性化,即使使用与中位数绝对误差(MedAE)报告的类似线性模型,也能实现更好的预测。
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)
score = {
"R2": f"{r2_score(y_test, y_pred_ridge_with_trans_target):.3f}",
"MedAE": f"{median_absolute_error(y_test, y_pred_ridge_with_trans_target):.3f}",
}
print("\nLinear Regression on transformed targets:")
for key, val in score.items():
print(f"{key}: {val}")
绘制两个模型的实际值与预测值对比图
我们绘制两个模型的实际值与预测值对比图,并在每个轴的图例中添加得分。
f, (ax0, ax1) = plt.subplots(1, 2, sharey=True)
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},
)
for ax, y_pred in zip([ax0, ax1], [y_pred_ridge, y_pred_ridge_with_trans_target]):
for name, score in score.items():
ax.plot([], [], " ", label=f"{name}={score}")
ax.legend(loc="upper left")
ax0.set_title("岭回归 \n 无目标转换")
ax1.set_title("岭回归 \n 有目标转换")
f.suptitle("合成数据", y=1.05)
plt.tight_layout()
加载并预处理艾姆斯房屋数据
我们加载艾姆斯房屋数据集,并通过仅保留数值列以及移除包含缺失值(NaN)或无穷大值(Inf)的列来对其进行预处理。要预测的目标是每栋房屋的售价。
from sklearn.datasets import fetch_openml
from sklearn.preprocessing import quantile_transform
ames = fetch_openml(name="house_prices", as_frame=True, parser="pandas")
## 仅保留数值列
X = ames.data.select_dtypes(np.number)
## 移除包含缺失值(NaN)或无穷大值(Inf)的列
X = X.drop(columns=["LotFrontage", "GarageYrBlt", "MasVnrArea"])
## 让价格以千美元为单位
y = ames.target / 1000
y_trans = quantile_transform(
y.to_frame(), n_quantiles=900, output_distribution="normal", copy=True
).squeeze()
绘制艾姆斯房屋数据的目标分布
我们绘制应用分位数变换器(QuantileTransformer)前后目标的概率密度函数。
f, (ax0, ax1) = plt.subplots(1, 2)
ax0.hist(y, bins=100, density=True)
ax0.set_ylabel("概率")
ax0.set_xlabel("目标")
ax0.set_title("目标分布")
ax1.hist(y_trans, bins=100, density=True)
ax1.set_ylabel("概率")
ax1.set_xlabel("目标")
ax1.set_title("变换后的目标分布")
f.suptitle("艾姆斯房屋数据:售价", y=1.05)
plt.tight_layout()
针对艾姆斯房屋数据的原始目标训练并评估线性回归模型
我们针对艾姆斯房屋数据的原始目标训练并评估一个线性回归模型。
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)
ridge_cv = RidgeCV().fit(X_train, y_train)
y_pred_ridge = ridge_cv.predict(X_test)
score = {
"R2": f"{r2_score(y_test, y_pred_ridge):.3f}",
"MedAE": f"{median_absolute_error(y_test, y_pred_ridge):.3f}",
}
print("\n针对原始目标的线性回归:")
for key, val in score.items():
print(f"{key}: {val}")
针对艾姆斯房屋数据的变换后目标训练并评估线性回归模型
我们使用变换后目标,通过变换目标回归器(TransformedTargetRegressor)针对艾姆斯房屋数据训练并评估一个线性回归模型。
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)
score = {
"R2": f"{r2_score(y_test, y_pred_ridge_with_trans_target):.3f}",
"MedAE": f"{median_absolute_error(y_test, y_pred_ridge_with_trans_target):.3f}",
}
print("\n针对变换后目标的线性回归:")
for key, val in score.items():
print(f"{key}: {val}")
绘制两个模型的实际值与预测值以及残差与预测值的对比图
我们绘制两个模型的实际值与预测值以及残差与预测值的对比图,并在每个轴的图例中添加得分。
f, (ax0, ax1) = plt.subplots(2, 2, sharey="row", figsize=(6.5, 8))
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},
)
for ax, y_pred in zip([ax0[0], ax0[1]], [y_pred_ridge, y_pred_ridge_with_trans_target]):
for name, score in score.items():
ax.plot([], [], " ", label=f"{name}={score}")
ax.legend(loc="upper left")
ax0[0].set_title("岭回归 \n 无目标变换")
ax0[1].set_title("岭回归 \n 有目标变换")
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("岭回归 \n 无目标变换")
ax1[1].set_title("岭回归 \n 有目标变换")
f.suptitle("艾姆斯房屋数据:售价", y=1.05)
plt.tight_layout()
plt.show()
总结
在本实验中,我们学习了如何使用 scikit-learn 库中的变换目标回归器(TransformedTargetRegressor)。我们将其应用于两个不同的数据集,以观察在训练线性回归模型之前变换目标值的好处。我们使用合成数据和艾姆斯房屋数据集来说明变换目标值的影响。我们观察到对数函数使目标线性化,即使使用与中位数绝对误差(MedAE)报告的类似线性模型,也能实现更好的预测。我们还观察到,对于艾姆斯房屋数据集,变换器的效果较弱,但仍导致 R2 增加和 MedAE 大幅下降。