
1. 项目概述当回归预测遇上多变量协同在数学建模和数据科学竞赛中我们经常遇到这样的场景一个系统的状态不是由单一指标决定的而是由多个输入变量共同作用同时我们关心的输出结果也不止一个。比如预测一个地区的空气质量输入可能是工厂排放量、汽车流量、风速、湿度等十几个指标而输出则需要同时预测PM2.5、PM10、臭氧浓度等多个污染物指标。这就是典型的多输入多输出Multi-Input Multi-Output, MIMO回归问题。传统的回归模型如线性回归或支持向量回归SVR通常被设计为处理单输出问题。面对多输出任务时一个朴素的做法是为每个输出单独训练一个模型。但这种方法忽略了输出变量之间可能存在的内在关联。例如PM2.5和PM10的浓度变化往往具有强相关性单独建模会丢失这部分信息导致模型整体预测精度下降且计算成本成倍增加。因此能够直接处理多输出问题的模型显得尤为重要。而XGBoost作为梯度提升决策树GBDT家族中的“明星算法”以其卓越的预测性能、高效的训练速度和对缺失值的鲁棒性在各类竞赛和工业实践中大放异彩。将XGBoost扩展应用于多输入多输出回归预测本质上就是利用其强大的特征组合与非线性拟合能力同时学习多个目标变量之间的复杂映射关系与内在关联。这个模型组合非常适合数学建模竞赛中那些涉及多指标预测的题目比如经济预测同时预测GDP、CPI、失业率、环境评估同时预测多种污染物浓度、工业生产同时预测产品的多个质量参数等。它不仅能提供一个强大的预测工具其模型本身如特征重要性也能为问题分析提供深刻的洞见。2. 核心思路从单输出到多输出的策略迁移实现多输入多输出的XGBoost回归预测核心在于策略选择。我们不能直接把多输出数据塞进标准的单输出XGBoost模型里需要一些技巧来“教会”模型同时处理多个目标。主要有三种主流策略各有优劣选择哪种取决于你的数据特性和任务需求。2.1 策略一多输出回归器MultiOutputRegressor这是最直接、最易于理解的策略。Scikit-learn库提供了MultiOutputRegressor这个元估计器meta-estimator。它的工作原理非常简单将多输出问题分解为多个独立的单输出问题。假设你有k个输出变量MultiOutputRegressor会在内部创建k个独立的XGBoost回归器每个回归器专门预测其中一个输出变量。实现方式from sklearn.multioutput import MultiOutputRegressor import xgboost as xgb # 假设 base_model 是一个配置好的单输出XGBoost回归器 base_model xgb.XGBRegressor(n_estimators100, max_depth5, learning_rate0.1) multi_model MultiOutputRegressor(base_model) # 训练和预测 multi_model.fit(X_train, y_train) # y_train 形状为 (n_samples, n_outputs) predictions multi_model.predict(X_test)优点简单直观概念清晰易于实现和调试。灵活性高可以为不同的输出变量定制不同的基模型参数虽然MultiOutputRegressor默认使用相同参数但可以通过迭代器实现差异化。并行训练各个模型之间相互独立可以方便地利用多核CPU进行并行训练加速整个过程。缺点忽略关联性这是最大的弊端。模型完全忽略了输出变量之间可能存在的相关性比如一个输出是另一个输出的函数或者它们受某些共同潜在因素影响。这可能导致模型无法学习到这种联合分布从而影响预测一致性。资源占用需要训练k个模型内存和存储开销较大尽管训练可以并行。适用场景当输出变量之间相互独立或关联性很弱且你对模型的可解释性和简易性有较高要求时可以选择此策略。2.2 策略二自定义多输出损失函数这是一种更“高级”且更“本质”的方法。XGBoost的核心在于它的目标函数损失函数正则化项。标准的回归任务通常使用均方误差MSE或平均绝对误差MAE作为损失函数。对于多输出任务我们可以自定义一个损失函数使其能够同时计算所有输出变量的误差。一个常见的做法是使用所有输出变量误差的加权和作为总损失。例如多输出MSE损失可以定义为Loss Σ_i Σ_j w_j * (y_true_ij - y_pred_ij)^2其中i遍历样本j遍历输出变量w_j是第j个输出变量的权重可以相等也可以根据业务重要性调整。实现思路 你需要使用XGBoost的自定义目标函数接口。这需要你提供损失函数的一阶梯度gradient和二阶梯度hessian。对于MSE梯度是残差的-2倍二阶梯度是常数2。对于多输出你需要将每个输出维度的梯度拼接起来。优点建模关联性通过一个统一的损失函数优化所有目标模型在训练过程中能够隐式地学习到输出变量之间的关联性因为参数更新是基于所有输出的总误差。单一模型最终只有一个模型结构紧凑。缺点实现复杂需要深厚的数学和编程功底来推导和实现梯度与海森矩阵尤其是对于非标准损失函数。调试困难自定义函数的错误可能难以排查且训练过程可能不稳定。灵活性受限所有输出共享相同的树结构可能无法很好地适应某些输出差异极大的情况。适用场景当你确信输出变量间存在强相关性并且你希望模型能捕获这种关系同时你具备相应的自定义函数实现能力时可以考虑此方法。在数学建模竞赛中如果追求模型的创新性和理论深度这是一个加分项。2.3 策略三数据转换法问题重构这种策略不改变模型而是改变数据。其核心思想是将多输出问题通过数据转换变成一个单输出或形式上的单输出问题。常用方法输出变量堆叠将k个输出变量视为“长”格式。假设原始数据有N个样本每个样本有k个输出。我们可以将数据转换为N*k个样本每个样本对应一个原始样本和一个特定的输出变量。同时我们需要添加一个新的特征列来标识这是哪个输出变量例如用0,1,...,k-1编码。这样多输出问题就变成了一个带有“输出类型”特征的单输出问题。构建复合目标如果输出变量量纲一致或可以标准化并且业务上允许可以考虑将多个输出通过某种方式如加权平均、主成分分析PCA后的第一主成分合并成一个综合指标然后预测这个单一指标。预测完成后如果需要再通过逆变换或模型分解回各个原始输出这通常很困难。优点复用现有工具可以直接使用任何强大的单输出模型包括XGBoost无需寻找其多输出版本。可能引入交互信息在堆叠方法中新增的“输出标识符”特征与原始特征的交互可能有助于模型学习不同输出模式下的差异化规律。缺点样本依赖性破坏堆叠法破坏了样本的独立性因为来自同一个原始样本的k个新样本是高度相关的这可能违反许多机器学习模型的基本假设需要谨慎处理如交叉验证时的数据划分。信息损失或扭曲构建复合目标的方法通常会损失原始输出变量的独立信息且逆变换往往不准确。效率问题堆叠法使数据量膨胀了k倍可能增加计算负担。适用场景当输出变量数量不多且其他策略实施困难时可以尝试堆叠法作为基线。构建复合目标的方法则适用于输出变量高度共线且最终决策真的只依赖于一个综合分数的场景。实操心得在数学建模竞赛的有限时间内策略一MultiOutputRegressor通常是性价比最高的起点。它实现快速效果稳定足以应对大多数赛题要求。如果时间充裕且想挑战更高分数可以尝试策略二自定义损失并将其结果与策略一进行对比作为模型创新的体现。策略三更多是一种思维拓展在实际中需谨慎使用。3. 实战构建基于MultiOutputRegressor的完整流程我们以策略一为例展示一个完整的、可复现的多输入多输出XGBoost回归预测建模流程。假设我们有一个数据集需要根据多个环境因素输入预测多种空气污染物浓度输出。3.1 环境准备与数据加载首先确保你的Python环境安装了必要的库xgboost,scikit-learn,pandas,numpy,matplotlib(用于可视化)。import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.multioutput import MultiOutputRegressor from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score import xgboost as xgb import matplotlib.pyplot as plt # 设置随机种子以保证可复现性 np.random.seed(42)接下来加载数据。这里我们模拟一个数据集实际应用中请替换为你的数据路径。# 模拟数据生成1000个样本10个输入特征3个输出目标 n_samples 1000 n_features 10 n_outputs 3 X np.random.randn(n_samples, n_features) * 10 5 # 输入特征 # 输出目标与输入存在非线性关系且输出间有相关性 y1 2 * X[:, 0] 0.5 * X[:, 1]**2 np.random.randn(n_samples) * 0.5 y2 1.5 * X[:, 0] 0.8 * X[:, 2] 0.3 * y1 np.random.randn(n_samples) * 0.3 # y2与y1相关 y3 -1 * X[:, 3] np.sin(X[:, 4]) np.random.randn(n_samples) * 0.7 y np.column_stack([y1, y2, y3]) # 转换为DataFrame方便查看可选 feature_names [fFeature_{i} for i in range(n_features)] target_names [fTarget_{i} for i in range(n_outputs)] df_X pd.DataFrame(X, columnsfeature_names) df_y pd.DataFrame(y, columnstarget_names) print(f输入数据形状: {df_X.shape}) print(f输出数据形状: {df_y.shape}) print(df_X.head()) print(df_y.head())3.2 数据预处理与划分数据预处理是建模成功的关键一步对于多输出问题同样重要。# 1. 划分训练集和测试集 (80%训练20%测试) X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) print(f训练集输入: {X_train.shape}, 输出: {y_train.shape}) print(f测试集输入: {X_test.shape}, 输出: {y_test.shape}) # 2. 特征缩放可选但推荐尤其是对于基于距离或梯度的模型 # XGBoost基于树模型对特征缩放不敏感但进行缩放有时能加速收敛。 # 这里使用StandardScaler进行标准化 from sklearn.preprocessing import StandardScaler scaler_X StandardScaler() X_train_scaled scaler_X.fit_transform(X_train) X_test_scaled scaler_X.transform(X_test) # 注意使用训练集的参数来转换测试集 # 对于输出变量y是否缩放取决于你的评估指标和业务解释需求。 # 如果使用MSE缩放不影响模型比较但如果你需要观察原始量级的预测值可以不缩放。 # 这里我们选择不缩放y以便于后续直观理解预测误差。3.3 模型构建、训练与评估现在我们使用MultiOutputRegressor来包装XGBoost。# 1. 定义基模型单输出XGBoost回归器 # 关键参数说明 # - n_estimators: 树的数量迭代次数太小欠拟合太大可能过拟合。 # - max_depth: 每棵树的最大深度控制模型复杂度。 # - learning_rate: 学习率缩小每棵树的贡献与n_estimators配合使用。 # - subsample: 每棵树随机采样的样本比例防止过拟合。 # - colsample_bytree: 每棵树随机采样的特征比例。 # - random_state: 随机种子。 base_xgb xgb.XGBRegressor( n_estimators150, max_depth6, learning_rate0.05, subsample0.8, colsample_bytree0.8, random_state42, n_jobs-1 # 使用所有CPU核心加速单模型训练 ) # 2. 构建多输出回归器 multi_xgb MultiOutputRegressor( estimatorbase_xgb, n_jobs-1 # 并行训练各个输出对应的模型-1表示使用所有核心 ) # 3. 训练模型 print(开始训练模型...) multi_xgb.fit(X_train_scaled, y_train) print(模型训练完成。) # 4. 在测试集上进行预测 y_pred multi_xgb.predict(X_test_scaled) # 5. 评估模型性能 # 为每个输出单独计算评估指标并计算平均值 mse_list, mae_list, r2_list [], [], [] for i in range(n_outputs): mse mean_squared_error(y_test[:, i], y_pred[:, i]) mae mean_absolute_error(y_test[:, i], y_pred[:, i]) r2 r2_score(y_test[:, i], y_pred[:, i]) mse_list.append(mse) mae_list.append(mae) r2_list.append(r2) print(fTarget_{i}: MSE {mse:.4f}, MAE {mae:.4f}, R² {r2:.4f}) # 计算整体平均指标 print(f\n平均 MSE: {np.mean(mse_list):.4f}) print(f平均 MAE: {np.mean(mae_list):.4f}) print(f平均 R²: {np.mean(r2_list):.4f}) # 也可以计算所有样本所有输出变量的整体MSE向量化计算 overall_mse mean_squared_error(y_test.ravel(), y_pred.ravel()) print(f整体 MSE (所有数据拉平): {overall_mse:.4f})3.4 可视化分析结果可视化是理解模型表现和发现问题的重要手段。# 1. 绘制每个输出变量的真实值 vs 预测值散点图 fig, axes plt.subplots(1, n_outputs, figsize(15, 4)) for i in range(n_outputs): ax axes[i] ax.scatter(y_test[:, i], y_pred[:, i], alpha0.6, edgecolorsk) # 绘制理想对角线 yx min_val min(y_test[:, i].min(), y_pred[:, i].min()) max_val max(y_test[:, i].max(), y_pred[:, i].max()) ax.plot([min_val, max_val], [min_val, max_val], r--, lw2, labelIdeal Fit) ax.set_xlabel(fTrue {target_names[i]}) ax.set_ylabel(fPredicted {target_names[i]}) ax.set_title(f{target_names[i]} - R²: {r2_list[i]:.4f}) ax.legend() ax.grid(True, linestyle--, alpha0.5) plt.suptitle(True vs Predicted Values for Each Target) plt.tight_layout() plt.show() # 2. 绘制特征重要性这里以第一个输出对应的模型为例 # 注意MultiOutputRegressor为每个输出训练了一个模型它们的特征重要性可能不同。 first_model multi_xgb.estimators_[0] # 获取预测第一个目标的模型 importances first_model.feature_importances_ indices np.argsort(importances)[::-1] # 按重要性降序排列 plt.figure(figsize(10, 6)) plt.title(fFeature Importances for {target_names[0]}) plt.bar(range(X_train.shape[1]), importances[indices], aligncenter) plt.xticks(range(X_train.shape[1]), [feature_names[i] for i in indices], rotation45, haright) plt.ylabel(Importance Score (e.g., Gain)) plt.tight_layout() plt.show() # 可以循环查看所有输出模型的特征重要性 for idx, model in enumerate(multi_xgb.estimators_): importances model.feature_importances_ top_feat_idx np.argmax(importances) print(f对于 {target_names[idx]}, 最重要的特征是: {feature_names[top_feat_idx]} (重要性: {importances[top_feat_idx]:.3f}))4. 模型调优与高级技巧使用默认参数训练模型只是一个开始。要获得最佳性能必须进行系统的超参数调优。4.1 超参数调优策略对于MultiOutputRegressor调优对象是其内部的基模型参数。我们可以使用网格搜索GridSearchCV或随机搜索RandomizedSearchCV并配合多输出评估指标。关键点由于MultiOutputRegressor包装了多个估计器Scikit-learn的搜索工具会默认将多个输出视为一个整体进行调优。我们需要指定一个scoring参数该参数能够处理多输出数据。‘neg_mean_squared_error’等指标默认支持多输出它会计算所有输出的平均MSE。from sklearn.model_selection import GridSearchCV # 重新定义基模型不设置具体参数由网格搜索决定 base_xgb_for_tuning xgb.XGBRegressor(random_state42, n_jobs-1) # 定义多输出模型 multi_model_for_tuning MultiOutputRegressor(base_xgb_for_tuning, n_jobs-1) # 定义要搜索的参数网格 param_grid { estimator__n_estimators: [100, 200, 300], estimator__max_depth: [3, 5, 7], estimator__learning_rate: [0.01, 0.05, 0.1], estimator__subsample: [0.7, 0.8, 1.0], estimator__colsample_bytree: [0.7, 0.8, 1.0] } # 注意参数名前需要加 estimator__ 前缀因为我们要调的是MultiOutputRegressor内部estimator的参数。 # 创建网格搜索对象 # 使用负均方误差作为评分标准它支持多输出。 grid_search GridSearchCV( estimatormulti_model_for_tuning, param_gridparam_grid, cv5, # 5折交叉验证 scoringneg_mean_squared_error, # 越大越好所以是负MSE verbose2, # 输出详细过程 n_jobs-1, # 并行计算 refitTrue # 找到最佳参数后用全部训练数据重新训练 ) # 执行网格搜索耗时可能较长 print(开始网格搜索...) grid_search.fit(X_train_scaled, y_train) print(网格搜索完成。) # 输出最佳参数和最佳得分 print(f最佳参数: {grid_search.best_params_}) print(f最佳交叉验证分数 (负MSE): {grid_search.best_score_:.4f}) # 注意best_score_是交叉验证的平均分数且是负MSE。实际MSE -best_score_ # 使用最佳模型进行预测 best_model grid_search.best_estimator_ y_pred_tuned best_model.predict(X_test_scaled) # 评估调优后的性能 mse_tuned mean_squared_error(y_test, y_pred_tuned) print(f调优后模型在测试集上的整体MSE: {mse_tuned:.4f})注意事项网格搜索的组合数会随着参数数量和取值呈指数增长非常耗时。在实际数学建模竞赛中时间有限建议先使用随机搜索RandomizedSearchCV在更大的参数空间内进行少量迭代采样快速找到有希望的区域。基于随机搜索的结果缩小参数范围再进行小范围的精细网格搜索。利用n_jobs-1充分并行化。考虑使用更高效的超参数优化库如Optuna或Hyperopt它们支持贝叶斯优化通常比网格/随机搜索更快找到好参数。4.2 处理输出变量间的相关性虽然MultiOutputRegressor本身不建模输出相关性但我们可以在特征工程阶段引入技巧来间接帮助模型。技巧将其他输出变量作为输入特征这是一种在训练阶段“偷看”相关性的方法但必须谨慎使用以防数据泄露。对于训练集我们可以利用输出变量之间的相关性。例如在预测Target_2时除了原始输入特征还可以把Target_0和Target_1的真实值或它们的滞后项、变换项作为额外的输入特征。这相当于告诉模型其他目标的信息。关键限制在预测未来或测试集时我们并没有Target_0和Target_1的真实值。因此我们需要采用迭代预测或联合训练的策略。迭代预测先训练模型预测Target_0仅用原始特征。然后用这个模型预测出Target_0的值将其作为新特征与原始特征一起训练预测Target_1的模型。接着用Target_0和Target_1的预测值作为特征训练预测Target_2的模型以此类推。这种方法对输出变量的顺序敏感。联合训练与模型链RegressorChainScikit-learn提供了RegressorChain它类似于MultiOutputRegressor但会按照指定顺序将前面模型的预测输出作为后面模型的额外输入。这比手动迭代更优雅。from sklearn.multioutput import RegressorChain # 使用RegressorChain顺序为[0, 1, 2] chain_model RegressorChain(base_xgb, order[0, 1, 2], random_state42) chain_model.fit(X_train_scaled, y_train) y_pred_chain chain_model.predict(X_test_scaled) mse_chain mean_squared_error(y_test, y_pred_chain) print(fRegressorChain 整体MSE: {mse_chain:.4f})这种方法在输出变量存在强因果关系或时序依赖时效果可能更好但也增加了模型的复杂性和过拟合风险。5. 常见问题与实战排坑指南在实际操作中你肯定会遇到各种问题。以下是一些典型问题及其解决方案。5.1 评估指标选择与解读问题多输出回归应该用什么指标只看平均MSE够吗分析与解决整体标量指标像mean_squared_error(y_true, y_pred)这样直接计算所有维度误差的平均得到一个单一数字便于模型间快速比较。但会掩盖不同输出变量上的性能差异。分维度指标必须为每个输出变量单独计算MSE、MAE、R²等。这能告诉你模型在哪个目标上表现好哪个表现差。如果某个目标的误差远大于其他目标可能需要检查该目标的数据分布是否有异常值量级是否差异巨大考虑对该目标进行单独缩放。考虑为该目标赋予更高的损失权重在自定义损失函数中实现或在业务评估中给予更高关注。业务相关指标最终模型要服务于业务。例如在预测污染物时可能更关心是否超过安全阈值分类准确率而不是绝对浓度误差。可以将回归预测结果转换为分类问题是否超标进行评估。5.2 过拟合与欠拟合诊断问题如何判断多输出XGBoost模型是过拟合还是欠拟合诊断方法学习曲线绘制训练集和验证集误差随训练样本数增加的变化曲线。如果两条曲线都很高且接近可能是欠拟合如果训练误差很低但验证误差很高则是过拟合。XGBoost内置的评估集在训练时传入eval_set参数监控模型在验证集上的性能。# 将训练集再分出一部分作为验证集 X_train_part, X_val, y_train_part, y_val train_test_split(X_train_scaled, y_train, test_size0.2, random_state42) # 对于MultiOutputRegressor需要为每个输出准备评估指标名称 eval_set [(X_train_part, y_train_part), (X_val, y_val)] # 注意XGBoost原生接口处理多输出较麻烦这里更推荐用sklearn API配合learning_curve绘制学习曲线。观察特征重要性如果模型严重依赖少数几个特征而其他大量特征重要性为0可能提示特征冗余或模型复杂度不够。如果很多不相关的特征被赋予较高重要性可能过拟合。调参反馈欠拟合迹象高偏差增加max_depth、n_estimators减少正则化参数如reg_alpha,reg_lambda增加subsample和colsample_bytree。过拟合迹象高方差减少max_depth、n_estimators增加reg_alpha和reg_lambda减少subsample和colsample_bytree增加min_child_weight。5.3 输出变量尺度差异大问题三个输出目标一个范围在[0, 1]一个在[100, 1000]一个在[-10, 10]。直接用MSE损失模型会主要优化误差大的目标范围在[100,1000]的那个而忽略小尺度目标。解决方案标准化/归一化输出目标在训练前对每个输出变量y分别进行标准化减均值除标准差或归一化缩放到[0,1]。这样所有目标都处于相近的尺度。切记要用训练集的均值和标准差或最大最小值来转换训练集和测试集。预测完成后再将预测值反向转换回原始尺度进行评估和解释。from sklearn.preprocessing import StandardScaler scaler_y_list [] y_train_scaled np.zeros_like(y_train) y_test_scaled np.zeros_like(y_test) for i in range(n_outputs): scaler StandardScaler() y_train_scaled[:, i] scaler.fit_transform(y_train[:, i].reshape(-1, 1)).ravel() y_test_scaled[:, i] scaler.transform(y_test[:, i].reshape(-1, 1)).ravel() scaler_y_list.append(scaler) # 用 y_train_scaled 和 y_test_scaled 训练和评估模型 # 预测得到 y_pred_scaled 后需要反标准化 # y_pred_original scaler_y_list[i].inverse_transform(y_pred_scaled[:, i].reshape(-1, 1)).ravel()使用自定义加权损失函数策略二在自定义损失函数中为每个输出变量的误差项赋予不同的权重。权重可以根据业务重要性或目标变量的方差倒数来确定。5.4 特征重要性不一致问题MultiOutputRegressor中每个子模型给出的特征重要性排名不一样我该信哪个分析与解决 这是正常现象说明不同的输出变量依赖于不同的输入特征组合。这正是多输出问题的有趣之处。你应该分别分析针对每个重要的输出变量查看其对应的特征重要性图理解是哪些特征在驱动该目标的预测。汇总分析可以计算所有子模型特征重要性的平均值、中位数或最大值得到一个“全局”特征重要性视图识别出对大多数目标都重要的特征。all_importances np.array([est.feature_importances_ for est in multi_xgb.estimators_]) mean_importances np.mean(all_importances, axis0) # 然后按 mean_importances 排序和绘图业务解读将特征重要性与领域知识结合。如果某个特征在业务上被认为很关键但在模型中重要性很低可能需要检查特征工程例如创建交互项、非线性变换或数据本身是否存在问题。5.5 模型保存与部署问题训练好的多输出XGBoost模型如何保存和加载解决方案 由于我们使用的是Scikit-learn的MultiOutputRegressor包装器它可以和普通的Scikit-learn模型一样使用joblib或pickle进行序列化。import joblib # 保存模型 model_filename multi_output_xgb_model.joblib joblib.dump(multi_xgb, model_filename) # 如果使用了特征缩放器也需要保存 joblib.dump(scaler_X, scaler_X.joblib) # 如果对y也做了缩放保存y的缩放器列表 # joblib.dump(scaler_y_list, scaler_y_list.joblib) print(f模型已保存至 {model_filename}) # 加载模型 loaded_model joblib.load(model_filename) loaded_scaler_X joblib.load(scaler_X.joblib) # 对新数据进行预测 new_data_scaled loaded_scaler_X.transform(new_data_raw) new_predictions loaded_model.predict(new_data_scaled)记住在部署时必须确保数据预处理如缩放的步骤与训练时完全一致。将预处理管道Pipeline和模型打包在一起是更稳健的做法。from sklearn.pipeline import Pipeline # 创建包含预处理和建模的完整管道 pipeline Pipeline([ (scaler, StandardScaler()), (regressor, MultiOutputRegressor(base_xgb)) ]) pipeline.fit(X_train, y_train) # 注意这里使用未缩放的X_train # 保存和加载整个管道 joblib.dump(pipeline, full_pipeline.joblib) # 预测时管道会自动进行缩放 new_predictions pipeline.predict(new_data_raw)通过以上五个部分的详细拆解从核心概念到代码实战从模型调优到问题排查你应该已经掌握了使用XGBoost构建多输入多输出回归预测模型的完整技能链。在数学建模竞赛中这套方法能为你提供一个强大、可靠且可解释的预测基线助你从容应对涉及多指标协同预测的复杂赛题。