MathorCup C题时间序列预测实战:ARIMA与LSTM模型全流程解析
2026/8/27 1:48:51 网站建设 项目流程

1. 项目概述:从赛题到解决方案的完整拆解

又到了一年一度的MathorCup数模竞赛季,对于很多数学建模爱好者和在校学生来说,这既是一场脑力的狂欢,也是一次实战的淬炼。我参加过也指导过不少数模竞赛,深知在有限时间内,从读懂赛题到构建模型,再到写出逻辑清晰的论文和可运行的代码,每一步都充满挑战。今天,我想以2024年MathorCup C题为例,抛开那些泛泛而谈的“竞赛攻略”,深入聊聊一套完整的解题思路、技术选型背后的逻辑,以及那些在官方指导之外、却能决定你论文上限的实操细节。这不仅仅是一份“解析”,更像是我作为过来人,和你一起复盘一次完整的项目实战,分享从数据预处理、模型构建到结果可视化的全流程经验,尤其是如何将ARIMA、LSTM这类时间序列预测模型用得“恰到好处”。

C题通常聚焦于具有实际背景的预测或优化问题,可能涉及金融、交通、能源等领域的时间序列或面板数据。我们的核心目标是利用历史数据,构建稳健的预测模型,以洞察未来趋势并支持决策。这要求我们不仅要掌握模型算法,更要理解问题本质,做好数据的前期“精加工”,并在多个模型间进行科学的对比与融合。接下来,我将从解题的整体框架讲起,逐步深入到每个技术环节的“为什么”和“怎么做”,并附上关键的Python代码片段和避坑指南。

2. 解题整体框架与核心思路解析

面对一个数模赛题,最忌讳的就是拿到数据直接套模型。一个清晰的顶层设计能让你事半功倍。对于预测类赛题,我习惯将其拆解为四个递进阶段:问题定义与数据探查、数据预处理与特征工程、预测模型构建与评估、结果分析与可视化。这个框架确保了分析的系统性和逻辑的连贯性。

2.1 问题定义与数据探查:读懂数据背后的故事

首先,必须彻底理解赛题要求。C题通常会提供一份背景说明和一组数据集。你需要明确:预测目标是什么(例如,明日股价、下月货运量)?数据是单变量时间序列还是多变量面板数据?评价指标是什么(MAE, RMSE, MAPE)?这直接决定了后续的技术路线。

拿到数据后,第一步不是清洗,而是“探查”。使用pandas进行快速概览:

import pandas as pd import numpy as np # 假设数据已加载为df print(df.head()) print(df.info()) print(df.describe())

关键探查点包括:

  1. 时间范围与频率:数据是日度、周度还是月度?时间戳是否连续?是否存在大的缺失时段。
  2. 变量含义:每个字段代表什么?哪些是特征,哪个是目标变量?是否存在分类变量。
  3. 缺失值情况:使用df.isnull().sum()统计缺失值,并观察缺失模式(随机缺失还是连续缺失)。
  4. 异常值初判:通过描述性统计(如describe)查看最大值、最小值、分位数,初步判断是否存在离谱的数值。

注意:很多赛题数据会故意包含一些“陷阱”,如日期格式不统一、存在重复记录、或某些字段在部分时间段含义发生变化。仔细的探查能提前发现这些问题,避免模型建立在一堆“脏数据”上。

2.2 技术路线选型:为什么是ARIMA与LSTM?

在预测模型中,ARIMA和LSTM是两类经典且常被对比的模型。选择它们并非跟风,而是基于其各自的特点和赛题数据的特性。

ARIMA(自回归积分滑动平均模型)是处理单变量线性时间序列的标杆。它的核心思想是将非平稳序列通过差分变为平稳序列,然后用自回归(AR)和滑动平均(MA)模型来拟合。其优势在于模型原理清晰,参数具有可解释性(p, d, q),对于具有明显趋势和季节性的序列,往往能获得稳健的基础预测。在MathorCup这类竞赛中,提供一个ARIMA基准模型,是论文严谨性的体现。

