news 2026/9/23 2:40:16

流感时间序列预测实战:ARIMA、LSTM与Transformer对比及残差混合建模

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
流感时间序列预测实战:ARIMA、LSTM与Transformer对比及残差混合建模

简介:这份Python源码项目围绕流感时间序列预测展开,整合ARIMA、SARIMA、LSTM与Transformer等多类模型,面向计算机相关专业正在做课程设计、期末大作业或需要项目实战练习的学习者,帮助其完成从数据平稳性检验、差分处理到模型估计、残差分析与多模型对比的完整建模流程。资源包共25个文件,包含7个py脚本、7个csv数据集、4个xls表格、2个ipynb笔记本及5个zip压缩文件,整体约4.93MB,脚本与笔记本对应建模各阶段,数据文件支撑训练与验证,结构清晰便于按模块查阅。目前已有387人学习下载,可作为高分项目参考。读者可据此掌握时间序列预测的完整赛题方案,理解ARIMA与深度学习模型的组合思路、评估对比方法及排错要点,适合作为课程设计模板或实战练习素材。

1. 从一份 98 分课设拆开看:流感时间序列预测到底在预测什么

流感数据不是股票,也不是电商销量,它有两个很讨厌的性质:强季节性(北半球冬春高发)和强噪声(上报延迟、就诊行为变化)。ILINet.csv 这类数据通常按周采样,一条曲线里同时混着趋势、年周期和随机扰动。直接丢给 LSTM 让它端到端学,往往在验证集上看着还行,一到真实外推就崩。这份源码的价值不在于模型多花哨,而在于它把 ARIMA 这条统计基线、LSTM 这条深度基线、Transformer 这条注意力基线放在同一条流水线上对比,并且把平稳性检验、差分、ACF/PACF 定阶这些"老派但必要"的步骤单独拆成脚本。适合正在做课程设计、期末大作业,或者想拿一个完整时间序列项目练手的人。目录结构里1_1_Stationarity_differencing.py5_forecast.py是 ARIMA 主线,lstm-flu.ipynbsarima _v3.ipynb是两条对照线,4_compare.py负责把结果拉到一起。下面按"数据怎么变成可建模的序列 → ARIMA 怎么定阶 → LSTM/Transformer 怎么搭 → 怎么对比和排错"的顺序拆。

2. 数据预处理与平稳性检验:差分、ADF、ACF/PACF 三件套

2.1 ILINet.csv 的字段与建模目标

拿到data/ILINet.csv先别急着画图,先确认列名和采样频率。CDC 的 ILINet 数据一般包含DATE% WEIGHTED ILI%UNWEIGHTED ILIAGE 0-4等列,建模通常选% WEIGHTED ILI作为目标序列,因为它按人口加权,比未加权更稳定。加载时把日期列解析成索引,并强制按周频率重采样,避免中间缺周导致差分错位。

import pandas as pd import numpy as np # 读取 ILINet 数据,跳过表头里的说明行(不同年份文件行数不同,按实际调整) df = pd.read_csv("data/ILINet.csv", skiprows=1) df.columns = [c.strip() for c in df.columns] # 只保留全国汇总行,避免各州数据混入 df = df[df["REGION TYPE"] == "National"].copy() df["DATE"] = pd.to_datetime(df["DATE"]) df = df.set_index("DATE").sort_index() # 目标序列:加权 ILI 百分比 ts = df["% WEIGHTED ILI"].astype(float) ts = ts.asfreq("W") # 强制周频率 ts = ts.interpolate() # 少量缺周用线性插值补 print(ts.head(), ts.shape)

skiprows=1是因为原始 CSV 第一行常是元信息;REGION TYPE == "National"过滤掉州级行,否则同一周会有几十条记录,差分后完全没意义;asfreq("W")把不规则日期对齐到周,interpolate()只补少量缺口,缺口太多要回头查数据源而不是硬插。

2.2 平稳性检验:ADF 与差分次数

ARIMA 的d参数来自差分次数,而差分次数要靠 ADF 检验说话。1_2_Stationarity_ADF.py做的就是这件事:对原序列做 ADF,若 p 值大于 0.05 就差分一次再检验,直到平稳。常见做法是同时看 ADF 统计量和 p 值,p 小于 0.05 且统计量小于 1% 临界值才算稳。

from statsmodels.tsa.stattools import adfuller def adf_report(series, name): result = adfuller(series.dropna(), autolag="AIC") print(f"{name}: ADF={result[0]:.4f}, p={result[1]:.4f}, " f"lags={result[2]}, crit={result[4]}") adf_report(ts, "原始序列") adf_report(ts.diff(1), "一阶差分") adf_report(ts.diff(1).diff(1), "二阶差分")

