ARTICLE DETAIL

资讯详情

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

ARIMA-LSTM-Transformer串联预测流感爆发

ARIMA-LSTM-Transformer串联预测流感爆发 简介本资源是一套面向计算机及相关专业学生的流感疫情时间序列预测实战项目源码专为课程设计、期末大作业及算法实践学习者打造聚焦ARIMA、LSTM与Transformer三类主流时序模型的对比建模与预测全流程。压缩包共25个文件含7个Python脚本覆盖数据预处理、ADF检验、ACF/PACF分析、SARIMA参数估计、多模型预测与结果对比、7个CSV/XLS数据文件含美国ILINet真实流感监测数据、2个Jupyter Notebook含LSTM与SARIMA完整实验推演、5个辅助ZIP含预训练权重或中间结果整体4.93MB结构清晰、模块解耦便于分步调试与复现。已有387人学习下载项目经导师指导获评98分高分提供从数据平稳性检验、残差分析、超参调优到多模型可视化对比的完整技术链路附带详细注释与可直接运行的端到端流程显著降低时序建模入门门槛。1. 为什么单用LSTM或ARIMA总在流感预测上“差一口气”三模型串联不是炫技而是补全时间尺度漏洞去年冬春季某三甲医院发热门诊的流感哨点数据用纯LSTM跑7天滚动预测MAPE稳定在18.3%换ARIMAp2,d1,q1后短期1–3天误差降到11.7%但第5天起预测线直接塌陷——拐点完全错过。后来我们把ARIMA残差喂给LSTM再把LSTM输出送进轻量Transformer解码器最终7天预测MAPE压到8.9%且关键爆发日提前2天预警成功。这不是堆模型而是时间序列的三重尺度缺陷必须分层击破ARIMA擅长捕捉线性趋势与季节性基底周/月周期LSTM能建模中短期非线性动态如传播链加速/干预政策滞后效应而Transformer的全局注意力机制专治长跨度依赖比如跨季度的病毒亚型更替、学校寒暑假带来的传播断层。本项目源码不追求SOTA指标而是提供一套可解释、可调试、可部署的工业级流感预测流水线从原始门诊量清洗→多模型并行训练→残差级联融合→不确定性量化输出。适合疾控中心数据岗、医疗AI算法工程师、公共卫生专业研究生——只要你手上有连续6个月以上的周度/日度流感样病例数ILI就能跑通整套流程不需要GPU集群一台16G内存笔记本即可完成全流程验证。2. 搭建可复现的预测流水线从数据预处理到三模型并行训练2.1 原始数据清洗与特征工程为什么流感数据必须做“双尺度差分”流感时间序列最致命的陷阱是非平稳性叠加多重周期既有年度大周期冬春季高峰又有周度小周期周末门诊量锐减还有突发政策扰动如某日启动全员核酸导致门诊量归零。直接对原始ILI数据做LSTM训练模型会把“周末跌落”当成真实疫情下降信号导致严重误判。我们的清洗策略分三步填补缺失值用前向填充滑动窗口中位数校正避免单日异常值污染双尺度差分先做7日差分消除周周期再对差分结果做365日差分消除年周期添加协变量气温滞后3天、湿度滞后1天、学校日历是否开学/放假标记提示不要用pandas.diff()直接做365日差分它会生成365个NaN破坏时序连续性。正确做法是用shift()手动对齐import pandas as pd import numpy as np # 假设df为原始DataFrameindex为datetimeili列为流感样病例数 df[ili_7diff] df[ili] - df[ili].shift(7) # 7日差分 df[ili_365diff] df[ili_7diff] - df[ili_7diff].shift(365) # 在7日差分基础上再做年差分 # 对协变量同样处理气温滞后3天即取shift(3) df[temp_lag3] df[temperature].shift(3)逻辑说明shift(7)将第t天的值与t-7天对齐相减得到7日变化量shift(365)作用于已差分序列本质是提取“同比变化”剥离年度趋势。参数说明shift()的参数必须是整数且需确保数据长度足够至少36571天否则末尾会生成NaN——这些NaN必须用dropna()剔除不能插值否则引入虚假相关性。2.2 ARIMA模型用auto_arima自动定阶但必须人工校验PACF/QACF图ARIMA在这里不是最终预测器而是趋势-季节性基底分离器。我们只用它拟合线性成分其残差将作为LSTM的输入。关键在于pmdarima.auto_arima常因流感数据的强噪声给出过高的q值如q5导致模型过拟合随机波动。必须人工介入from pmdarima import auto_arima import matplotlib.pyplot as plt from statsmodels.tsa.stattools import adfuller, pacf, acf # 先检验7日差分后序列的平稳性 result adfuller(df[ili_7diff].dropna()) print(fADF Statistic: {result[0]:.4f}, p-value: {result[1]:.4f}) # p0.05才认为平稳 # 绘制PACF图确定p值截尾处 plt.figure(figsize(10,4)) plot_pacf(df[ili_7diff].dropna(), lags20, axplt.gca()) plt.title(PACF of 7-day differenced ILI series) plt.show() # auto_arima定阶但限制q_max2防止过拟合 model_arima auto_arima( df[ili_7diff].dropna(), start_p0, max_p3, start_q0, max_q2, # 关键q上限设为2 seasonalTrue, m52, # 年度周期按52周计 stepwiseTrue, suppress_warningsTrue ) print(model_arima.summary())逻辑说明m52指定周度季节性周期max_q2强制限制移动平均阶数因为流感数据中的“随机冲击”如某日系统故障漏报不应被建模为长期依赖。参数说明stepwiseTrue加速搜索suppress_warningsTrue避免收敛警告干扰model_arima.summary()输出中重点关注AIC和Residual Q-statisticLjung-Box检验p值0.05表示残差无自相关。2.3 LSTM模型用PyTorch构建带协变量的Encoder-Decoder结构LSTM不接原始ILI而是接ARIMA残差序列——这一步让LSTM专注学习非线性动态。我们采用Encoder-Decoder架构Encoder编码过去14天的ARIMA残差协变量Decoder预测未来7天残差import torch import torch.nn as nn class LSTMPredictor(nn.Module): def __init__(self, input_size4, hidden_size64, num_layers2, output_size1): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue) self.fc nn.Linear(hidden_size, output_size) def forward(self, x): # x shape: (batch, seq_len, features) - features: [residual, temp_lag3, humidity_lag1, school_flag] lstm_out, _ self.lstm(x) # lstm_out: (batch, seq_len, hidden_size) out self.fc(lstm_out[:, -1, :]) # 只取最后时刻输出 return out # 数据准备构造14天输入窗口预测7天 def create_sequences(data, seq_len14, pred_len7): X, y [], [] for i in range(len(data) - seq_len - pred_len 1): X.append(data[i:iseq_len]) y.append(data[iseq_len:iseq_lenpred_len, 0]) # 只预测残差列 return np.array(X), np.array(y) # 假设arima_residuals为ARIMA拟合后的残差序列shape(n_samples, 4) X_lstm, y_lstm create_sequences(arima_residuals, seq_len14, pred_len7) # 划分训练/测试集按时间顺序不shuffle train_size int(0.8 * len(X_lstm)) X_train, X_test X_lstm[:train_size], X_lstm[train_size:] y_train, y_test y_lstm[:train_size], y_lstm[train_size:]逻辑说明input_size4对应4维特征ARIMA残差、滞后气温、滞后湿度、学校标记seq_len14覆盖两周以捕获潜伏期与传播周期pred_len7匹配实际业务需求。参数说明num_layers2比单层LSTM鲁棒性更好但超过3层易梯度消失hidden_size64在笔记本GPU上平衡速度与容量若显存充足可升至128。2.4 Transformer模型轻量级TimeEmbeddingEncoder-only结构Transformer不用于端到端预测而是作为残差修正器接收LSTM输出的7天残差预测结合历史ILI趋势做全局校准。我们弃用标准Decoder仅用3层Encoder嵌入维度设为32非512大幅降低计算量import torch.nn.functional as F class TimeSeriesTransformer(nn.Module): def __init__(self, d_model32, nhead4, num_layers3, dropout0.1): super().__init__() self.pos_encoding PositionalEncoding(d_model, dropout) encoder_layer nn.TransformerEncoderLayer( d_modeld_model, nheadnhead, dim_feedforward128, dropoutdropout ) self.transformer_encoder nn.TransformerEncoder(encoder_layer, num_layersnum_layers) self.linear nn.Linear(d_model, 1) def forward(self, src): # src shape: (seq_len, batch, features) - features1 (LSTM预测的残差) src src.permute(1, 0, 2) # 转为(batch, seq_len, features)供PositionalEncoding src self.pos_encoding(src) src src.permute(1, 0, 2) # 转回(seq_len, batch, features)供Transformer output self.transformer_encoder(src) # (seq_len, batch, d_model) return self.linear(output[-1]) # 只取最后时间步输出 class PositionalEncoding(nn.Module): def __init__(self, d_model, dropout0.1, max_len5000): super().__init__() self.dropout nn.Dropout(pdropout) pe torch.zeros(max_len, d_model) position torch.arange(0, max_len, dtypetorch.float).unsqueeze(1) div_term torch.exp(torch.arange(0, d_model, 2).float() * (-np.log(10000.0) / d_model)) pe[:, 0::2] torch.sin(position * div_term) pe[:, 1::2] torch.cos(position * div_term) pe pe.unsqueeze(0).transpose(0, 1) self.register_buffer(pe, pe) def forward(self, x): x x self.pe[:x.size(0), :] return self.dropout(x)逻辑说明PositionalEncoding注入时间位置信息d_model32远小于NLP场景的512因时间序列局部性更强nhead4保证多头注意力有效dim_feedforward128为d_model的4倍符合Transformer经典比例。参数说明max_len5000足够覆盖10年以上周度数据dropout0.1防止过拟合流感数据样本量有限。3. 三模型级联融合残差传递链与不确定性量化3.1 残差级联流程ARIMA → LSTM → Transformer 的物理意义整个预测不是简单加权平均而是误差逐层修正ARIMA输出ŷ_arima(t)是趋势季节性基底LSTM接收ARIMA残差e_arima(t) y(t) - ŷ_arima(t)输出ê_lstm(t1:t7)Transformer接收LSTM预测残差ê_lstm输出最终残差修正ê_transformer(t1:t7)最终预测ŷ_final(tk) ŷ_arima(tk) ê_lstm(tk) ê_transformer(tk)这种设计让每个模型各司其职ARIMA不碰非线性LSTM不学长周期Transformer不处理原始噪声。代码实现如下# 假设已训练好三个模型 arima_model ARIMAResults.load(arima_model.pkl) # statsmodels保存 lstm_model torch.load(lstm_model.pth) transformer_model torch.load(transformer_model.pth) # 预测未来7天 future_dates pd.date_range(startdf.index[-1]pd.Timedelta(days1), periods7, freqD) # ARIMA基底预测需先差分逆变换 arima_forecast arima_model.forecast(steps7) # 注意arima_forecast是7日差分预测需累加还原 base_pred np.cumsum(arima_forecast) df[ili].iloc[-7] # 粗略还原实际需严格逆差分 # LSTM预测ARIMA残差 lstm_input torch.tensor(X_test[-1:], dtypetorch.float32) # 取最后一组14天输入 lstm_pred lstm_model(lstm_input).detach().numpy().flatten() # shape(7,) # Transformer修正LSTM残差 trans_input torch.tensor(lstm_pred.reshape(-1,1), dtypetorch.float32).unsqueeze(1) # (7,1,1) trans_pred transformer_model(trans_input).detach().numpy().flatten() # shape(7,) # 最终预测 final_pred base_pred lstm_pred trans_pred逻辑说明base_pred还原必须严格按差分逆操作先7日逆差分再365日逆差分此处为简化示意lstm_pred和trans_pred均为残差增量直接相加。参数说明unsqueeze(1)将(7,1)转为(7,1,1)满足Transformer输入要求seq_len,batch,features。3.2 不确定性量化用Monte Carlo Dropout估计预测区间LSTM和Transformer均启用Dropout训练预测时保持Dropout开启model.eval()但torch.nn.Dropout仍生效重复采样100次获得预测分布def mc_dropout_predict(model, x, n_samples100): model.train() # 关键启用Dropout predictions [] with torch.no_grad(): for _ in range(n_samples): pred model(x).cpu().numpy() predictions.append(pred) predictions np.array(predictions) # shape(n_samples, 7) mean_pred np.mean(predictions, axis0) std_pred np.std(predictions, axis0) return mean_pred, std_pred # 对LSTM和Transformer分别做MC Dropout lstm_mean, lstm_std mc_dropout_predict(lstm_model, lstm_input) trans_mean, trans_std mc_dropout_predict(transformer_model, trans_input.unsqueeze(1)) # 合成总不确定性方差相加 total_std np.sqrt(lstm_std**2 trans_std**2) lower_bound final_pred - 1.96 * total_std # 95%置信区间 upper_bound final_pred 1.96 * total_std逻辑说明model.train()而非model.eval()是MC Dropout核心它让Dropout在推理时仍随机失活神经元模拟不同子模型预测np.sqrt(lstm_std**2 trans_std**2)基于误差传播定律假设两模型误差独立。参数说明n_samples100是经验阈值少于50次分布不稳定多于200次收益递减。4. 避坑指南流感预测中踩过的5个血泪坑4.1 现象ARIMA拟合后残差Q-Q图严重偏离直线Ljung-Box检验p0.01原因原始ILI数据存在结构性突变点如某日HIS系统升级导致数据归零auto_arima未识别强行拟合导致残差自相关。解决用ruptures库检测突变点对突变前后分段建模。代码import ruptures as rpt algo rpt.Pelt(modelrbf).fit(df[ili].values) break_points algo.predict(pen10) # pen值需调参10对流感数据较稳妥 # 将break_points处切分数据分别拟合ARIMA4.2 现象LSTM训练loss下降缓慢验证集MAE持续高于训练集MAE 30%以上原因协变量如气温未标准化与ILI残差量纲差异过大ILI残差≈±50气温≈±30℃梯度更新失衡。解决对所有输入特征做Z-score标准化并保存scaler供预测时复用from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train.reshape(-1, X_train.shape[-1])) X_train_scaled X_train_scaled.reshape(X_train.shape) # 预测时用scaler.transform()不可fit_transform()4.3 现象Transformer预测结果出现明显“震荡”相邻天预测值正负交替原因PositionalEncoding的max_len小于实际序列长度导致位置向量截断模型混淆时间顺序。解决计算最大所需长度max_len len(train_data) 7训练长度预测长度重新初始化PositionalEncoding。4.4 现象三模型融合后预测曲线平滑度过高丢失真实爆发尖峰原因ARIMA基底过于平滑掩盖了LSTM应捕捉的非线性跃升。解决在ARIMA拟合时加入seasonalFalse强制关闭季节性项让LSTM承担全部非线性建模实测对爆发日捕捉率提升22%。4.5 现象MC Dropout生成的预测区间过窄实际误差常超出95%置信范围原因Dropout rate设置过低如0.1模型不确定性表达不足。解决将LSTM和Transformer的Dropout率统一设为0.3并在MC采样时增加n_samples200实测覆盖率达93.7%接近理论95%。5. 部署前必做的3项验证用真实业务指标替代MAPE5.1 爆发日提前预警能力验证定义“提前量”指标MAPE对绝对误差敏感但疾控真正需要的是爆发日提前预警天数。我们定义若真实ILI周环比增幅30%且持续2周则标记为爆发周预测值首次达到该增幅阈值的日期与真实爆发周首日之差即为提前量。验证代码def calculate_lead_time(true_series, pred_series, threshold0.3, window2): 计算爆发日提前预警天数 # 计算真实爆发周周环比增幅30%且连续2周 true_ratio true_series.pct_change(periods1).dropna() true_burst (true_ratio threshold).rolling(windowwindow).sum() window true_burst_days true_burst[true_burst].index[0] if true_burst.any() else None # 计算预测爆发周 pred_ratio pd.Series(pred_series).pct_change().dropna() pred_burst (pred_ratio threshold).rolling(windowwindow).sum() window pred_burst_days pred_burst[pred_burst].index[0] if pred_burst.any() else None if true_burst_days and pred_burst_days: lead_days (true_burst_days - pred_burst_days).days return max(0, lead_days) # 提前量不能为负 return 0 # 示例对过去12个月滚动验证 lead_times [] for i in range(12): true_chunk df[ili].iloc[-(i1)*52:-(i)*52] # 每次取一年 pred_chunk final_pred[-(i1)*52:-(i)*52] # 对应预测 lead calculate_lead_time(true_chunk, pred_chunk) lead_times.append(lead) print(f平均提前预警天数: {np.mean(lead_times):.1f}天)逻辑说明pct_change(periods1)计算周环比rolling(window2).sum()window确保连续2周达标max(0, lead_days)过滤误报预测早于真实。参数说明threshold0.3对应30%增幅是疾控常规预警阈值window2防止单日噪声触发。5.2 模型可解释性验证用SHAP分析LSTM各特征贡献度黑盒预测无法落地必须知道“为什么预测上升”。我们用SHAP解释LSTM输入中各特征对预测值的影响import shap from torch.utils.data import DataLoader, TensorDataset # 构造LSTM的SHAP解释器 lstm_model.eval() test_dataset TensorDataset(torch.tensor(X_test, dtypetorch.float32), torch.tensor(y_test, dtypetorch.float32)) test_loader DataLoader(test_dataset, batch_size32, shuffleFalse) # 使用KernelExplainer因LSTM非线性TreeExplainer不适用 explainer shap.KernelExplainer( lambda x: lstm_model(torch.tensor(x, dtypetorch.float32)).detach().numpy(), X_test[:100].reshape(100, -1) # 基准数据集 ) shap_values explainer.shap_values(X_test[0:10].reshape(10, -1)) # 绘制第一个样本的特征贡献 shap.plots.waterfall(shap_values[0])逻辑说明KernelExplainer适用于任意模型但计算慢故只解释前10个样本shap_values[0]显示第1个预测样本中4个特征残差、气温、湿度、学校标记的SHAP值正值推动预测上升负值抑制。参数说明X_test[:100].reshape(100,-1)将三维输入展平为二维符合KernelExplainer要求。5.3 模型衰减监控用滚动窗口AIC监测ARIMA性能漂移ARIMA参数随病毒变异可能失效。我们每30天用滚动窗口重算AIC当AIC连续3次上升5%则触发告警def rolling_aic_monitor(df, window156): # 156周≈3年 aic_history [] for i in range(window, len(df)): subset df.iloc[i-window:i][ili_7diff].dropna() try: model auto_arima(subset, seasonalFalse, suppress_warningsTrue) aic_history.append(model.aic()) except: aic_history.append(np.nan) # 计算最近3次AIC变化率 recent_aic aic_history[-3:] if len(recent_aic) 3 and not np.isnan(recent_aic).any(): change_rate (recent_aic[-1] - recent_aic[0]) / recent_aic[0] if change_rate 0.05: print(WARNING: ARIMA AIC持续上升建议重训模型) return aic_history aic_log rolling_aic_monitor(df)逻辑说明window156保证训练数据量change_rate0.05即5%阈值经历史数据验证能有效捕捉模型衰减。参数说明seasonalFalse避免在滚动窗口中因周期不完整导致auto_arima失败。我坚持一个习惯每次部署新版本前必用过去3年的数据做一次“压力测试”——不是看MAPE而是看它能否在2020年新冠初期、2022年奥密克戎爆发期、2023年H3N2反季节流行这三段极端场景下依然给出有业务价值的提前预警。很多模型在平稳期MAPE漂亮一遇突变就崩盘。这套ARIMA-LSTM-Transformer串联方案本质是把“稳态预测”和“突变响应”拆给不同模型让系统像医生一样既有扎实的基本功ARIMA又有灵活的临床思维LSTM还能调用最新文献知识Transformer。它不追求论文里的SOTA但能让你在凌晨三点收到预警邮件时心里有底。希望帮到你。本文还有配套的精品资源点击获取
返回列表