LSTM(长短期记忆网络)是RNN的一种变体,擅长捕捉时间序列中的长期依赖关系复杂非线性模式。与ARIMA相比,LSTM能自然地处理多变量输入,即可以利用多个相关特征共同预测目标变量。这对于赛题中常见的“外部因素影响预测”的场景非常有用。例如,预测货运量时,除了历史货运量,还可以加入油价、节假日指标等作为LSTM的输入特征。

实操中的选型策略

  1. 数据量较小、序列模式相对简单(趋势+季节性):优先考虑ARIMA,它更容易训练和调参,且结果稳定。
  2. 数据量充足、序列包含复杂非线性波动、且存在有用的外部特征:LSTM或更高级的时序网络(如GRU、Transformer)更有优势。
  3. 最稳健的做法:构建模型融合集成策略。例如,用ARIMA捕捉线性趋势,用LSTM捕捉残差中的非线性模式,再将两者结果加权平均。这种方法在竞赛中往往能显著提升预测精度和论文的亮点。

3. 数据预处理与特征工程实战详解

数据预处理的质量直接决定了模型性能的天花板。这一步往往耗费整个项目50%以上的时间,但绝对值得。

3.1 缺失值处理:不仅仅是填充

缺失值处理没有银弹,需要根据缺失机制和业务逻辑判断。

  • 连续少量缺失:对于时间序列,常用前后插值法(df.interpolate(method='time'))或线性插值。
  • 连续大段缺失:如果缺失段较长,插值可能引入巨大误差。此时需要考虑:
    • 是否该时间段数据本就无效(如设备停机)?若是,可考虑剔除该时间段,或将其作为一个特殊的“事件标志”特征。
    • 使用更复杂的方法,如基于其他相关变量的回归预测来填充,但要小心避免数据泄露。
  • 分类变量缺失:通常用众数填充,或单独设为“未知”类别。

3.2 异常值检测与处理:稳健性的关键

异常值可能是真正的极端事件(如黑天鹅),也可能是数据错误。处理方式同样需要谨慎。

  1. 检测方法
    • 统计方法3σ原则(三倍标准差)或IQR(四分位距)法。对于单变量序列,IQR法更稳健。
    Q1 = df['value'].quantile(0.25) Q3 = df['value'].quantile(0.75) IQR = Q3 - Q1 lower_bound = Q1 - 1.5 * IQR upper_bound = Q3 + 1.5 * IQR outliers = df[(df['value'] < lower_bound) | (df['value'] > upper_bound)]
    • 可视化方法:绘制箱线图或时间序列图,直观判断。
  2. 处理方法
    • 保留:如果异常值代表重要事件(如疫情爆发对客流的影响),应保留,甚至可将其编码为哑变量特征。
    • 修正:如果明确是错误(如负的销量),可用前后值插值或盖帽法(用上下限值替换)修正。
    • 删除:仅当异常值极少且明确为错误时考虑。

3.3 特征工程:为模型注入“洞察力”

对于LSTM等多变量模型,特征工程是提升性能的核心。

  • 时间特征:从时间戳中提取年、月、日、星期几、是否周末、是否节假日、季度等。这些能帮助模型捕捉周期模式。
  • 滞后特征:创建目标变量的历史值(lag features),如前1天、前7天、前30天的值。这是时间序列预测最直接有效的特征。
  • 滑动窗口统计特征:计算过去一个窗口期内的均值、标准差、最大值、最小值等。可以反映近期趋势和波动。
  • 外部特征:根据赛题背景引入。例如,预测能源需求时,加入温度、湿度数据;预测销量时,加入促销活动标识。
  • 交互特征:在某些场景下,不同特征的组合可能更有意义,但要注意避免维度爆炸和过拟合。

实操心得:特征不是越多越好。可以先构建一个丰富的特征池,然后使用特征重要性评估(如基于树模型)或递归特征消除(RFE)进行筛选。对于LSTM,要特别注意特征之间的尺度差异,务必进行归一化(如MinMaxScaler),否则会严重影响训练收敛速度和效果。

4. ARIMA模型构建:从原理到调参

4.1 模型原理与定阶

ARIMA(p,d,q)模型包含三个参数:

  • d(积分阶数):使序列平稳所需的差分次数。可通过观察序列图或使用ADF单位根检验确定。通常一次差分即可。
  • p(自回归阶数):观察自相关图(ACF),看截尾或拖尾情况。
  • q(移动平均阶数):观察偏自相关图(PACF)。