autolag="AIC"让 statsmodels 自动选滞后阶数,比手写固定 lag 稳。流感序列一般一阶差分就够,二阶差分容易过差分,把信号差没。如果一阶差分后 p 值仍大,先怀疑是不是有异常年份(比如某年上报口径变了),而不是无脑加d

2.3 ACF/PACF 定阶与2_estimate_pq.py的用法

差分平稳后,用 ACF 看 MA 阶数q,PACF 看 AR 阶数p1_3_Stationarity_ACF_PACF.py画图,2_estimate_pq.py则用 AIC/BIC 网格搜索兜底。经验上 ACF 截尾在 lag k 则 q≈k,PACF 截尾在 lag k 则 p≈k,但流感数据拖尾常见,所以网格搜索更靠谱。

参数含义判断依据流感数据常见取值
pAR 阶数PACF 截尾位置1~3
d差分次数ADF 检验1
qMA 阶数ACF 截尾位置1~2
P,D,Q,s季节项年周期 s=52P=1,D=0,Q=1
import itertools, warnings from statsmodels.tsa.statespace.sarimax import SARIMAX warnings.filterwarnings("ignore") best = None for p, q in itertools.product(range(3), range(3)): try: m = SARIMAX(ts, order=(p, 1, q), seasonal_order=(1, 0, 1, 52), enforce_stationarity=False).fit(disp=False) if best is None or m.aic < best[0]: best = (m.aic, p, q) except Exception: continue print("best AIC/p/q:", best)

s=52对应周数据的年周期,这是流感预测的关键,漏掉季节项模型基本废掉。enforce_stationarity=False在网格搜索阶段能加快拟合,但最终模型建议打开。搜索范围别开太大,p、q 各到 3 已经够,再大容易过拟合且拟合时间爆炸。

提示:ADF 检验对异常值敏感,先画一眼原始曲线,把明显的数据断点或口径变化年份标出来,再决定是否截断训练区间。

3. ARIMA/SARIMA 建模与残差诊断:从3_residual.py看模型是否合格

3.1 SARIMA 拟合与预测区间

定好阶后,用SARIMAX拟合完整模型,5_forecast.py负责外推。预测时一定要拿get_forecast而不是predict,因为前者带置信区间,后者只给点估计。流感预测里区间比点值更有用,评审老师也爱看。

model = SARIMAX(ts, order=(1, 1, 1), seasonal_order=(1, 0, 1, 52), enforce_stationarity=True, enforce_invertibility=True) res = model.fit(disp=False) # 外推 12 周,带 95% 置信区间 fc = res.get_forecast(steps=12) mean = fc.predicted_mean ci = fc.conf_int(alpha=0.05) print(mean.head())

steps=12是预测周数,按作业要求调整;conf_int(alpha=0.05)给 95% 区间。注意 SARIMA 长期外推区间会迅速变宽,这是模型诚实的地方,不要为了图好看把区间裁掉。

3.2 残差诊断:Ljung-Box 与残差正态性

3_residual.py的核心是验证残差是不是白噪声。如果残差还有自相关,说明模型没榨干信息。常用 Ljung-Box 检验,p 值大于 0.05 才接受"残差无自相关"。

from statsmodels.stats.diagnostic import acorr_ljungbox import scipy.stats as st lb = acorr_ljungbox(res.resid, lags=[10, 20], return_df=True) print(lb) # 残差正态性 stat, p = st.normaltest(res.resid.dropna()) print("normaltest p:", p)

lags=[10,20]覆盖短中期自相关;normaltest的 p 小于 0.05 说明残差非正态,预测区间会偏,但点预测仍可用。残差图里如果出现明显的周期性波动,八成是季节项没定对,回去调PQs

3.3 常见报错与排查

SARIMA 拟合最常见的两个坑:一是LinAlgError: Singular matrix,通常是差分过度或 p、q 太大导致矩阵奇异,降阶即可;二是收敛警告ConvergenceWarning,可以换method="powell"或增大maxiter。另外s=52时数据至少要两个完整年周期,否则季节项估不出来,这也是为什么流感数据一般从 2010 年之后取。

4. LSTM 与 Transformer 对照实现:lstm-flu.ipynb的滑窗与归一化

4.1 滑窗构造与归一化

LSTM 不吃原始序列,要吃监督样本。lstm-flu.ipynb用滑动窗口把序列切成(样本数, 时间步, 特征数)。归一化必须只用训练集统计量,否则验证集信息泄漏,指标虚高。

