Python案例如何用Scikit-learn做多输出回归

wen python案例 1

Python案例:如何用Scikit-learn做多输出回归——从原理到实战

📖 目录导读

  • 什么是多输出回归?它与单输出回归有何不同?

    Python案例如何用Scikit-learn做多输出回归

  • Scikit-learn中有哪些支持多输出回归的算法?

  • 案例实战:构建一个房价与租金联合预测模型

  • 模型评估与调优技巧

  • 常见问答:多输出回归的陷阱与解决方案


什么是多输出回归?它与单输出回归有何不同?

多输出回归(Multi-Output Regression) 是指一个模型需要同时预测多个连续型目标变量的任务,根据气象数据同时预测明天的温度、湿度和风速;或者根据房屋特征同时预测售价和月租金。

与单输出回归的核心区别

  • 单输出回归:每个模型只预测一个目标变量(如房价)。
  • 多输出回归:一个模型输出多个目标变量,且这些变量之间通常存在相关性(如房价与租金呈正相关)。

Scikit-learn(简称sklearn)提供了两种实现方式:

  1. 问题转换法:将多输出问题拆解成多个单输出问题(如MultiOutputRegressor包装器)。
  2. 原生多输出算法:直接支持多输出的模型(如RandomForestRegressorMLPRegressor)。

Scikit-learn中有哪些支持多输出回归的算法?

下表列出了sklearn中常用的多输出回归方法及特点:

算法类 是否原生支持多输出 适用场景
MultiOutputRegressor 包装器(可将任意单输出回归器转为多输出) 线性模型、SVR等不支持原生多输出的算法
RandomForestRegressor ✅ 原生支持 非结构化数据、非线性关系、特征维度高
GradientBoostingRegressor ✅ 原生支持 需要高精度、数据量较大时
MLPRegressor(神经网络) ✅ 原生支持 复杂非线性、数据量充足
Ridge, Lasso ❌ 需包装 线性关系明确、需要正则化
SVR ❌ 需包装 小样本、非线性核函数

关键选择原则:如果目标变量之间存在强相关性,原生多输出模型通常比包装器效果好,因为它能学习到输出间的联合分布。


案例实战:构建一个房价与租金联合预测模型

1 问题定义

我们使用经典的加州房价数据集(加利福尼亚州房产数据模拟),但将任务扩展为同时预测:

  • MedHouseVal:房屋中位数价格(单位:十万美元)
  • RentEstimate:模拟的月租金(单位:千美元)

其中RentEstimate由房价与卧室数线性变换生成(添加噪声),以模拟真实中的相关性。

2 数据准备与预处理

import numpy as np
import pandas as pd
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 加载数据
housing = fetch_california_housing()
X = pd.DataFrame(housing.data, columns=housing.feature_names)
y = pd.DataFrame({})
y['MedHouseVal'] = housing.target
# 生成第二个目标变量(模拟租金)
# 租金 = 0.8 * 房价 + 0.2 * 卧室数 + 噪声
np.random.seed(42)
y['RentEstimate'] = 0.8 * y['MedHouseVal'] + 0.2 * X['AveBedrms'] + np.random.normal(0, 0.1, len(X))
# 切分训练集与测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 特征标准化(SVR、神经网络等需要)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

3 构建多输出回归模型

我们对比三种方法:随机森林(原生)MultiOutputRegressor + SVR梯度提升(原生)

from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.multioutput import MultiOutputRegressor
from sklearn.svm import SVR
from sklearn.metrics import mean_absolute_error, mean_squared_error
# 模型1:随机森林(原生多输出)
rf = RandomForestRegressor(n_estimators=100, random_state=42)
rf.fit(X_train, y_train)
# 模型2:MultiOutputRegressor + SVR(包装器)
svr_wrapper = MultiOutputRegressor(SVR(kernel='rbf', C=100, gamma='scale'))
svr_wrapper.fit(X_train_scaled, y_train)
# 模型3:梯度提升(原生多输出)
gbr = GradientBoostingRegressor(n_estimators=100, learning_rate=0.1, random_state=42)
gbr.fit(X_train, y_train)

4 预测与结果展示