在实际竞赛中,我们常使用pmdarima库的auto_arima函数进行自动定阶,它通过信息准则(AIC/BIC)网格搜索最优参数,非常高效。

from pmdarima import auto_arima import warnings warnings.filterwarnings('ignore') # 假设series是预处理后的单变量时间序列 model = auto_arima(series, start_p=0, start_q=0, max_p=5, max_q=5, m=12, # m为季节性周期,月度数据设为12 seasonal=True, # 是否考虑季节性 d=None, trace=True, error_action='ignore', suppress_warnings=True, stepwise=True) # 逐步搜索,加快速度 print(model.summary())

4.2 模型训练与诊断

确定参数后,使用statsmodels库进行精细建模和诊断。

from statsmodels.tsa.statespace.sarimax import SARIMAX # 假设最优参数为 (1,1,1)(0,1,1,12) order = (1, 1, 1) seasonal_order = (0, 1, 1, 12) model = SARIMAX(series, order=order, seasonal_order=seasonal_order) model_fit = model.fit(disp=False) print(model_fit.summary())

关键诊断步骤

  1. 残差分析:理想的残差应近似为白噪声(均值为0,方差恒定,无自相关)。绘制残差图、残差ACF/PACF图,并进行Ljung-Box检验。如果残差存在自相关,说明模型未能完全捕捉序列信息,需要调整参数。
  2. 参数显著性:查看summary中参数的P值,通常要求小于0.05,确保纳入的项是显著的。

4.3 预测与评估

使用拟合好的模型进行样本外预测,并计算评估指标。

# 预测未来n步 forecast_steps = 30 forecast = model_fit.get_forecast(steps=forecast_steps) forecast_mean = forecast.predicted_mean forecast_ci = forecast.conf_int() # 置信区间 # 计算评估指标 (以训练集后部分作为验证集为例) from sklearn.metrics import mean_absolute_error, mean_squared_error def mean_absolute_percentage_error(y_true, y_pred): return np.mean(np.abs((y_true - y_pred) / y_true)) * 100 mae = mean_absolute_error(y_true, y_pred) rmse = np.sqrt(mean_squared_error(y_true, y_pred)) mape = mean_absolute_percentage_error(y_true, y_pred)

注意事项:ARIMA对长期预测的误差会累积放大,因此更适用于短期预测。在论文中,除了给出点预测,务必提供预测置信区间,这能体现你对预测不确定性的考量,是加分项。

5. LSTM模型构建:深度学习的时序应用

5.1 数据准备与序列构造

LSTM要求输入数据为三维张量:[样本数, 时间步长, 特征数]。我们需要将时间序列构造为监督学习的形式。

from sklearn.preprocessing import MinMaxScaler import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout # 1. 归一化 scaler = MinMaxScaler() scaled_data = scaler.fit_transform(df[['target', 'feature1', 'feature2']]) # 2. 构造序列函数 def create_sequences(data, seq_length): X, y = [], [] for i in range(len(data) - seq_length): X.append(data[i:i+seq_length, :]) # 包含所有特征 y.append(data[i+seq_length, 0]) # 假设第一列是目标变量 return np.array(X), np.array(y) SEQ_LENGTH = 60 # 使用过去60个时间步预测下一个 X, y = create_sequences(scaled_data, SEQ_LENGTH) # 3. 划分训练集和测试集(注意保持时间顺序!) split_idx = int(len(X) * 0.8) X_train, X_test = X[:split_idx], X[split_idx:] y_train, y_test = y[:split_idx], y[split_idx:]

5.2 网络结构设计与训练

一个基础的LSTM网络结构如下:

model = Sequential() # 第一层LSTM,设置return_sequences=True以连接下一层LSTM model.add(LSTM(units=50, return_sequences=True, input_shape=(X_train.shape[1], X_train.shape[2]))) model.add(Dropout(0.2)) # Dropout层防止过拟合 # 第二层LSTM model.add(LSTM(units=50, return_sequences=False)) model.add(Dropout(0.2)) # 全连接输出层 model.add(Dense(units=1)) model.compile(optimizer='adam', loss='mean_squared_error') # 训练模型,使用验证集监控 history = model.fit(X_train, y_train, epochs=100, batch_size=32, validation_data=(X_test, y_test), verbose=1, shuffle=False) # 时间序列shuffle必须为False