import numpy as np from sklearn.preprocessing import MinMaxScaler def make_windows(series, lookback=12): X, y = [], [] for i in range(len(series) - lookback): X.append(series[i:i+lookback]) y.append(series[i+lookback]) return np.array(X), np.array(y) split = int(len(ts) * 0.8) train, test = ts[:split], ts[split:] scaler = MinMaxScaler() train_scaled = scaler.fit_transform(train.values.reshape(-1, 1)).flatten() test_scaled = scaler.transform(test.values.reshape(-1, 1)).flatten() X_train, y_train = make_windows(train_scaled, lookback=12) X_test, y_test = make_windows(test_scaled, lookback=12) X_train = X_train.reshape(-1, 12, 1) X_test = X_test.reshape(-1, 12, 1)

lookback=12表示用过去 12 周预测下一周,正好覆盖一个季度;fit_transform只在训练集上做,测试集用transform,这是时间序列里最容易写错的一行。reshape(-1,12,1)把二维样本变成 LSTM 需要的三维张量。

4.2 LSTM 网络结构与训练参数

from tensorflow.keras import layers, models model = models.Sequential([ layers.LSTM(64, return_sequences=True, input_shape=(12, 1)), layers.LSTM(32), layers.Dropout(0.2), layers.Dense(1) ]) model.compile(optimizer="adam", loss="mse") history = model.fit(X_train, y_train, epochs=50, batch_size=16, validation_split=0.1, verbose=0)

两层 LSTM 第一层return_sequences=True才能接第二层;Dropout(0.2)抑制过拟合;epochs=50配合validation_split=0.1观察早停。如果验证 loss 先降后升,说明过拟合,减层或加 dropout。

4.3 Transformer 分支的位置编码与注意力

Transformer 分支的关键是位置编码,因为自注意力本身无序。transformer相关热词里问得最多的就是"位置信息怎么算",这里用标准正弦编码。

