公司动态

XGBoost多输出回归实战:数学建模竞赛中的MIMO预测模型构建与调优

📅 2026/8/28 7:49:30
XGBoost多输出回归实战:数学建模竞赛中的MIMO预测模型构建与调优
1. 从单输出到多输出为什么数学建模竞赛越来越青睐MIMO回归如果你参加过近几年的数学建模竞赛无论是国赛、美赛还是亚太杯你会发现一个明显的趋势赛题越来越“贪心”。组委会不再满足于让你预测一个单一的指标比如“预测某地未来24小时的PM2.5浓度”。他们更倾向于抛出这样的问题“预测未来一周内某城市多个监测点的PM2.5、SO2、NO2浓度并分析其空间分布规律”。从预测一个值到同时预测一整套相互关联的值这就是多输入多输出Multi-Input Multi-Output, MIMO回归预测模型要解决的核心问题。这背后反映的是现实世界问题的复杂性。在环境科学、金融风控、工业生产、医疗诊断等领域变量之间很少是孤立的。预测股票价格时你不可能只关心收盘价开盘价、最高价、最低价、成交量这些指标天然就是一套需要同时预测的“套餐”。在工业生产中一个工艺参数的变化可能同时影响产品的多个质量指标如强度、韧性、纯度。如果我们还沿用传统的“单输出”模型为每个指标单独训练一个模型不仅计算成本高更重要的是模型之间完全割裂无法捕捉和利用输出变量之间潜在的相关性这会导致预测结果在整体上不协调甚至出现逻辑矛盾。因此掌握MIMO回归模型已经成为数学建模参赛者从“会做题”到“做好题”的关键分水岭。它让你有能力处理更复杂、更贴近实际的赛题在论文中展现出更高的建模视野和技术深度。而XGBoost作为机器学习竞赛和工业界经久不衰的“神器”以其卓越的性能、高效的计算和对复杂关系强大的捕捉能力自然成为了实现MIMO回归预测的绝佳载体。今天我就结合自己多次带队参赛和项目实战的经验手把手带你拆解如何用XGBoost构建一个稳健、高效的多输出回归预测模型并分享那些官方文档里不会写的“踩坑”实录。2. 理解MIMO回归不止是多个模型的简单堆叠在深入代码之前我们必须从原理上厘清MIMO回归与多个独立单输出模型的本质区别。很多人初学者的误区是MIMO不就是训练N个模型每个模型预测一个输出吗如果真是这样那这个概念就毫无新意了。真正的MIMO模型其价值在于联合建模。2.1 核心思想利用输出间的相关性提升整体精度想象一下你要预测一个人的身高和体重。如果分开预测预测身高的模型完全不知道体重的信息反之亦然。但常识告诉我们身高和体重是强相关的。一个预测出身高很高但体重很轻的个体在现实中是罕见的。MIMO模型在训练时其损失函数会同时考虑所有输出变量的误差并在优化过程中隐式地学习到这些输出变量之间的协方差结构。以XGBoost为例当我们将其扩展用于MIMO任务时通常通过multioutput策略模型在构建每一棵树时选择的特征分裂点不仅要能最好地区分单个目标值的差异更要能同时区分所有目标值构成的多维向量的差异。这相当于在特征空间中寻找能同时优化所有输出预测能力的规则。当输出变量间存在正相关或负相关时这种联合学习能有效利用这些信息往往能获得比独立模型集更好的泛化性能特别是在训练数据有限的情况下数学建模竞赛的常态。2.2 常见策略与XGBoost的适配性实现MIMO回归主要有两大类策略问题转换法将多输出问题分解为多个单输出问题。这就是前面提到的独立模型法。此外还有一种更巧妙的方法叫“输出编码”比如将多个连续输出通过某种方式如PCA压缩成一个标签但这对于回归问题处理起来比较麻烦更常用于多标签分类。算法扩展法直接修改算法内部机制使其能原生处理多输出。一些算法如决策树、支持向量回归SVR有天然的多输出扩展。XGBoost属于第二类。虽然其核心是 boosting 决策树但通过设置objectivereg:squarederror并配合multioutput参数在某些接口中或者更常见地使用scikit-learn的MultiOutputRegressor包装器可以使其支持多输出。MultiOutputRegressor的本质是为每个输出训练一个独立的XGBoost模型但它提供了一个统一的接口并且在某些实现中可以进行并行训练方便了我们的使用。而一些更底层的库如xgboost的DMatrix可以直接接受二维的目标值y实现真正的联合训练这通常能带来更好的效果但需要对XGBoost有更深的理解和自定义能力。对于数学建模竞赛我推荐优先使用sklearn.multioutput.MultiOutputRegressor包装XGBoost。理由很实际稳。它减少了因输出维度带来的复杂调试每个输出一个模型思路清晰易于解释而且sklearn的管道Pipeline和网格搜索GridSearchCV能无缝集成极大简化了我们的调参和验证流程。在论文中你可以清晰地阐述“我们为每个预测变量建立了一个XGBoost模型并利用集成思想进行预测”这完全符合评委的审阅逻辑。3. 实战构建从数据准备到模型训练的全流程拆解理论说得再多不如一行代码。我们假设一个经典的数学建模场景预测城市多个区域的空气质量指数AQI及其主要组分PM2.5, PM10, SO2, NO2, CO, O3。输入特征可能包括气象数据温度、湿度、风速、气压、时间特征小时、星期、是否节假日、历史污染物浓度、以及可能的空间特征区域编码、与污染源的距离。3.1 环境准备与数据预处理首先确保你的环境安装了必要的库。除了经典的pandas,numpy,matplotlib核心是xgboost和scikit-learn。pip install xgboost scikit-learn pandas numpy matplotlib seaborn数据预处理是模型成功的基石对于MIMO问题尤其重要。import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.multioutput import MultiOutputRegressor import xgboost as xgb import warnings warnings.filterwarnings(ignore) # 1. 加载数据 # 假设 df 是一个 DataFrame 列包括特征和多个目标变量 # 例如特征列: [‘temp’, ‘humidity’, ‘wind_speed’, ‘hour’, ‘day_of_week’, ‘PM2.5_lag1’, ...] # 目标列: [‘PM2.5’, ‘PM10’, ‘SO2’, ‘NO2’, ‘CO’, ‘O3’] df pd.read_csv(air_quality_data.csv) # 2. 划分特征和目标 X df.drop(columns[PM2.5, ‘PM10’, ‘SO2’, ‘NO2’, ‘CO’, ‘O3’]) # 请替换为你的目标列名 y df[[PM2.5, ‘PM10’, ‘SO2’, ‘NO2’, ‘CO’, ‘O3’]] # 注意y 现在是一个 DataFrame 或二维数组 # 3. 处理缺失值根据数据情况选择 # 对于特征X常用填充均值、中位数、前后值或删除。 # 对于时间序列前向填充ffill可能更合理。 X X.fillna(methodffill).fillna(methodbfill) # 先前填充后后填充 # 对于目标y如果缺失严重可能需要考虑删除该样本因为我们是监督学习。 y y.fillna(methodffill).fillna(methodbfill) # 4. 划分训练集和测试集 # 对于时间序列数据切记不能随机划分必须按时间顺序划分。 # 假设数据是按时间排序的 split_ratio 0.8 split_idx int(len(X) * split_ratio) X_train, X_test X.iloc[:split_idx], X.iloc[split_idx:] y_train, y_test y.iloc[:split_idx], y.iloc[split_idx:] # 5. 特征标准化/归一化 # XGBoost基于树模型理论上不需要对特征进行缩放但对连续型特征进行标准化有时能加速收敛。 # 更重要的是如果你的特征量纲差异巨大如距离是千米浓度是微克/立方米 # 标准化可以避免数值问题并使后续分析如特征重要性更公平。 scaler_X StandardScaler() X_train_scaled scaler_X.fit_transform(X_train) X_test_scaled scaler_X.transform(X_train) # 注意使用训练集的参数转换测试集 # 对于目标变量y是否要标准化这是一个关键选择。 # 如果多个目标变量的量纲和范围差异很大如PM2.5在0-500CO在0-10 # 直接使用原始值训练模型可能会被数值大的目标如PM2.5主导导致对小数值目标如CO预测不准。 # 因此对y进行标准化通常是MIMO回归的好习惯。 scaler_y StandardScaler() y_train_scaled scaler_y.fit_transform(y_train) # y_test 我们暂时不转换用于最终评估时反标准化。注意这里有一个极易踩坑的点。很多同学在对目标变量y进行标准化后直接用标准化后的y计算评估指标如RMSE得到一个非常小的、看似很棒的数字比如0.1。这是完全错误的这个0.1是标准化后空间的误差没有物理意义。正确的做法是用训练好的模型预测得到标准化后的结果y_pred_scaled然后通过scaler_y.inverse_transform(y_pred_scaled)反标准化回原始量纲再与原始的y_test计算误差指标。3.2 构建与训练MultiOutput XGBoost模型接下来是核心的模型构建环节。我们将使用sklearn的 API因为它更符合我们的建模习惯且易于集成到更复杂的工作流中。# 1. 定义基础XGBoost回归器 # 这里先使用一组相对保守的初始参数 base_xgb xgb.XGBRegressor( objectivereg:squarederror, # 回归任务使用平方误差 n_estimators200, # 树的棵树可以先设一个中等值后续通过早停优化 learning_rate0.05, # 学习率小一些通常更稳健但需要更多树 max_depth6, # 树的最大深度控制模型复杂度防止过拟合 subsample0.8, # 每棵树随机采样的样本比例 colsample_bytree0.8, # 每棵树随机采样的特征比例 random_state42, # 随机种子保证结果可复现 n_jobs-1 # 使用所有CPU核心并行训练 ) # 2. 使用MultiOutputRegressor进行包装 multi_output_model MultiOutputRegressor(base_xgb) # 3. 训练模型 print(开始训练MultiOutput XGBoost模型...) multi_output_model.fit(X_train_scaled, y_train_scaled) print(训练完成)这个过程会为y的每一个维度本例中是6种污染物训练一个独立的XGBoost模型。MultiOutputRegressor会管理这6个模型当我们调用predict时它会收集所有模型的预测结果并组合成一个[n_samples, n_outputs]的数组。3.3 模型预测与结果反标准化训练完成后进行预测并评估。# 1. 在测试集上进行预测得到的是标准化后的结果 y_pred_scaled multi_output_model.predict(X_test_scaled) # 形状: (n_test_samples, 6) # 2. 将预测结果反标准化回原始量纲 y_pred scaler_y.inverse_transform(y_pred_scaled) # 3. 计算整体评估指标 from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score # 计算每个输出变量的指标 metrics_per_output {} for i, col in enumerate(y_test.columns): mse mean_squared_error(y_test.iloc[:, i], y_pred[:, i]) rmse np.sqrt(mse) mae mean_absolute_error(y_test.iloc[:, i], y_pred[:, i]) r2 r2_score(y_test.iloc[:, i], y_pred[:, i]) metrics_per_output[col] {RMSE: rmse, ‘MAE’: mae, ‘R2’: r2} print(f{col}: RMSE {rmse:.2f}, MAE {mae:.2f}, R2 {r2:.4f}) # 计算所有输出变量的平均指标有时也很有参考价值 total_mse mean_squared_error(y_test, y_pred, multioutputuniform_average) total_rmse np.sqrt(total_mse) total_r2 r2_score(y_test, y_pred, multioutputuniform_average) print(f\n整体平均指标 - RMSE: {total_rmse:.2f}, R2: {total_r2:.4f})3.4 可视化分析让结果说话在数学建模论文中精美的可视化是获得高分的关键。我们需要从多个角度展示预测效果。import matplotlib.pyplot as plt import seaborn as sns # 设置绘图风格 sns.set_style(whitegrid) plt.figure(figsize(16, 10)) # 1. 绘制每个输出变量的真实值 vs 预测值散点图 outputs y_test.columns for idx, col in enumerate(outputs): plt.subplot(2, 3, idx1) # 假设有6个输出排成2行3列 plt.scatter(y_test[col], y_pred[:, idx], alpha0.5, s20) # 绘制对角线完美预测线 min_val min(y_test[col].min(), y_pred[:, idx].min()) max_val max(y_test[col].max(), y_pred[:, idx].max()) plt.plot([min_val, max_val], [min_val, max_val], r--, lw2) plt.xlabel(True col) plt.ylabel(Predicted col) plt.title(f{col}: R2 {metrics_per_output[col][“R2”]:.3f}) plt.tight_layout() plt.suptitle(各污染物预测值与真实值散点对比, fontsize16, y1.02) plt.show() # 2. 绘制一段时间序列的对比以PM2.5为例 sample_idx 0 # 选择第一个输出变量例如PM2.5 plt.figure(figsize(14, 5)) plt.plot(y_test.iloc[:100, sample_idx].values, labelTrue, markero, markersize3) plt.plot(y_pred[:100, sample_idx], labelPredicted, markers, markersize3) plt.xlabel(Time Step (Sample Index)) plt.ylabel(y_test.columns[sample_idx]) plt.title(f{y_test.columns[sample_idx]} - 真实值与预测值时间序列对比 (前100个样本)) plt.legend() plt.grid(True, linestyle--, alpha0.7) plt.show() # 3. 绘制各输出变量预测误差如RMSE的柱状图直观比较不同变量的预测难度 errors [metrics_per_output[col][‘RMSE’] for col in outputs] plt.figure(figsize(10, 6)) bars plt.bar(outputs, errors, colorsns.color_palette(“husl”, len(outputs))) plt.xlabel(Pollutant) plt.ylabel(RMSE) plt.title(各污染物预测RMSE对比) # 在柱子上方添加数值 for bar, error in zip(bars, errors): plt.text(bar.get_x() bar.get_width()/2, bar.get_height() max(errors)*0.01, f{error:.1f}, hacenter, vabottom) plt.xticks(rotation45) plt.tight_layout() plt.show()这些图表能非常直观地展示模型在每个具体输出上的性能以及不同污染物预测的难易程度为你的论文提供强有力的论据支撑。4. 性能飞跃针对MIMO场景的XGBoost高级调优策略用默认参数跑通模型只是第一步。要让模型在竞赛中脱颖而出精细化的调优必不可少。针对MIMO时间序列的特点我们的调优需要有侧重点。4.1 超参数调优网格搜索与随机搜索虽然我们有6个独立的模型但通常假设它们的最佳超参数是相似的因为数据特征相同。我们可以使用GridSearchCV或RandomizedSearchCV对一个输出如最重要的PM2.5进行调优然后将找到的最佳参数集应用于所有输出模型或者作为MultiOutputRegressor的通用参数。这是效率与效果的一个平衡。from sklearn.model_selection import GridSearchCV, TimeSeriesSplit # 由于是时间序列数据我们不能用简单的K折交叉验证要用时间序列分割 tscv TimeSeriesSplit(n_splits5) # 定义要搜索的参数网格 param_grid { ‘estimator__n_estimators’: [100, 200, 300], ‘estimator__learning_rate’: [0.01, 0.05, 0.1], ‘estimator__max_depth’: [3, 6, 9], ‘estimator__subsample’: [0.7, 0.8, 1.0], ‘estimator__colsample_bytree’: [0.7, 0.8, 1.0], ‘estimator__gamma’: [0, 0.1, 0.2], # 节点分裂所需的最小损失减少 } # 创建一个新的基础模型用于搜索 base_for_search xgb.XGBRegressor(objectivereg:squarederror, random_state42, n_jobs-1) multi_for_search MultiOutputRegressor(base_for_search) # 为了节省时间我们只针对第一个输出索引0进行调参。 # 注意GridSearchCV会尝试拟合所有输出但我们通过‘scoring’参数指定只评估第一个输出。 # 这里需要一个自定义的scorer或者使用sklearn的‘make_scorer’配合多输出评估。 # 更简单直接的方法单独拿出第一个目标变量来调参。 print(“针对第一个输出变量进行超参数调优...”) y_train_single y_train_scaled[:, 0] # 取第一个目标变量 grid_search GridSearchCV( estimatorbase_for_search, # 这次是对单个XGBRegressor调参 param_gridparam_grid, cvtscv, scoringneg_mean_squared_error, # 负均方误差sklearn约定越大越好 verbose1, n_jobs-1 ) grid_search.fit(X_train_scaled, y_train_single) print(“最佳参数”, grid_search.best_params_) print(“最佳交叉验证分数负MSE:”, grid_search.best_score_) # 使用找到的最佳参数重新定义我们的MultiOutput模型 best_params grid_search.best_params_ # 注意grid_search.best_estimator_ 已经是一个拟合好的单输出模型。 # 我们需要用这些参数重新初始化一个MultiOutput模型。 optimized_base_xgb xgb.XGBRegressor(**best_params, objectivereg:squarederror, random_state42, n_jobs-1) optimized_multi_model MultiOutputRegressor(optimized_base_xgb) optimized_multi_model.fit(X_train_scaled, y_train_scaled)实操心得对于数学建模竞赛时间有限进行全量的网格搜索可能不现实。我常用的策略是两阶段调优第一阶段用RandomizedSearchCV进行较广范围的随机搜索迭代50-100次快速定位参数的大致优区第二阶段在优区附近用小范围的GridSearchCV进行精细搜索。同时learning_rate和n_estimators是一对需要联合考虑的黄金参数。较小的learning_rate需要更多的树n_estimators来达到好的效果但模型更稳健不易过拟合。我通常先固定一个较小的learning_rate如0.05然后用早停法early_stopping_rounds来确定大致需要的树的数量再以此为基础调其他参数。4.2 特征工程为时间序列与空间关联注入先验知识XGBoost虽然能自动处理特征交互但好的特征工程能极大降低模型学习难度提升性能和可解释性。对于我们的多输出空气质量预测场景时间特征除了简单的“小时”、“星期几”可以构造“是否工作日”、“是否节假日”、“一天中的时段早晨、下午、晚上、深夜”、“季度”、“月份”等周期性特征。对于小时、星期几这类循环特征建议使用正弦余弦编码以体现其周期性如周一和周日本质上相邻。df[hour_sin] np.sin(2 * np.pi * df[hour] / 24) df[hour_cos] np.cos(2 * np.pi * df[hour] / 24) df[week_sin] np.sin(2 * np.pi * df[day_of_week] / 7) df[week_cos] np.cos(2 * np.pi * df[day_of_week] / 7)滞后特征这是时间序列预测的灵魂。不仅为目标变量创建滞后项如PM2.5前1小时、前3小时、前24小时的值也可以为重要的输入特征如风速、温度创建滞后项。这相当于给模型提供了“记忆”。for lag in [1, 3, 6, 12, 24]: # 根据数据采样频率决定 df[f‘PM2.5_lag_{lag}’] df[‘PM2.5’].shift(lag) df[f‘wind_speed_lag_{lag}’] df[‘wind_speed’].shift(lag) # 注意创建滞后特征后前lag行会变成NaN需要删除或填充滑动窗口统计特征计算过去一段时间窗口内的统计量如均值、标准差、最大值、最小值。这能帮助模型捕捉短期趋势和波动。window_sizes [3, 6, 12] for w in window_sizes: df[f‘PM2.5_rolling_mean_{w}’] df[‘PM2.5’].rolling(windoww, min_periods1).mean() df[f‘PM2.5_rolling_std_{w}’] df[‘PM2.5’].rolling(windoww, min_periods1).std()空间交互特征如果数据包含多个区域可以构造区域之间的污染物浓度差值、比值或者使用空间插值如Kriging得到的背景场浓度作为特征。领域知识特征例如根据气象学知识构造“大气稳定度指数”、“通风系数”风速与混合层高度的乘积等复合特征。这些特征往往有奇效。4.3 集成学习与模型融合进阶MultiOutputRegressor已经是一种简单的模型集成多个模型的集合。我们还可以更进一步Stacking集成用第一层多个不同的基模型如XGBoost, LightGBM, RandomForest分别进行MIMO预测然后将它们的预测结果作为新的特征输入到第二层的一个元模型通常是线性回归或简单的XGBoost中进行最终预测。这能有效融合不同模型的优势。针对不同输出的差异化建模如果发现某些输出变量如O3的预测效果明显差于其他不要死磕一套参数。可以单独为这个“困难户”设计更复杂的特征或者尝试不同的模型如对于具有强周期性的O3可以结合SARIMA模型进行混合预测。5. 避坑指南与竞赛实战经验纸上得来终觉浅绝知此事要躬行。下面是我在多次实战中总结的、最容易出问题的几个环节。5.1 数据泄露时间序列划分的致命陷阱这是新手甚至是有经验的选手都可能犯的最大错误。绝对不能使用sklearn.model_selection.train_test_split的默认随机划分来处理时间序列数据随机划分会导致未来信息“泄露”到训练集中模型看似在测试集上表现惊人实则毫无泛化能力。正确做法严格按时间顺序划分。假设你有2020年1月到2023年12月的数据可以用2020-2022年训练2023年测试。在交叉验证时使用TimeSeriesSplit它确保训练集总是在测试集之前。# 错误做法数据泄露 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) # 正确做法时间顺序 split_point int(len(X) * 0.8) X_train, X_test X.iloc[:split_point], X.iloc[split_point:] y_train, y_test y.iloc[:split_point], y.iloc[split_point:]5.2 评估指标的选择与误读在MIMO回归中评估指标需要谨慎选择和解读。统一平均 vs 按输出平均mean_squared_error(y_true, y_pred, multioutputuniform_average)计算的是所有输出所有样本的平均MSE。这给出了一个整体的性能概览。但有时我们更关心每个输出变量的单独表现特别是当它们的物理意义和重要程度不同时。务必在论文中同时展示整体指标和分项指标。R²分数的陷阱r2_score在多输出模式下multioutputuniform_average计算的是每个输出R²的简单平均。但要注意R²可能为负当模型比简单用均值预测还差时。如果某个输出的R²为负说明模型在该输出上完全失败需要单独分析原因。量纲的影响RMSE和MAE是有量纲的直接比较不同量纲输出的误差大小没有意义。比较PM2.5的RMSE10和CO的RMSE0.5并不能说前者预测得差。通常需要结合业务背景或者使用标准化后的误差如NRMSE归一化均方根误差进行比较。5.3 特征重要性分析多输出下的复杂解读XGBoost提供了强大的特征重要性分析feature_importances_。但在MultiOutputRegressor包装下每个输出模型都有自己的特征重要性。如何呈现分别展示为每个重要输出如PM2.5和O3绘制其特征重要性条形图分析影响不同污染物的主导因素有何异同。这能体现你分析的深度。聚合展示计算所有输出模型特征重要性的平均值或加权平均按R²加权得到一组“全局”重要性特征。这有助于识别对整体预测贡献最大的特征。importances [] for idx, estimator in enumerate(optimized_multi_model.estimators_): importances.append(estimator.feature_importances_) avg_importance np.mean(importances, axis0) # 然后绘制 avg_importance 的条形图5.4 过拟合与早停法XGBoost虽然通过正则化项有一定抗过拟合能力但在数据量有限或噪声大的竞赛场景下过拟合风险依然存在。除了调整max_depth,gamma,subsample等参数早停法是最有效的工具。我们可以利用验证集进行早停。但注意在时间序列中验证集也必须是训练集时间线之后的连续数据块。# 从训练集中再分出一部分作为验证集用于早停 X_train_main, X_val, y_train_main, y_val train_test_split(X_train_scaled, y_train_scaled, test_size0.2, shuffleFalse) # shuffleFalse! # 使用原生XGBoost接口进行训练以便使用早停回调 # 我们需要为每个输出单独做这里以第一个输出为例 dtrain xgb.DMatrix(X_train_main, labely_train_main[:, 0]) dval xgb.DMatrix(X_val, labely_val[:, 0]) params { ‘objective’: ‘reg:squarederror’, ‘learning_rate’: 0.05, ‘max_depth’: 6, ‘subsample’: 0.8, ‘colsample_bytree’: 0.8, ‘seed’: 42 } evals [(dtrain, ‘train’), (dval, ‘eval’)] model_single xgb.train(params, dtrain, num_boost_round1000, evalsevals, early_stopping_rounds50, verbose_eval50) # 输出会显示在验证集上性能不再提升时即停止并返回最佳迭代次数。 best_n_estimators model_single.best_iteration将这个best_n_estimators应用到我们的MultiOutputRegressor的n_estimators参数中可以避免不必要的过拟合。6. 从模型到论文如何在数学建模竞赛中呈现你的工作构建一个强大的模型只是成功的一半如何在论文中清晰、专业地呈现它是赢得评委青睐的另一半。问题重述与模型框架图在模型介绍部分用一张清晰的框架图展示你的MIMO-XGBoost预测流程。包括数据预处理、特征工程、模型构建突出MultiOutputRegressor的并行结构、训练优化、预测与反标准化、评估反馈等环节。这能让评委迅速把握你的技术路线。分阶段阐述不要将所有代码和步骤堆砌在一起。按照“数据预处理与特征构造”、“多输出预测模型构建”、“模型训练与超参数优化”、“预测结果分析与评估”的逻辑顺序来组织小节。每个小节下再分点论述关键技术细节。可视化结果深度解读不要仅仅把图表贴上去。对每一张图都要有文字描述和深入分析。例如散点图指出预测值与真实值的集中分布区域点偏离对角线的可能原因如极端污染事件难以预测。时间序列对比图指出模型在哪些时段如平稳期、突变期预测效果好/差并尝试结合特征如风速突变解释原因。误差对比柱状图分析为什么某种污染物如O3的预测误差最大是数据噪声大还是其生成机理更复杂模型捕捉能力不足灵敏度分析与模型鲁棒性检验这是加分项。可以设计实验比如特征重要性消融实验依次移除重要性最高的特征观察模型性能下降程度验证该特征的关键性。时间窗口灵敏度改变滞后特征和滑动窗口的大小观察模型性能变化确定最优的历史依赖长度。噪声鲁棒性在输入特征中加入不同程度的高斯噪声测试模型预测误差的增长情况说明模型的稳定性。模型对比务必设置合理的基线模型进行对比。例如基线1朴素预测用前一天同一时刻的值作为预测值持久化预测。基线2单输出模型集用6个独立的、未经联合训练的XGBoost模型分别预测。你的模型MIMO-XGBoost。 用表格清晰列出所有模型在所有评估指标上的结果并用文字强调你的模型在哪些方面如整体RMSE、对相关性强的污染物预测一致性取得了显著提升。代码与可复现性在附录中提供清晰、注释完整的核心代码片段如特征工程、模型定义、训练和评估流程。确保关键步骤如数据划分、标准化的随机种子固定声明主要使用的库及版本号保证评审专家可以复现你的结果。最后记住数学建模竞赛的核心是“用数学方法解决实际问题”。你的论文要始终围绕“问题-模型-结果-分析”这条主线。XGBoost和MIMO回归是你的工具但你的思考过程、对问题的洞察、以及基于结果的合理化建议才是真正打动评委的地方。把模型当作一个黑盒子扔上去是远远不够的你需要打开它解释它为什么有效以及它的局限性在哪里这才是一篇优秀论文应有的深度。