关键超参数经验

  • LSTM单元数:通常从50开始尝试,根据数据复杂度和计算资源调整。单元数过多易过拟合。
  • 网络层数:1-3层通常足够。更深不一定更好,反而会增加训练难度和过拟合风险。
  • Dropout率:0.2到0.5之间,是有效的正则化手段。
  • 优化器Adam是默认且效果良好的选择。
  • 早停(EarlyStopping):务必使用!监控验证集损失,当其不再下降时停止训练,防止过拟合。
from tensorflow.keras.callbacks import EarlyStopping early_stop = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True) history = model.fit(..., callbacks=[early_stop])

5.3 预测、反归一化与评估

训练完成后,进行预测并记得将数据反归一化回原始尺度。

# 预测 y_pred_scaled = model.predict(X_test) # 反归一化(需要构造与输入形状匹配的数据进行逆变换) # 注意:需要将预测值与其他特征值拼接成与原始数据相同的形状 dummy_features = np.zeros((len(y_pred_scaled), scaled_data.shape[1])) dummy_features[:, 0] = y_pred_scaled.flatten() y_pred = scaler.inverse_transform(dummy_features)[:, 0] # 同样处理y_test dummy_test = np.zeros((len(y_test), scaled_data.shape[1])) dummy_test[:, 0] = y_test.flatten() y_true = scaler.inverse_transform(dummy_test)[:, 0] # 计算评估指标 mae_lstm = mean_absolute_error(y_true, y_pred) rmse_lstm = np.sqrt(mean_squared_error(y_true, y_pred)) mape_lstm = mean_absolute_percentage_error(y_true, y_pred)

6. 模型对比、融合与结果分析

6.1 科学的模型对比

将ARIMA和LSTM的预测结果在同一个测试集上进行对比。不仅要看MAE、RMSE、MAPE等点估计指标,还要通过可视化进行直观比较。

import matplotlib.pyplot as plt plt.figure(figsize=(12,6)) plt.plot(y_true, label='Actual', linewidth=2) plt.plot(y_pred_arima, label='ARIMA Forecast', linestyle='--') plt.plot(y_pred_lstm, label='LSTM Forecast', linestyle='-.') plt.fill_between(range(len(y_true)), forecast_ci.iloc[:,0], forecast_ci.iloc[:,1], color='gray', alpha=0.3, label='ARIMA CI') plt.legend() plt.title('Model Comparison on Test Set') plt.xlabel('Time Step') plt.ylabel('Value') plt.show()

分析可能出现的几种情况:

  1. ARIMA表现更好:说明序列线性成分强,或数据量不足以训练复杂的LSTM。
  2. LSTM表现更好:说明序列中存在非线性、复杂模式,且特征工程有效。
  3. 两者各有优劣:可能ARIMA在平稳段表现好,LSTM在波动段捕捉能力强。这引出了模型融合的思路。

6.2 模型融合策略

简单的融合策略是加权平均。更高级的做法是使用Stacking

  1. 将ARIMA和LSTM的预测结果作为新的特征(元特征)。
  2. 使用一个简单的元模型(如线性回归、岭回归)在验证集上学习如何组合这两个预测。
  3. 用训练好的元模型对最终的测试集预测进行组合。
from sklearn.linear_model import Ridge # 假设在验证集上有 pred_arima_val, pred_lstm_val, y_val X_meta_val = np.column_stack((pred_arima_val, pred_lstm_val)) meta_model = Ridge(alpha=1.0) meta_model.fit(X_meta_val, y_val) # 在测试集上融合 X_meta_test = np.column_stack((y_pred_arima, y_pred_lstm)) y_pred_fused = meta_model.predict(X_meta_test)

融合后的模型往往能结合两者的优点,获得更稳定、更精准的预测,这在竞赛论文中是强有力的亮点。

6.3 结果分析与论文写作要点