import tensorflow as tf class PositionalEncoding(layers.Layer): def __init__(self, d_model): super().__init__() self.d_model = d_model def call(self, x): seq_len = tf.shape(x)[1] pos = tf.range(seq_len, dtype=tf.float32)[:, tf.newaxis] i = tf.range(self.d_model, dtype=tf.float32)[tf.newaxis, :] angle = pos / tf.pow(10000.0, (2 * (i // 2)) / self.d_model) angle = tf.where(i % 2 == 0, tf.sin(angle), tf.cos(angle)) return x + angle[tf.newaxis, :, :]

d_model是嵌入维度,i // 2保证 sin/cos 成对出现。加完位置编码再进多头注意力。Transformer 在小样本周数据上不一定赢 LSTM,它的优势在长依赖,但流感序列长度有限,所以4_compare.py里三者差距往往不大,这本身就是个值得写进报告的结论。

5. 多模型对比与结果复现:4_compare.py的指标口径与踩坑

5.1 统一评估口径

对比 ARIMA、LSTM、Transformer 时,最容易犯的错是各算各的指标。4_compare.py应该把三者预测值对齐到同一测试区间,再统一算 MAE、RMSE、MAPE。

from sklearn.metrics import mean_absolute_error, mean_squared_error def evaluate(y_true, y_pred, name): mae = mean_absolute_error(y_true, y_pred) rmse = np.sqrt(mean_squared_error(y_true, y_pred)) mape = np.mean(np.abs((y_true - y_pred) / (y_true + 1e-8))) * 100 print(f"{name}: MAE={mae:.4f}, RMSE={rmse:.4f}, MAPE={mape:.2f}%") return mae, rmse, mape

1e-8防止除零,流感序列里偶尔有接近 0 的周。MAPE 在低值周会偏大,报告里最好同时给 MAE 和 RMSE,别只报 MAPE。

5.2 反归一化与对齐

LSTM/Transformer 输出是归一化后的值,必须用同一个 scaler 反变换回原始量纲,才能和 ARIMA 比。ARIMA 预测的是原始序列,不需要反变换。对齐时注意 LSTM 的测试样本比原始测试段少lookback个点,切片要对应。

pred_lstm = model.predict(X_test).flatten() pred_lstm_inv = scaler.inverse_transform(pred_lstm.reshape(-1, 1)).flatten() y_test_inv = scaler.inverse_transform(y_test.reshape(-1, 1)).flatten() # ARIMA 预测对齐到同一区间 arima_pred = mean.values[:len(y_test_inv)] evaluate(y_test_inv, pred_lstm_inv, "LSTM") evaluate(y_test_inv, arima_pred, "SARIMA")

5.3 复现环境与依赖

vscode python环境配置是高频搜索词,这份源码建议 Python 3.8~3.10,核心依赖pandasstatsmodelsscikit-learntensorflow。statsmodels 和 tensorflow 对 numpy 版本敏感,建议先建虚拟环境再装,避免numpy.dtype size changed这类二进制不兼容报错。跑之前按1_15_forecast的顺序执行,notebook 单独跑,最后用4_compare.py汇总。

注意:如果 LSTM 指标好得离谱,先查归一化是不是在全量数据上 fit 的,这是时间序列作业里最隐蔽的泄漏。

6. 进阶技巧:用残差混合模型把 ARIMA 和 LSTM 串起来

单模型各有短板:ARIMA 抓线性季节,LSTM 抓非线性残差。一个在课设里能加分的做法是混合建模——先用 SARIMA 拟合,拿到残差,再用 LSTM 去学残差里的非线性结构,最后把两部分预测相加。3_residual.py已经把残差算出来了,直接复用。

# SARIMA 残差作为 LSTM 的新目标 resid = res.resid.dropna().values scaler_r = MinMaxScaler() resid_scaled = scaler_r.fit_transform(resid.reshape(-1, 1)).flatten() Xr, yr = make_windows(resid_scaled, lookback=12) Xr = Xr.reshape(-1, 12, 1) resid_model = models.Sequential([ layers.LSTM(32, input_shape=(12, 1)), layers.Dense(1) ]) resid_model.compile(optimizer="adam", loss="mse") resid_model.fit(Xr, yr, epochs=30, batch_size=16, verbose=0) # 最终预测 = SARIMA 点预测 + LSTM 残差预测 resid_pred = scaler_r.inverse_transform( resid_model.predict(Xr[-12:].reshape(-1, 12, 1)).reshape(-1, 1) ).flatten() final = mean.values[:len(resid_pred)] + resid_pred

resid是 SARIMA 没解释掉的部分,LSTM 在这里只学残差,任务更简单,收敛更快。final把线性和非线性预测叠加,通常比任一单模型稳。验证时仍用同一测试区间算 MAE/RMSE,和4_compare.py里的单模型结果并排写进报告。另一个实用技巧是给 LSTM 输入加外生特征,比如把AGE 0-4AGE 65+的 ILI 分列作为多变量输入,input_shape(12,1)改成(12,k),往往比调网络结构更有效。最后,所有随机种子固定(tf.random.set_seed(42)np.random.seed(42)),否则每次跑出来的对比表都不一样,答辩时会被追问。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 2:40:15

线性回归预测PM2.5:从特征工程到模型部署的完整指南

简介&#xff1a;面向机器学习入门者与数据分析学习者&#xff0c;基于线性回归的PM2.5预测系统完整源码包&#xff0c;围绕空气污染物浓度预测任务&#xff0c;提供从数据读取、预处理到模型训练与结果导出的全流程Python实现。压缩包共19个文件&#xff0c;以12个csv数据文件…

作者头像 李华
网站建设 2026/9/23 2:34:59

食品干燥技术:热风与红外耦合的Comsol仿真实践

1. 食品干燥技术概述食品干燥是食品加工中最基础也最关键的环节之一。作为一名在食品工程领域摸爬滚打十多年的从业者&#xff0c;我深知干燥工艺对食品品质的决定性影响。传统热风干燥虽然设备简单、成本低廉&#xff0c;但普遍存在能耗高、时间长、营养损失大等问题。而红外干…

作者头像 李华
网站建设 2026/9/23 2:34:10

瞳孔虹膜分割数据集实战:从标注验证到Unet/YOLO-seg训练全流程解析

简介&#xff1a;面向医学图像分割与计算机视觉研究者&#xff0c;提供一套高分辨率瞳孔与虹膜分割数据集。数据集中于人眼区域图像&#xff0c;统一为640640分辨率&#xff0c;标注图采用灰度mask格式&#xff0c;像素值0、1、2分别对应背景、瞳孔和虹膜&#xff0c;可直接用于…

作者头像 李华
网站建设 2026/9/23 2:32:30

AI服务的SLO与影子流量-在上线前证明它没变笨

摘要 传统服务的 SLO 通常围绕可用性与延迟&#xff0c;用在 AI 服务上会漏掉最重要的一环&#xff1a;质量。一个响应快、从不报错但答案越来越差的系统&#xff0c;在传统监控下表现完美。本文拆解 AI 服务应当定义的四类 SLO、为什么尾延迟比平均延迟更重要、影子流量如何用…

作者头像 李华