models = {
    'RandomForest': rf,
    'SVR+Wrapper': svr_wrapper,
    'GradientBoosting': gbr
}
for name, model in models.items():
    if name == 'SVR+Wrapper':
        y_pred = model.predict(X_test_scaled)
    else:
        y_pred = model.predict(X_test)
    print(f"=== {name} ===")
    for i, col in enumerate(y_test.columns):
        mae = mean_absolute_error(y_test.iloc[:, i], y_pred[:, i])
        rmse = np.sqrt(mean_squared_error(y_test.iloc[:, i], y_pred[:, i]))
        print(f"  目标变量 {col}: MAE={mae:.4f}, RMSE={rmse:.4f}")
    print()

示例输出(实际数值因随机种子而异):

=== RandomForest ===
  目标变量 MedHouseVal: MAE=0.3312, RMSE=0.5134
  目标变量 RentEstimate: MAE=0.2951, RMSE=0.4397
=== SVR+Wrapper ===
  目标变量 MedHouseVal: MAE=0.4871, RMSE=0.7210
  目标变量 RentEstimate: MAE=0.4233, RMSE=0.6982
=== GradientBoosting ===
  目标变量 MedHouseVal: MAE=0.3478, RMSE=0.5401
  目标变量 RentEstimate: MAE=0.3105, RMSE=0.4802

解读:随机森林在此场景下表现最佳,因为其能捕捉非线性和变量间的交互作用,SVR+Wrapper由于使用RBF核且数据量大,训练时间较长但精度不占优。


模型评估与调优技巧

1 评估指标选择

  • 平均绝对误差(MAE):解释性强,适合业务汇报。
  • 均方根误差(RMSE):对大误差惩罚更重,适合优化目标。
  • R²分数:对于多输出,可以使用平均R²或按各自变量单独计算。

2 超参数调优

对于多输出任务,推荐使用GridSearchCVRandomizedSearchCV,但需注意:某些原生多输出模型(如随机森林)的调优方式与单输出相同;而包装器需要调整基估计器的参数。

# 示例:随机森林的网格搜索(注意参数搜索范围要根据数据规模调整)
from sklearn.model_selection import GridSearchCV
param_grid = {
    'n_estimators': [50, 100, 200],
    'max_depth': [10, 20, None],
    'min_samples_split': [2, 5]
}
grid_rf = GridSearchCV(RandomForestRegressor(random_state=42), 
                       param_grid, cv=3, 
                       scoring='neg_mean_squared_error')
grid_rf.fit(X_train, y_train)
print("Best params:", grid_rf.best_params_)

3 集成与堆叠

可以尝试使用StackingRegressor将多个多输出模型进行堆叠,进一步提升预测稳定性。


常见问答:多输出回归的陷阱与解决方案

❓ Q1:为什么我的多输出回归模型预测效果很差?

  • 可能原因:目标变量量纲差异大(如房价范围0-5,租金范围0-50k)。解决方案:对多个输出进行标准化,使量纲一致。
  • 可能原因:特征对每个输出的重要性差异巨大。解决方案:尝试使用MultiOutputRegressor包装器,它在内部为每个输出独立训练一个模型(相当于特征选择更灵活)。

❓ Q2:什么时候应该使用原生多输出算法,什么时候用包装器?

  • 使用原生:当目标变量间存在强相关性、且数据量足够时(如气象多变量预测)。
  • 使用包装器:当每个目标变量对特征的要求差异较大,或基础模型不支持多输出时(如线性回归、K近邻)。

❓ Q3:如何验证多输出模型的预测结果是否是“协同的”?

  • 可以计算预测残差的相关矩阵,如果某个输出的残差与另一个输出的残差高度相关,说明模型未能捕捉到输出间的联合分布,此时ChainRegressor(链式回归)可能更适合——它允许前一个输出的预测作为后一个输出的特征。

❓ Q4:为什么我使用MultiOutputRegressor时训练很慢?

  • 因为包装器实际上为每个输出单独训练一个模型,如果输出变量数量大(如10个以上),应优先选用原生多输出算法(随机森林、梯度提升)。

🔗 推荐资源

  • Scikit-learn官方文档:多输出回归指南(访问时可修改域名规则)
  • 进阶阅读:《Python机器学习》第4章——多标签与多输出问题

通过上述案例和原理,你可以将单输出回归任务轻松扩展到多输出场景,关键在于理解你的数据中输出变量间的依赖关系,并选择合适的算法结构,如果遇到具体问题,欢迎在评论区交流。

抱歉,评论功能暂时关闭!