在论文的“结果分析”部分,不能只罗列数字,要深入解读:

  • 趋势洞察:你的模型预测出了什么趋势?上升、下降还是周期性波动?这个趋势与背景知识是否吻合?
  • 关键拐点:模型是否成功预测了重要的转折点?如果没有,原因可能是什么?(例如,出现了未在历史数据中出现过的新事件)。
  • 不确定性量化:除了点预测,你如何描述预测的不确定性?(置信区间、预测误差分布)。
  • 模型局限性:诚实地讨论你模型的假设和可能失效的场景。例如:“本模型假设未来模式与历史相似,若发生重大政策变化或黑天鹅事件,预测可能需要调整。” 这体现了批判性思维。

7. 完整代码结构与技术文档撰写指南

7.1 可复现的代码结构

一个清晰的项目结构能让评委和读者一目了然。建议按如下方式组织:

MathorCup2024_C题/ ├── data/ │ ├── raw/ # 原始数据 │ └── processed/ # 处理后的数据 ├── src/ │ ├── 01_data_exploration.ipynb │ ├── 02_preprocessing_feature_engineering.ipynb │ ├── 03_arima_modeling.ipynb │ ├── 04_lstm_modeling.ipynb │ ├── 05_model_fusion_evaluation.ipynb │ └── utils.py # 自定义函数(如评估指标、序列创建函数) ├── models/ # 保存训练好的模型文件(.pkl, .h5) ├── results/ │ ├── figures/ # 生成的所有图表 │ └── forecasts/ # 最终的预测结果文件 ├── requirements.txt # 项目依赖包列表 └── README.md # 项目说明文档

使用Jupyter Notebook时,要确保代码单元格有清晰的Markdown注释,说明每一步的目的。关键步骤后应有可视化输出或结果打印。

7.2 技术文档(论文)核心章节撰写

数模竞赛论文有其固定的格式,但核心在于将你的工作清晰、有逻辑地呈现。

  1. 问题重述与分析:用自己的话精炼概括问题,并分析其特点(时序、预测、多变量等)。
  2. 模型假设与符号说明:列出合理的假设,定义全文使用的关键符号。
  3. 数据分析与预处理:展示数据探查结果(配图),详细说明处理缺失值、异常值、特征工程的方法及理由
  4. 模型建立
    • ARIMA模型:说明平稳性检验、定阶过程(附ACF/PACF图或auto_arima结果)、模型诊断(残差分析图)。
    • LSTM模型:说明网络结构图(可用plot_model绘制)、输入输出定义、超参数选择依据、训练过程(附损失曲线图)。
    • 模型融合:阐述融合策略和原理。
  5. 模型求解与结果:展示预测结果对比图、各种评估指标对比表格。对结果进行深入分析,而不仅仅是展示。
  6. 模型评价与推广:总结模型的优缺点,提出改进方向(例如,尝试Transformer、引入注意力机制等),并讨论模型在其他类似场景的应用可能性。
  7. 参考文献与附录:规范引用,附录可包含核心代码片段。

避坑指南:论文中所有的图表都必须有编号和标题,并在正文中引用。避免出现大段未解释的代码。公式使用LaTeX格式规范编写。保持全文术语一致。

8. 常见问题与排查技巧实录

在实际操作中,你一定会遇到各种报错和不如预期的结果。这里记录一些典型问题的解决思路。

8.1 ARIMA模型常见问题

问题1:auto_arima运行非常慢或内存溢出。

  • 原因:参数搜索空间(max_p,max_q,max_d)设置过大,或季节性周期m设置不当。
  • 解决
    1. 先通过观察序列图和ADF检验,手动确定一个大概的d(通常0,1,2),缩小d的搜索范围。
    2. 对于非明显季节性数据,将seasonal设为False
    3. 启用stepwise=True(默认),它采用逐步搜索策略,比网格搜索快得多。
    4. 如果数据量很大,可以考虑先使用一个子样本进行参数寻优。

问题2:ARIMA模型拟合后预测值是一条直线或常数。

  • 原因:可能是差分阶数d过高,导致序列被过度差分,失去了内在趋势;或者pq的值都为0,模型退化为随机游走。
  • 解决:检查model.summary()中的参数值。尝试降低d,或确保pq至少有一个不为零。重新审视ACF/PACF图。

