ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

预测模型实战指南:从ARIMA到XGBoost的代码实现与选型

预测模型实战指南:从ARIMA到XGBoost的代码实现与选型 1. 项目概述从理论到代码的预测模型实战在数学建模竞赛和实际数据分析工作中预测模型是绕不开的核心工具。无论是预测未来一周的客流量还是分析股票价格的潜在走势一个合适的预测模型往往能成为解决问题的关键。然而很多初学者甚至是有一定经验的参赛者常常面临一个困境理论书上讲得头头是道一到自己动手实现就感觉无从下手代码跑不通结果出不来论文里写不出有说服力的分析。这正是“理论”与“机器代码实现”之间的鸿沟。这个内容就是专门为填平这道鸿沟而准备的。它不是一本包罗万象的算法教科书而是一份聚焦于“常见预测模型”的实战指南。我会结合自己多年带队和评审的经验挑选出在数学建模竞赛如国赛、美赛、亚太杯和实际业务中最常被用到的几类预测模型不仅讲清楚它们背后的核心思想与适用场景更重要的是会提供清晰、可运行、带详细注释的Python代码实现并分享如何将这些代码结果转化为论文中具有说服力的图表和文字分析。适合阅读的人群包括正在备战数学建模竞赛的大学生、需要快速应用预测模型解决实际问题的数据分析师、以及希望巩固机器学习预测模型代码实现能力的开发者。我们将避开过于晦涩的数学推导用“说人话”的方式解释原理用“能跑通”的代码展示过程目标是让你看完就能用用了就有效。2. 预测模型的核心分类与选型逻辑面对一个预测问题第一步也是最重要的一步就是选择合适的模型。模型选错了后面再精致的调参也是事倍功半。这里我根据预测目标的特性将常见模型分为三大类并给出清晰的选型决策树。2.1 时间序列预测当数据自带时间戳时间序列预测的核心假设是“未来与过去相似”即利用历史数据中蕴含的趋势、季节性和周期性来外推未来。如果你的数据是按固定时间间隔采集的如每日销售额、每小时温度、每分钟股票报价那么首先应该考虑时间序列模型。经典模型代表ARIMA模型堪称时间序列预测的“基本功”。它综合了自回归(AR)、差分(I)和移动平均(MA)三个部分擅长捕捉平稳时间序列的线性关系。对于有明显趋势或季节性的数据需要先进行差分或使用SARIMA季节性ARIMA。指数平滑模型包括简单指数平滑、霍尔特线性趋势以及霍尔特-温特斯季节性模型。它的思想是给近期的观测值更大的权重远期的权重按指数衰减。实现简单对趋势和季节性的预测非常直观在商业预测中应用极广。Prophet由Facebook开源专门为商业时间序列设计。它内置处理了节假日效应、变点检测趋势突变和缺失值对异常值不敏感基本属于“开箱即用”型特别适合具有强季节性如每周、每年和多个节假日的序列。选型心得数据量小、趋势明显优先尝试指数平滑它参数少不容易过拟合。数据平稳、想探究内在结构用ARIMA通过ACF/PACF图定阶的过程能帮你深入理解序列特性。数据包含复杂季节性、节假日无脑用Prophet它能节省你大量特征工程的时间。注意传统时间序列模型通常是“单变量”的即只利用序列自身的历史值进行预测。2.2 回归预测当你有丰富的特征回归预测解决的是“因变量Y如何随多个自变量X变化”的问题。它不要求数据有时间顺序而是寻找特征与目标之间的映射关系。比如根据房屋的面积、地段、房龄预测房价根据用户的年龄、历史消费、活跃度预测其流失概率。经典模型代表线性回归基石中的基石。假设特征与目标呈线性关系可解释性极强。通过系数可以判断特征的影响方向和大小。虽然简单但在特征经过充分筛选和预处理后其表现往往非常稳健。决策树与随机森林非线性关系的捕捉利器。决策树通过一系列if-else规则进行预测易于理解和可视化。随机森林是决策树的集成通过构建多棵树并投票能有效降低单棵树的过拟合风险是当前应用最广泛的模型之一无需太多调参就能得到不错的结果。XGBoost/LightGBM梯度提升决策树GBDT的高效实现堪称竞赛“神器”。它们通过迭代地训练新树来纠正前一棵树的残差精度通常比随机森林更高但调参也更复杂。LightGBM在速度和内存消耗上通常更有优势。选型心得追求可解释性特征与目标关系近似线性用线性回归配合正则化Lasso/Ridge防止过拟合。特征与目标关系复杂、非线性且想要一个稳健的基线模型用随机森林。它几乎总是个不会出错的选择。追求极致精度愿意花时间调参上XGBoost或LightGBM。在结构化数据的预测任务上它们常常能刷出最高分。注意回归预测模型的效果极度依赖于特征工程的质量。特征的好坏直接决定了模型性能的天花板。2.3 混合与高级模型当简单模型不够用当问题复杂单一模型难以胜任时就需要考虑更高级的策略。时序特征混合模型例如在时间序列预测中除了历史值我们还可以加入其他特征如“是否是周末”、“促销活动标识”等。这时可以将问题转化为一个回归问题使用XGBoost等模型进行预测。或者使用Prophet生成趋势、季节项作为特征再输入到LightGBM中进行微调预测。深度学习模型对于超长序列或具有复杂空间依赖的数据如多变量时间序列可以考虑LSTM、GRU等循环神经网络或Transformer架构。它们能自动学习长期依赖和复杂模式但需要大量的数据、计算资源和调参经验在数学建模竞赛中需谨慎使用除非问题非常契合且你有把握。选型决策流程面对一个预测问题你可以按以下流程思考数据是否有时间顺序是 - 进入时间序列分支否 - 进入回归预测分支。时间序列分支序列是否有明显季节性/节假日是 - 优先尝试Prophet否 - 序列是否平稳是 - 尝试ARIMA否有趋势- 尝试指数平滑或先差分再用ARIMA。回归预测分支是否需要极强的模型可解释性是 - 线性回归可加Lasso做特征选择否 - 想要快速稳健的基线 - 随机森林想要冲击更高精度 - XGBoost/LightGBM。无论哪个分支如果效果不佳考虑混合模型思路或回头检查数据质量和特征工程。注意模型选型不是一次性的而是一个迭代过程。通常先用简单模型跑出基线再尝试复杂模型看提升是否显著。在数学建模论文中展示这个选型对比过程本身就是一个加分项。3. 核心模型的理论精要与代码实现拆解这一部分我们将深入三个最具代表性的模型内部理解其核心参数的意义并手把手实现代码。我会使用Python的pandas,statsmodels,scikit-learn,xgboost和prophet等库。3.1 ARIMA模型理解参数与平稳性处理ARIMA(p,d,q)模型包含三个参数p (自回归阶数)表示当前值与前p个历史值的关系。d (差分阶数)为了使序列变得平稳而进行差分的次数。平稳性是ARIMA建模的前提意味着序列的均值和方差不随时间变化。q (移动平均阶数)表示当前误差与前q个历史误差的关系。实操步骤与代码首先我们模拟一个具有趋势的非平稳时间序列。import pandas as pd import numpy as np import matplotlib.pyplot as plt from statsmodels.tsa.stattools import adfuller from statsmodels.graphics.tsaplots import plot_acf, plot_pacf from statsmodels.tsa.arima.model import ARIMA import warnings warnings.filterwarnings(ignore) # 1. 模拟数据趋势 季节性 噪声 np.random.seed(42) time np.arange(365) trend 0.05 * time seasonality 10 * np.sin(2 * np.pi * time / 365) noise np.random.normal(0, 2, 365) series trend seasonality noise 50 # 加一个基准值 dates pd.date_range(2023-01-01, periods365, freqD) df pd.DataFrame({date: dates, value: series}) df.set_index(date, inplaceTrue) # 绘制原始序列 plt.figure(figsize(12, 6)) plt.plot(df.index, df[value], labelOriginal Series) plt.title(Simulated Time Series with Trend and Seasonality) plt.xlabel(Date) plt.ylabel(Value) plt.legend() plt.grid(True) plt.show()第一步检验平稳性Augmented Dickey-Fuller Test# ADF检验 result adfuller(df[value]) print(ADF Statistic: %f % result[0]) print(p-value: %f % result[1]) print(Critical Values:) for key, value in result[4].items(): print(\t%s: %.3f % (key, value)) # 如果p-value 0.05则序列不平稳需要差分。通常模拟的带趋势序列p值会大于0.05表明不平稳。第二步差分使序列平稳# 一阶差分 df[value_diff_1] df[value].diff().dropna() # 再次检验平稳性 result_diff adfuller(df[value_diff_1].dropna()) print(Diff ADF p-value:, result_diff[1]) # 如果p-value 0.05则差分后序列平稳d1。第三步通过ACF和PACF图定阶p和qfig, axes plt.subplots(1, 2, figsize(12,4)) plot_acf(df[value_diff_1].dropna(), lags40, axaxes[0]) plot_pacf(df[value_diff_1].dropna(), lags40, axaxes[1], methodywm) plt.show()ACF图自相关图如果拖尾逐渐衰减到0通常提示需要AR部分。如果截尾在滞后q阶后突然降至不显著提示MA的阶数q。PACF图偏自相关图如果截尾在滞后p阶后突然降至不显著提示AR的阶数p。对于有季节性的序列ACF/PACF会在季节周期如滞后7、14、21天处出现峰值这时需要考虑季节性ARIMA(SARIMA)。第四步拟合ARIMA模型假设我们从图中初步判断p2, d1, q2。# 划分训练集和测试集 train_size int(len(df) * 0.8) train, test df[value].iloc[:train_size], df[value].iloc[train_size:] # 拟合ARIMA(2,1,2)模型 model ARIMA(train, order(2,1,2)) model_fit model.fit() print(model_fit.summary())查看summary()重点关注coef各参数的估计值应显著不为0看P|z|列小于0.05较好。AIC/BIC信息准则用于模型比较值越小越好但要在相同数据集上比较。第五步预测与评估# 进行预测预测长度等于测试集长度 forecast model_fit.forecast(stepslen(test)) forecast_index test.index # 计算评估指标 from sklearn.metrics import mean_absolute_error, mean_squared_error mae mean_absolute_error(test, forecast) rmse np.sqrt(mean_squared_error(test, forecast)) print(fMAE: {mae:.2f}) print(fRMSE: {rmse:.2f}) # 可视化 plt.figure(figsize(12,6)) plt.plot(train.index, train, labelTrain) plt.plot(test.index, test, labelTest, colororange) plt.plot(forecast_index, forecast, labelARIMA Forecast, colorred, linestyle--) plt.title(ARIMA Model Forecast) plt.legend() plt.grid(True) plt.show()实操心得自动定阶在实际中完全依赖看图定阶不靠谱。可以使用pmdarima库的auto_arima函数自动搜索最优(p,d,q)参数组合这是一个非常实用的技巧。残差诊断一个好的ARIMA模型其残差应该类似于白噪声均值为0方差恒定无自相关。可以通过model_fit.plot_diagnostics()来检查。差分陷阱差分次数d不是越大越好。过度差分会使序列方差变大并可能引入不必要的相关性。通常d0,1,2就够了。3.2 随机森林回归特征重要性与抗过拟合随机森林通过构建多棵决策树并集成结果其预测结果是所有树预测值的平均。它的两个关键随机性Bootstrap采样和随机特征子集选择确保了模型的多样性和抗过拟合能力。核心参数解析n_estimators森林中树的数量。越多通常效果越好但计算成本增加边际收益递减。一般从100开始尝试。max_depth单棵树的最大深度。控制模型复杂度是防止过拟合最重要的参数。通常通过交叉验证调优。min_samples_split内部节点再划分所需的最小样本数。值越大树越保守越不容易过拟合。min_samples_leaf叶节点所需的最小样本数。同样用于控制过拟合。max_features寻找最佳分割时考虑的特征数。可以是整数、浮点数比例或‘auto’sqrt(总特征数)。减少此值可以增加树的差异性提升模型鲁棒性。代码实现与特征重要性分析我们使用一个经典的波士顿房价数据集或模拟数据集示例。from sklearn.datasets import fetch_california_housing from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV from sklearn.metrics import mean_absolute_error, r2_score import seaborn as sns # 1. 加载数据 housing fetch_california_housing() X pd.DataFrame(housing.data, columnshousing.feature_names) y housing.target print(f数据集形状: {X.shape}) print(f特征名: {housing.feature_names}) # 2. 划分数据集 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) # 3. 创建并训练随机森林模型使用默认参数作为基线 rf_base RandomForestRegressor(n_estimators100, random_state42, n_jobs-1) rf_base.fit(X_train, y_train) # 4. 基线模型评估 y_pred_base rf_base.predict(X_test) print(f基线模型 - R2 Score: {r2_score(y_test, y_pred_base):.4f}) print(f基线模型 - MAE: {mean_absolute_error(y_test, y_pred_base):.4f}) # 5. 特征重要性分析 feature_importances pd.DataFrame({ feature: housing.feature_names, importance: rf_base.feature_importances_ }).sort_values(importance, ascendingFalse) plt.figure(figsize(10,6)) sns.barplot(ximportance, yfeature, datafeature_importances) plt.title(Random Forest Feature Importances) plt.xlabel(Importance Score) plt.tight_layout() plt.show()特征重要性图能直观告诉我们哪些特征对预测房价贡献最大这本身就是建模分析中的一个重要结论。第六步网格搜索调优关键参数# 定义参数网格 param_grid { n_estimators: [100, 200], max_depth: [10, 20, None], # None表示不限制深度 min_samples_split: [2, 5, 10], min_samples_leaf: [1, 2, 4] } # 创建网格搜索对象使用5折交叉验证 rf RandomForestRegressor(random_state42, n_jobs-1) grid_search GridSearchCV(estimatorrf, param_gridparam_grid, cv5, scoringneg_mean_absolute_error, verbose1, n_jobs-1) grid_search.fit(X_train, y_train) # 输出最佳参数和最佳得分 print(fBest Parameters: {grid_search.best_params_}) print(fBest CV Score (Negative MAE): {grid_search.best_score_:.4f}) # 用最佳模型在测试集上评估 best_rf grid_search.best_estimator_ y_pred_best best_rf.predict(X_test) print(f调优后模型 - R2 Score: {r2_score(y_test, y_pred_best):.4f}) print(f调优后模型 - MAE: {mean_absolute_error(y_test, y_pred_best):.4f})实操心得不要一开始就调参先用默认参数n_estimators100,max_depthNone等跑一个基线模型了解数据的大致难度和模型表现。调参顺序先调n_estimators增加到性能不再显著提升再调max_depth和min_samples_leaf来控制过拟合最后微调max_features。交叉验证是关键一定要使用交叉验证如GridSearchCV来评估参数性能避免在单一训练/测试集上过拟合。特征重要性可解释性随机森林的特征重要性是基于“不纯度减少”的平均值它是一个相对指标。对于高度相关的特征重要性可能会被分散解读时需谨慎。3.3 XGBoost回归梯度提升与精细化调参XGBoosteXtreme Gradient Boosting是梯度提升算法的高效实现。它通过加法模型一组弱学习器通常是决策树来最小化损失函数。每一棵新树都学习去拟合之前所有树组合的残差负梯度。核心参数解析回归任务XGBoost参数分为通用参数、Booster参数和学习任务参数。这里聚焦最关键的几个通用参数n_estimators(或num_boost_round): 提升迭代次数即树的数量。太多容易过拟合。learning_rate(eta): 学习率控制每棵树对最终结果的贡献。越小需要的树越多但通常泛化能力更好。常与n_estimators配合调整。objective: 学习目标回归常用reg:squarederror均方误差。Booster参数树相关max_depth: 树的最大深度。控制模型复杂度防止过拟合。min_child_weight: 子节点所需的样本权重和的最小值。用于控制过拟合值越大模型越保守。subsample: 训练每棵树时使用的数据采样比例。小于1可以引入随机性防止过拟合。colsample_bytree: 训练每棵树时使用的特征采样比例。类似随机森林的max_features。gamma(min_split_loss): 在树的叶节点上进行进一步划分所需的最小损失减少值。值越大算法越保守。正则化参数reg_alpha(L1正则化权重) 和reg_lambda(L2正则化权重)直接对叶子权重进行惩罚是XGBoost防止过拟合的强力武器。代码实现与早期停止import xgboost as xgb from sklearn.metrics import mean_squared_error # 1. 转换为DMatrix格式XGBoost专用效率更高 dtrain xgb.DMatrix(X_train, labely_train) dtest xgb.DMatrix(X_test, labely_test) # 2. 设置基本参数 params { objective: reg:squarederror, learning_rate: 0.1, max_depth: 6, min_child_weight: 1, subsample: 0.8, colsample_bytree: 0.8, seed: 42 } # 3. 训练模型并使用验证集进行早期停止 evals [(dtrain, train), (dtest, eval)] num_rounds 1000 model xgb.train(params, dtrain, num_rounds, evalsevals, early_stopping_rounds50, verbose_eval100) # early_stopping_rounds50 表示如果验证集指标在50轮内没有提升则停止训练。 # verbose_eval100 表示每100轮打印一次评估结果。 # 4. 预测与评估 y_pred_xgb model.predict(dtest) print(fXGBoost - RMSE: {np.sqrt(mean_squared_error(y_test, y_pred_xgb)):.4f}) print(fXGBoost - R2 Score: {r2_score(y_test, y_pred_xgb):.4f}) # 5. 特征重要性可视化基于‘weight’被用作分割点的次数 xgb.plot_importance(model, max_num_features10, importance_typeweight) plt.title(XGBoost Feature Importance (weight)) plt.show()第七步系统化调参策略手动调参效率低。通常采用网格搜索或随机搜索结合交叉验证。由于XGBoost训练较慢可以先进行粗调再细调。from sklearn.model_selection import RandomizedSearchCV # 将数据转换回DataFrame格式用于sklearn API方便使用RandomizedSearchCV xgb_sk xgb.XGBRegressor(objectivereg:squarederror, random_state42, n_jobs-1) # 定义参数分布 param_dist { n_estimators: [100, 200, 500], learning_rate: [0.01, 0.05, 0.1], max_depth: [3, 6, 9], min_child_weight: [1, 3, 5], subsample: [0.7, 0.8, 0.9], colsample_bytree: [0.7, 0.8, 0.9], reg_alpha: [0, 0.1, 1], reg_lambda: [1, 1.5, 2] } # 随机搜索比网格搜索更快探索参数空间 random_search RandomizedSearchCV(estimatorxgb_sk, param_distributionsparam_dist, n_iter50, scoringneg_mean_absolute_error, cv3, verbose1, random_state42, n_jobs-1) random_search.fit(X_train, y_train) print(fBest Parameters from Random Search: {random_search.best_params_}) best_xgb random_search.best_estimator_ y_pred_best_xgb best_xgb.predict(X_test) print(f调优后XGBoost - R2 Score: {r2_score(y_test, y_pred_best_xgb):.4f})实操心得学习率与树的数量这是最重要的权衡。通常建议设置一个较小的学习率如0.01-0.1然后增加n_estimators直到验证误差不再下降。使用early_stopping_rounds可以自动找到合适的迭代次数。树深度与样本/叶子参数max_depth是控制复杂度的首要参数从3-10开始尝试。min_child_weight对于不平衡数据或小数据集很重要。行列采样subsample和colsample_bytree是XGBoost自带的“随机森林”特性能有效防止过拟合通常设置在0.7-0.9。正则化如果模型过拟合迹象明显训练集表现远好于验证集可以尝试增加reg_alpha或reg_lambda。并行计算设置n_jobs-1可以使用所有CPU核心加速训练。4. 模型评估、对比与结果可视化模型建好了预测结果也出来了但工作只完成了一半。如何科学地评估模型如何在论文中优雅地展示结果这是区分普通建模和优秀建模的关键。4.1 多维度评估指标解读不同的指标从不同角度衡量预测误差需根据业务场景选择。MAE (平均绝对误差)mean_absolute_error(y_true, y_pred)含义预测值与真实值绝对差的平均值。特点对异常值不敏感量纲与原始数据相同易于解释。例如房价预测MAE5意味着平均预测误差是5万美元。MSE (均方误差) / RMSE (均方根误差)mean_squared_error(y_true, y_pred) RMSE是其平方根。含义预测误差平方的平均值MSE再开方RMSE。特点对大的误差惩罚更重因为平方。RMSE的量纲也与原始数据相同比MSE更常用。在误差分布接近正态时RMSE是标准差的一个估计。MAPE (平均绝对百分比误差)mean_absolute_percentage_error(y_true, y_pred)含义绝对误差占真实值的百分比的平均值。特点无量纲便于比较不同量级数据集上的模型。但有一个致命缺点当真实值有0或接近0时MAPE会趋于无穷大或变得极不稳定此时慎用。R² (决定系数)r2_score(y_true, y_pred)含义模型解释的数据方差比例。取值范围(-∞, 1]1表示完美拟合0表示模型等于直接用均值预测负数表示模型比均值预测还差。特点无量纲是衡量模型拟合优度的经典指标。但增加无关特征也会使R²轻微上升因此需结合其他指标看。代码示例综合评估from sklearn.metrics import mean_absolute_error, mean_squared_error, mean_absolute_percentage_error, r2_score def evaluate_model(y_true, y_pred, model_name): mae mean_absolute_error(y_true, y_pred) rmse np.sqrt(mean_squared_error(y_true, y_pred)) try: mape mean_absolute_percentage_error(y_true, y_pred) * 100 # 转为百分比 except: mape np.nan # 如果真实值有0则置为NaN r2 r2_score(y_true, y_pred) print(f【{model_name}】评估结果) print(f MAE: {mae:.4f}) print(f RMSE: {rmse:.4f}) if not np.isnan(mape): print(f MAPE: {mape:.2f}%) print(f R²: {r2:.4f}) print(- * 30) return {MAE: mae, RMSE: rmse, MAPE: mape, R2: r2} # 假设我们有三个模型的预测结果y_pred_arima, y_pred_rf, y_pred_xgb results {} # results[ARIMA] evaluate_model(test, y_pred_arima, ARIMA) results[Random Forest] evaluate_model(y_test, y_pred_best, Random Forest (Tuned)) results[XGBoost] evaluate_model(y_test, y_pred_best_xgb, XGBoost (Tuned))4.2 结果可视化让论文图表说话一张好的图表胜过千言万语。在数学建模论文中以下图表至关重要1. 预测效果对比图将真实值序列与多个模型的预测值序列画在一起直观展示拟合和预测效果。plt.figure(figsize(14, 8)) # 假设我们有时序预测的测试集索引 test_dates 和对应的真实值 y_test_ts, 以及各模型预测值 # 这里以回归问题为例我们取测试集的前50个样本点进行细节展示 sample_range slice(0, 50) # 查看前50个测试样本 plt.plot(np.arange(len(y_test[sample_range])), y_test[sample_range], o-, labelTrue Value, linewidth2, markersize6) plt.plot(np.arange(len(y_pred_best[sample_range])), y_pred_best[sample_range], s--, labelRandom Forest Pred, alpha0.8) plt.plot(np.arange(len(y_pred_best_xgb[sample_range])), y_pred_best_xgb[sample_range], ^--, labelXGBoost Pred, alpha0.8) plt.xlabel(Sample Index) plt.ylabel(Target Value) plt.title(Model Predictions vs True Values (First 50 Samples)) plt.legend() plt.grid(True, linestyle--, alpha0.5) plt.tight_layout() plt.show()2. 残差分析图检查残差预测误差是否随机分布。理想的残差应该围绕0上下随机波动无任何模式。fig, axes plt.subplots(1, 2, figsize(12, 4)) # 残差散点图 residuals_rf y_test - y_pred_best axes[0].scatter(y_pred_best, residuals_rf, alpha0.5) axes[0].axhline(y0, colorr, linestyle--) axes[0].set_xlabel(Predicted Values) axes[0].set_ylabel(Residuals) axes[0].set_title(Random Forest Residual Plot) axes[0].grid(True, alpha0.3) # 残差直方图检查是否近似正态分布 axes[1].hist(residuals_rf, bins30, edgecolorblack, alpha0.7) axes[1].set_xlabel(Residuals) axes[1].set_ylabel(Frequency) axes[1].set_title(Distribution of Residuals) axes[1].grid(True, alpha0.3) plt.tight_layout() plt.show()如果残差图呈现漏斗形误差随预测值增大而增大可能需要对目标变量做变换如取对数。如果残差有趋势说明模型有系统性偏差未能捕捉到数据中的某些模式。3. 模型性能对比表格将多个模型的评估指标整理成表格清晰展示优劣。results_df pd.DataFrame(results).T print(results_df) # 更美观的Markdown格式输出可用于论文 print(\n### 模型性能对比表) print(results_df.to_markdown(floatfmt.4f))4. 特征重要性对比图针对树模型如果用了多个树模型可以横向对比它们认为重要的特征是否一致。# 获取两个模型的特征重要性 feat_imp_rf pd.Series(best_rf.feature_importances_, indexhousing.feature_names).sort_values(ascendingFalse) feat_imp_xgb pd.Series(best_xgb.feature_importances_, indexhousing.feature_names).sort_values(ascendingFalse) fig, axes plt.subplots(1, 2, figsize(14, 6)) feat_imp_rf.head(10).plot(kindbarh, axaxes[0], colorskyblue) axes[0].set_title(Top 10 Features - Random Forest) axes[0].set_xlabel(Importance) feat_imp_xgb.head(10).plot(kindbarh, axaxes[1], colorlightcoral) axes[1].set_title(Top 10 Features - XGBoost) axes[1].set_xlabel(Importance) plt.tight_layout() plt.show()5. 数学建模实战从问题到论文的完整链路掌握了模型和代码最终要服务于解决一个具体的数学建模问题。这里我以一个简化的“城市共享单车需求预测”赛题为例串联整个流程。5.1 问题理解与数据预处理假设赛题给定某城市共享单车历史租借数据包括时间、天气、温度、湿度、风速、是否工作日、是否节假日等预测未来一周每小时的租借量。第一步数据探索与清洗# 假设数据已加载为DataFrame df print(df.info()) print(df.describe()) print(df.isnull().sum()) # 处理缺失值根据情况选择填充或删除 df.fillna(methodffill, inplaceTrue) # 用前一个值填充适用于时间序列 # 将时间列转换为datetime格式并提取特征 df[datetime] pd.to_datetime(df[datetime]) df[hour] df[datetime].dt.hour df[day_of_week] df[datetime].dt.dayofweek df[is_weekend] df[day_of_week].apply(lambda x: 1 if x 5 else 0) df[month] df[datetime].dt.month # 可以进一步提取是否为早高峰、晚高峰等业务特征 # 查看目标变量租借量的分布 plt.figure(figsize(10,5)) plt.subplot(1,2,1) df[count].hist(bins50) plt.title(Distribution of Bike Count) plt.subplot(1,2,2) pd.plotting.autocorrelation_plot(df[count].iloc[:500]) # 查看自相关性 plt.title(Autocorrelation) plt.tight_layout() plt.show()第二步特征工程这是提升模型性能的关键。除了从时间戳提取的特征还可以考虑滞后特征将前1小时、前24小时、前一周同一天的租借量作为特征。滑动窗口统计特征过去3小时的平均租借量、过去24小时的最大租借量等。交互特征例如“小时”与“是否周末”的交互因为周末的出行模式与工作日不同。外部特征天气状况晴、雨、雪可以转换为独热编码One-Hot Encoding。# 创建滞后特征示例 df[count_lag1] df[count].shift(1) # 前一小时 df[count_lag24] df[count].shift(24) # 前一天同一小时 # 创建滑动窗口特征示例 df[count_rolling_mean_3h] df[count].rolling(window3, min_periods1).mean().shift(1) # 处理分类变量如天气 df pd.get_dummies(df, columns[weather], prefixweather) # 由于创建了滞后特征前几行会有NaN需要删除 df.dropna(inplaceTrue)5.2 模型构建、集成与结果分析第三步划分数据集注意时间序列不能随机划分必须按时间顺序划分。split_point int(len(df) * 0.8) train_df df.iloc[:split_point].copy() test_df df.iloc[split_point:].copy() # 分离特征和目标 target_col count feature_cols [col for col in df.columns if col not in [datetime, target_col]] X_train, y_train train_df[feature_cols], train_df[target_col] X_test, y_test test_df[feature_cols], test_df[target_col]第四步多模型训练与对比我们可以尝试时间序列模型以count为序列使用Prophet因为它能方便地加入额外的回归量即我们构造的特征。回归模型使用我们构造的所有特征训练随机森林和XGBoost。# 方法1Prophet (需要将数据整理成特定格式) from prophet import Prophet prophet_df train_df[[datetime, count]].rename(columns{datetime: ds, count: y}) # 可以添加额外的回归量这里假设我们有一个‘temperature’特征 prophet_df[temperature] train_df[temp].values model_prophet Prophet() model_prophet.add_regressor(temperature) # 添加额外回归量 model_prophet.fit(prophet_df) # 创建未来数据框进行预测 future_df model_prophet.make_future_dataframe(periodslen(test_df), freqH, include_historyFalse) future_df[temperature] test_df[temp].reset_index(dropTrue) # 提供未来时间的温度 forecast_prophet model_prophet.predict(future_df) y_pred_prophet forecast_prophet[yhat].values # 方法2 3: 随机森林和XGBoost (使用sklearn接口) # ... 训练和预测代码同上文 ...第五步模型集成如果单一模型表现都不错且各有侧重可以考虑简单加权平均或堆叠Stacking。# 简单加权平均集成 weight_rf, weight_xgb, weight_prophet 0.4, 0.4, 0.2 # 权重需要根据验证集表现调整 y_pred_ensemble (weight_rf * y_pred_best weight_xgb * y_pred_best_xgb weight_prophet * y_pred_prophet) # 评估集成模型 evaluate_model(y_test, y_pred_ensemble, Weighted Ensemble)第六步结果分析与论文撰写要点在论文中你需要清晰地呈现问题重述与假设明确预测目标提出你对影响因子天气、时间、历史需求的假设。数据预处理与特征工程用流程图或文字描述清洗和构造特征的过程这是体现你工作量的地方。模型选择与原理简述简要说明为什么选择这几种模型它们的原理和适用性。实验结果这是核心。必须包含评估指标对比表清晰列出各模型在测试集上的MAE、RMSE、R²等。预测效果对比图选择一段时间窗口画出真实值与各模型预测值的曲线。残差分析图证明你的最佳模型残差是随机的没有系统性偏差。特征重要性分析图如果用了树模型说明哪些因素对共享单车需求影响最大这能提升论文的深度。敏感性分析加分项例如改变训练集/测试集划分比例看模型性能是否稳定或者移除某个重要特征观察模型性能下降程度。模型优缺点与推广客观评价你所采用模型的优点和局限性并简要说明模型可以如何应用到其他类似场景。避坑指南与心得数据泄露在构造滞后特征、滑动窗口特征时绝对不能用未来的信息预测过去。所有基于历史构造的特征其值在预测时点都必须是已知的。这就是为什么我们在创建rolling_mean后要.shift(1)。评估陷阱对于时间序列问题必须使用“时间序列交叉验证”TimeSeriesSplit而不是普通的K-Fold以模拟真实的滚动预测场景。Prophet的额外回归量未来期的回归量如预测期的温度需要你额外提供或进行预测这本身就是一个子问题。在实际比赛中如果无法获得未来的天气数据这个特征就无法在预测时使用。论文图表规范图表务必清晰有标题、坐标轴标签、图例。使用对比明显的颜色。重要的结论可以在图表下方用文字简要说明。代码与论文分离论文中只展示核心思路、关键公式和最终结果图表。将完整的、可运行的代码作为附录提交或单独打包。评委可能会运行你的代码来复现结果。
返回列表