流感时间序列预测:ARIMA、LSTM与Transformer对比实战

发布时间:2026/10/25 12:38:24
流感时间序列预测:ARIMA、LSTM与Transformer对比实战 简介一份基于ARIMA、LSTM、Transformer等模型的流感时间序列预测Python源码项目面向正在做课程设计或期末大作业的计算机相关专业学生也适合需要项目实战练习的学习者。项目经导师指导并获认可为98分高分作业涵盖数据预处理、平稳性检验ADF、ACF/PACF、ARIMA/SARIMA定阶、残差分析、多模型对比与预测输出的完整流程可直接参考用于结课报告或答辩演示。压缩包共25个文件、4.93MB其中7个csv和4个xls提供数据与中间表格7个py脚本覆盖差分、ADF检验、ACF/PACF分析、参数估计、残差、对比和预测等环节2个ipynb便于交互式复现另有5个zip为压缩归档。目前已有387人学习下载代码模块清晰便于按需修改既能理解多个时序模型的实际衔接也能替换数据跑通预测是课程设计与时间序列建模入门的实用素材。1. 流感时间序列预测为什么要同时用 ARIMA、LSTM、Transformer 三种模型流感周报发布通常滞后一周以上医院药房备货、急诊排班、疾控研判却需要提前两周预判高峰。把流感样病例百分比ILI%这类周度序列同时交给 ARIMA、LSTM、Transformer 三种模型去预测就是这个 Python 源码项目的核心内容。它解决的不是「哪个模型更高级」的学术问题而是「在真实小样本周数据上怎么拿到可靠的多周预测值」的落地问题。这类高分项目源码通常包含数据预处理、三个独立模型脚本和结果对比适合疾控数据从业者、公共卫生方向学生以及想用时间序列预测但手里只有每周一条数据的人。一个反直觉的结论先放在这在真实流感周报上ARIMA 往往不输给深度学习模型三种模型组合起来才是真正的拿分点。2. 流感数据预处理把周报切成模型能吃的监督样本拿到流感时间序列预测源码后第一件事不是跑模型而是先把数据搞清楚。流感周报和股票分钟线、气象小时数据完全不同它一年只有 52 条十年才 520 条而且有强烈的年度周期和冬季双峰。不做预处理就把序列丢给神经网络训练出来的东西基本都是上一周数值的平移没有任何预测价值。2.1 ILI 周报的字段与时序特性常见的流感监测周报会记录每周的流感样病例就诊数、ILI 百分比和阳性检出率。核心字段一般就三个字段含义典型范围week_date统计周的截止日期每年 52/53 周ili_percent流感样病例占门急诊比例1%8%positive_rate流感病毒检测阳性率0%40%拿到 CSV 后先做两件事补缺失值、查平稳性。公卫周报偶尔会跳一周直接删行会破坏时间间隔所以我一般用线性插值两个相邻周之间差几天关系不大。平稳性用 ADF 检验p 值大于 0.05 就说明序列非平稳需要差分。import pandas as pd from statsmodels.tsa.stattools import adfuller df pd.read_csv(flu_weekly.csv, parse_dates[week_date]) df df.set_index(week_date).sort_index() df[ili_percent] df[ili_percent].interpolate(methodlinear) def adf_report(series, name): res adfuller(series.dropna(), autolagAIC) print(f{name}: ADF{res[0]:.3f}, p{res[1]:.5f}) return res[1] 0.05 adf_report(df[ili_percent], 原始序列) df[diff_1] df[ili_percent].diff(1) adf_report(df[diff_1], 一阶差分)diff(1)是当前周减上周的差值是最便宜的去趋势手段。流感序列做完一阶差分后 p 值通常会低于 0.05但注意别高兴太早一阶差分会把周期信号的一部分也吃掉后面第 5 章会专门讲这个坑。这里autolagAIC表示 ADF 检验自动按 AIC 选滞后阶数比你手写定滞后阶数更稳。2.2 滑窗构造监督样本时序切分与最小数据量神经网络不能直接吃一维时间序列需要把序列切成「用历史 52 周预测未来 1 周」的监督样本。窗口长度选 52 不是拍脑袋流感有完整年度周期窗口必须覆盖一个完整周期模型才看得到「去年这个时候在涨还是跌」。import numpy as np from sklearn.preprocessing import MinMaxScaler def make_windows(values, seq_len52, horizon1): X, y [], [] for i in range(len(values) - seq_len - horizon 1): X.append(values[i:i seq_len]) y.append(values[i seq_len:i seq_len horizon]) return np.array(X, dtypenp.float32), np.array(y, dtypenp.float32) values df[ili_percent].to_numpy() scaler MinMaxScaler() scaled scaler.fit_transform(values.reshape(-1, 1)).flatten() X_all, y_all make_windows(scaled, seq_len52, horizon1) split int(len(X_all) * 0.8) X_train, X_test X_all[:split], X_all[split:] y_train, y_test y_all[:split], y_all[split:]这段代码里有两个容易被忽略的参数。seq_len52是回看窗口设小了模型看不到年周期设大了样本数骤减horizon1是最简设置预测未来 1 周后面要预测未来 14 周门诊压力时把它改成 14 就行。切分比例 0.8 按时间顺序切绝对不能像图像分类那样随机打乱否则测试集里混着训练集期间未来的数据指标会好看得离谱这在第 5.1 节展开。提示流感序列建议至少留 8 年完整周数据少于 5 年神经网络的样本量连一个完整周期都覆盖不了建议直接用 ARIMA。3. 跑通 ARIMA用 statsmodels 定阶、拟合与滚动预测ARIMA 在流感时间序列预测里不是陪跑角色。它参数少、可解释、有置信区间是检验一切深度学习模型的 baseline。问题是定阶这步做不好后面全白搭。不少新手拿auto_arima一键跑完就交差结果预测曲线是一条水平直线——大概率是差分阶数定多了。3.1 定阶ACF/PACF 粗定AIC/BIC 精修ARIMA(p,d,q) 里p 是自回归阶数d 是差分次数q 是移动平均阶数。流感周报有强年度季节性和冬季双峰12 月一个小峰、2 月一个主峰直接上普通 ARIMA 残差里会残留明显周期这时应该使用带季节项的 SARIMA记作 SARIMA(p,d,q)(P,D,Q,m)m 是周期长度周数据就取 52。from statsmodels.graphics.tsaplots import plot_acf, plot_pacf import matplotlib.pyplot as plt plot_acf(df[ili_percent].diff(1).dropna(), lags40) plot_pacf(df[ili_percent].diff(1).dropna(), lags40) plt.savefig(acf_pacf.png)看这两个图有个经验ACF 在滞后 52 周附近出现显著峰值说明年度季节性没被处理干净这时候应该用 SARIMA 而不是继续加大 p。图看完后用auto_arima在小范围内扫描阶数比手动试快得多。from pmdarima import auto_arima model auto_arima( df[ili_percent], seasonalTrue, m52, start_p0, max_p3, start_q0, max_q3, start_P0, max_P1, start_Q0, max_Q1, d1, D1, traceTrue, error_actionignore, suppress_warningsTrue, stepwiseTrue ) print(model.order, model.seasonal_order)这里最关键的参数是m52和D1。m52告诉模型周期是 52 周D1对季节分量做一次差分把「去年同周」的水平差异抹掉。stepwiseTrue会先做条件搜索再用精确似然估计速度比穷举快很多流感这种小样本序列跑起来几秒就出结果。注意pmdarima不是statsmodels自带的需要单独pip install pmdarima。选完阶数后我一般会再回到statsmodels重新拟合一次目的是拿到标准残差诊断图确认残差里没有明显的自相关。AIC 和 BIC 的选择其实是个小权衡AIC 更偏向预测精度BIC 更偏向模型简洁。流感样本量小BIC 容易把模型压到过于简单我通常 AIC 为主两个候选 AIC 差不到 2 时选阶数低的那一个。3.2 预测滚动一步回填 vs 直接外推 14 周ARIMA 的预测有两种模式。一种是直接predict(n_periods14)外推未来 14 周误差会随着步数快速变大另一种是滚动一步预测——每预测完一周就把这一周的真实值塞回历史再预测下一周。后者在评估模型能力时更接近实战因为流感监测的真实场景中新一周的数据每周都会发布你永远可以用最新真实值更新模型输入。n_train 400 history list(df[ili_percent].iloc[:n_train]) preds [] for step in range(14): model auto_arima( history, seasonalTrue, m52, d1, D1, stepwiseTrue, error_actionignore, suppress_warningsTrue ) pred model.predict(n_periods1)[0] preds.append(pred) actual df[ili_percent].iloc[n_train step] history.append(actual) # 用真实值回填模拟每周拿到新数据上面这段是滚动一步预测的核心写法。注意history.append(actual)加的是真实值不是预测值这模拟的是「每周拿到最新周报后重新预测」的生产过程。如果你把actual换成pred就是递归预测误差会被自己一步步放大预测 14 周后曲线常常直接飘飞。实际部署时真实值还没出那就退化成直接外推两种模式要清楚自己在评估什么。评估 ARIMA 时还有个加分项它自带置信区间预测未来第 4 周高峰时可以顺带给出上下界这在医院备货场景里比点预测值实用得多。4. LSTM 与 Transformer同一套数据管线下的训练与对比神经网络部分是这个流感时间序列预测源码里最容易让新手黑匣子化的地方。很多人直接把别人图像分类的模型结构抄过来训几个 epoch 发现 loss 不降就开始怀疑数据有问题。其实流感周报这种小样本单变量序列用不到复杂网络LSTM 两个隐藏层够用Transformer 一个编码器也够用重点全在防过拟合和调学习率上。4.1 LSTM52 周全窗口输入两个隐藏层就够LSTM 处理流感序列的优势是能记住「去年冬季的走势」但记住太多就是过拟合。我用 PyTorch 写一个最小可跑的 LSTM 模型import torch import torch.nn as nn class LSTMFlu(nn.Module): def __init__(self, input_size1, hidden_size64, num_layers2): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue) self.fc nn.Linear(hidden_size, 1) def forward(self, x): out, _ self.lstm(x) return self.fc(out[:, -1, :]) model LSTMFlu() optimizer torch.optim.Adam(model.parameters(), lr1e-3) loss_fn nn.MSELoss() X_train_t torch.tensor(X_train).unsqueeze(-1) y_train_t torch.tensor(y_train) for epoch in range(80): model.train() optimizer.zero_grad() pred model(X_train_t) loss loss_fn(pred, y_train_t) loss.backward() optimizer.step() if epoch % 20 0: print(epoch, loss.item())输入张量的形状是(样本数, 52, 1)batch_firstTrue把 batch 放到第一维out[:, -1, :]取最后一个时间步的隐藏状态映射到预测值这是序列到点的标准写法。hidden_size64对一个 520 条左右的周序列足够了调到 256 必过拟合学习率 1e-3 是 Adam 的常用起点如果 loss 在 0.001 量级震荡就降到 3e-4。80 轮是我习惯的起点流感数据太少了真正训练时用验证集 loss 做早停连续 10 轮不降就停。4.2 Transformer单变量短序列要改位置编码和 dropouttransformer 在流感时间序列预测里不是拿来炫技的。很多人看 transformer 预测正弦数据的 demo 很漂亮以为搬到流感周报上一样丝滑结果训完画出来是一条贴近均值的直线。原因在于正弦数据低噪声、周期确定流感周报信噪比低、高峰位置每年漂移。用 Transformer 做这类短序列重点是让它不要过拟合到最近几周的历史模式上。import torch.nn as nn class TransformerFlu(nn.Module): def __init__(self, d_model64, nhead4, num_layers2, dropout0.2): super().__init__() self.input_proj nn.Linear(1, d_model) self.pos nn.Parameter(torch.randn(1, 52, d_model) * 0.02) encoder_layer nn.TransformerEncoderLayer( d_modeld_model, nheadnhead, dim_feedforward128, dropoutdropout, batch_firstTrue ) self.encoder nn.TransformerEncoder(encoder_layer, num_layersnum_layers) self.fc nn.Linear(d_model, 1) def forward(self, x): x self.input_proj(x) x x self.pos out self.encoder(x) return self.fc(out[:, -1, :])和 NLP 里常见的 transformer 模型详解不同这里只用编码器没有解码器因为流感预测是「历史 52 周 → 未来 1 个点」的序列到点回归不是序列到序列生成。三个关键改法self.pos用可学习位置编码而不是固定的 sin/cos短序列上可学习编码更灵活dropout0.2是为小样本故意调大的默认 0.1 在几百条样本上过拟合速度肉眼可见nhead4不要贪多单变量序列里本来就没有多少独立信息可拆8 个头纯粹增加过拟合风险。4.3 对比评估RMSE 和 MAPE 决定要不要上深度学习三个模型跑完后对比表按下面这个格式整理横轴是模型纵轴是测试集指标。常见的分布大致是模型训练耗时测试 RMSE 参考范围适用判断SARIMA秒级0.100.20必选 baseline小样本首选LSTM分钟级0.150.35数据量 500 且 ARIMA 残差明显时用Transformer分钟级0.200.40样本少于 500 不建议用这个表里的数字是示意范围不同地区流感强度差异很大不要拿它当绝对值。我的判断逻辑是如果 SARIMA 的测试 RMSE 已经低于 0.15深度学习带来的提升很可能不足以抵消调参成本那就不必上。只有当残差里明显还有周期性高峰没被 SARIMA 捕获时才值得把 LSTM 作为残差修正器接在后面。5. 实战避坑流感预测最容易翻车的 5 个细节这一章写的是我处理这类时序项目时真实踩过的坑每条都按「现象 → 原因 → 解决」记录比任何调参技巧都值钱。5.1 随机切分数据集测试集指标虚高到离谱现象测试集 RMSE 只有 0.03画图一看预测曲线和真实曲线几乎重合但换到下一个流感季的周报上预测立刻崩掉。原因时间序列不能像图像那样随机打乱切分。随机切分会把第 300 周的样本混进训练集、第 301 周混进测试集模型在训练时已经见过「紧挨着」的未来信息这属于标签泄漏。解决严格按时间顺序切分训练集只能是时间上靠前的部分测试集必须是时间上靠后的部分。用第 2.2 节的X_all[:split]和X_all[split:]写法切完后再检查一次训练集最大日期是否早于测试集最小日期。5.2 MinMaxScaler 用全量数据拟合反归一化后预测值整体失真现象LSTM 训练时 loss 很低反归一化后预测曲线形状对得上但数值整体偏高或偏低 0.3 到 0.5 个百分点。原因先对全量序列fit_transform再切训练测试集scaler 已经偷看了测试集的取值范围。测试集的上下限被提前告知模型反归一化时测试期的极值会整体拉偏数值。解决先按时间切分原始数值序列再在训练集上scaler.fit()然后用同一个 scaler 分别transform训练集和测试集。测试集的反归一化也只依靠训练时记录的 min 和 max这才是无损的流程。5.3 多步预测时递归把预测值当输入误差逐周爆炸现象预测未来第 1 周很准第 2 周开始偏第 6 周以后直接变成一条缓坡直线真实高峰完全看不到。原因LSTM 和 ARIMA 在递归预测时每一步的输入里都混着上一步的预测误差误差会像滚雪球一样累积。这不是模型坏了是评估方式错了。解决评估阶段用第 3.2 节的滚动回填方式每周用真实值更新历史。如果部署场景确实拿不到未来真实值那就接受「递归预测的多周结果只有趋势参考价值」这个事实别指望它精确复现高峰数值。5.4 ARIMA 差分阶数设过头周期信息被差没了现象auto_arima跑出来 d2 甚至 d3预测结果是一根贴近均值的平线置信区间宽到毫无意义。原因流感序列本身有年度周期做一次差分去掉趋势就够了。差分阶数太多会把季节性周期差成白噪声模型只学到「均值回归」自然输出水平线。解决把d和D固定不要放任自动搜索。我的习惯是 d1、D1m52然后看残差 ACF 图确认 52 阶附近没有显著峰。如果仍有峰优先调 P 和 Q而不是继续加大差分阶数。5.5 Transformer 在短序列上 loss 震荡训练到 50 轮还降不下来现象Transformer 的 loss 曲线像锯齿0.01 和 0.02 之间来回跳学习率调小后反而完全不降了。原因流感数据量太少Transformer 的加性位置编码和全连接层在样本不足时会互相干扰常见学习率 1e-3 对它来说偏大注意力层的梯度震荡剧烈。解决先用 3e-4 甚至 1e-4 的学习率加 100 步 warmup前 100 步学习率从 0 线性升到目标值。另外把num_layers从默认 6 层压到 2 层dim_feedforward从 2048 压到 128。Transformer 在小数据上根本不是「越大越好」是越小越稳。6. 把模型组合起来残差修正与滚动验证单模型跑通只是完成了这个流感时间序列预测源码的一半它真正拿高分的地方在组合策略上。6.1 残差组合ARIMA 抓线性主峰LSTM 修正非线性残差我的做法是让 ARIMA 先预测流感序列的主趋势和年度季节峰然后把真实值与 ARIMA 预测值的差作为残差序列交给 LSTM 去拟合最后把两者的预测结果相加。这样 ARIMA 负责解释「每年冬天都会涨」LSTM 只负责解释「今年 12 月的峰为什么比去年提前两周」。residuals df[ili_percent] - sarima_pred residual_scaled scaler_resid.fit_transform(residuals.values.reshape(-1, 1)).flatten() X_resid, y_resid make_windows(residual_scaled, seq_len52, horizon1)残差序列的均值通常在 0 附近LSTM 学起来比直接学原始峰值容易得多。组合预测值就等于 SARIMA 预测加 LSTM 的残差预测训练目标也从「猜准 5.5% 的峰值」变成「猜准 0.3% 的偏差」。6.2 滚动验证用连续 4 周的预测窗口检验稳定性验证组合模型时我不用单次切分而是每移动 4 周重新训练一次记录连续 4 个测试窗口的 RMSE。如果组合模型的 RMSE 比单独 SARIMA 的 RMSE 提升不到 5%我一般直接放弃深度学习部分保留 ARIMA 加置信区间作为最终交付。这是个血泪教训别为了向别人展示技术栈而组合模型不值得。行为准则就是「先跑基线再加增量没有增量就停」。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询