问题3:残差检验未通过(Ljung-Box检验p值小)。

  • 原因:模型未能充分捕捉序列中的自相关信息,残差不是白噪声。
  • 解决:尝试增加pq的阶数。或者,考虑使用包含季节性的SARIMA模型(seasonal_order参数)。如果仍不理想,可能意味着数据中存在非线性成分,需要考虑LSTM等模型。

8.2 LSTM模型常见问题

问题1:模型训练损失不下降,或预测结果几乎是均值。

  • 原因
    1. 学习率问题:学习率可能不合适。
    2. 梯度消失/爆炸:LSTM虽然缓解了梯度消失,但仍可能发生。
    3. 数据未归一化:这是最常见的原因!不同特征量纲差异巨大。
    4. 网络结构太简单或太复杂:对于复杂模式,网络能力不足;对于简单模式,网络可能过拟合。
  • 排查与解决
    1. 检查数据:确认输入数据已归一化。可视化输入序列,看是否合理。
    2. 调整学习率:尝试使用Adam默认学习率,或使用学习率调度器(ReduceLROnPlateau)。
    3. 梯度裁剪:在model.compile时,为优化器添加clipnormclipvalue参数。
    4. 调整网络:增减LSTM层数或单元数。增加Dropout率防止过拟合,减少Dropout率或增加网络容量防止欠拟合。
    5. 检查激活函数:输出层如果是回归问题,通常使用线性激活函数(默认)。

问题2:验证集损失在训练后期开始上升。

  • 原因:典型的过拟合。
  • 解决
    1. 早停(EarlyStopping):这是必须的。设置合理的patience参数。
    2. 增加正则化:增大Dropout率,或在LSTM层中添加kernel_regularizer
    3. 获取更多数据:在竞赛中可能有限,但可以思考数据增强方法,如通过滑动窗口生成更多训练样本(但需注意样本独立性)。
    4. 简化模型:减少LSTM单元数或层数。

问题3:预测结果相比真实值有滞后(相位偏差)。

  • 原因:LSTM有时会学习到一种“平滑”的模式,导致预测峰谷值滞后于真实值。这在金融序列预测中常见。
  • 解决
    1. 修改目标:不预测下一个时间点的绝对值,而是预测变化量(差分值)。
    2. 多步预测策略:采用递归预测(用上一个预测值预测下一个)容易累积误差导致滞后。可以尝试使用Seq2Seq结构直接输出多个时间步,或使用“多对一”但预测更远的未来点(需调整序列构造方式)。
    3. 引入注意力机制:帮助模型更关注最近的关键时间点,缓解滞后。

8.3 通用与流程问题

问题:评估指标在训练集和测试集上差异巨大。

  • 原因:数据划分不合理,存在时间泄露;或模型严重过拟合。
  • 解决绝对禁止在时间序列中随机划分数据集!必须按时间顺序划分(前80%训练,后20%测试)。确保在特征工程(如计算滑动平均)时,只使用历史信息,不能使用未来信息。

问题:代码在本地运行良好,但交给别人或换环境报错。

  • 解决:使用requirements.txtenvironment.yml严格记录包版本。关键包包括:pandas,numpy,statsmodels,pmdarima,scikit-learn,tensorflow/pytorch,matplotlib。在代码开头设置随机种子,确保可复现性。
import numpy as np import tensorflow as tf import random np.random.seed(42) tf.random.set_seed(42) random.seed(42)

最后,我想分享一点个人体会:数模竞赛的魅力不在于使用了多么高深的模型,而在于完整地展现从实际问题到数学解决方案的思维过程。ARIMA和LSTM只是工具,真正的核心是你的分析逻辑、对数据的深刻理解以及将结果清晰传达的能力。在有限的时间里,一个逻辑清晰、处理严谨、分析深入的“朴素”方案,远胜过一个漏洞百出的“复杂”模型堆砌。希望这份结合了原理、代码与实战经验的解析,能为你接下来的竞赛或项目提供一条清晰的路径。在实际动手时,多画图、多思考、多尝试,你会对时间序列预测有更深刻的领悟。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询