简介:面向计算机专业学生的流感时间序列预测完整项目,整合ARIMA、LSTM、Transformer三类主流模型,适用于期末大作业、课程设计及实战练习。压缩包共25个文件,包含7个Python脚本、7个CSV数据文件、4个Excel表格、2个Jupyter Notebook及5个辅助压缩包,整体约4.93MB,结构清晰。已有387人浏览学习。Python脚本覆盖数据平稳性检验、ADF检验、ACF/PACF分析、模型定阶、残差分析、多模型对比与最终预测等完整环节;Notebook便于分步运行和复现,数据文件可直接代入使用。项目经导师指导并获98分,能帮助学习者系统掌握时间序列预测的建模流程,理解ARIMA、LSTM等模型在流感数据上的应用与调优思路,是课程设计或毕业项目的高质量参考。
1. 流感预测不是玄学:为什么 ARIMA、LSTM、Transformer 要放一起比
做传染病预测的人应该都有同感:流感这类数据,单拿一个模型出来,要么被季节性波动带偏,要么在爆发拐点严重滞后。用 ARIMA 做基线,用 LSTM 抓非线性,用 Transformer 看长周期依赖,最后放在同一个评估框架里横向对比,这才是这份源码真正的价值所在。它不是一个模型,而是一整套「数据清洗 → 平稳性检验 → 多模型训练 → 指标对比 → 可视化呈现」的流水线,适合做课程设计、毕业设计,也适合刚入门时序预测的工程师拿来当模板改业务数据。
这个项目的定位很明确:用同一份流感历史数据,跑通三种典型时序模型的完整流程。你不需要自己从零搭框架,拿到源码后改数据路径、调几个核心参数就能出结果。对于新手,它能让你在一天内看到三种模型在同一数据集上的真实差异;对于熟手,它的价值在于帮你快速验证一个新数据集到底适合线性模型还是深度模型,省掉大量重复的造轮子时间。
整篇文章按落地路径走:先讲数据长什么样、怎么预处理才能喂给三个模型;再分别拆 ARIMA、LSTM、Transformer 在流感预测上的具体实现和参数设置;然后给出评估对比的标准方法;最后把最容易翻车的坑一次性讲透。
2. 先把数据伺候好:流感时序预测的预处理与数据集划分
2.1 流感数据的特点:周度粒度、缺失值、爆发尖峰
流感监测数据最常见的是周度发病数或阳性检出率,粒度偏粗,一条记录代表一周。这种数据有三个显著特点:强季节性(冬春季高峰)、年度漂移(不同年份毒株流行强度不同)、突发尖峰(流感爆发周可能出现数倍于平时的数值)。这三个特点直接决定了预处理策略不能照搬通用时序模板。
拿到原始数据后,第一步不是建模而是画图。把时间序列折线图、月度热力图、年度对比图先画出来,确认三件事:有没有明显趋势项、季节性周期是 12 个月还是 52 周、有没有需要特殊处理的异常尖峰。这份源码里自带了数据探索的可视化脚本,我建议你跑完后自己盯着图看几分钟再做下一步。
缺失值处理上,周度流感数据通常采用线性插值或前向填充。线性插值适用于缺失点较少(少于 5%)、周围趋势平稳的情况;如果缺失的是爆发周附近的数据,线性插值反而会拉低峰值,这种情况下用相邻年份同一周的均值更稳妥。异常值不要直接删除,先判断是录入错误还是真实爆发,真实爆发周的数据对训练模型识别尖峰至关重要。
2.2 平稳性检验与差分:ARIMA 的前置门槛
ARIMA 模型要求输入序列平稳,所以差分阶数 d 的选择是第一个关键决策点。常见做法是先用 ADF 检验看原始序列的 p 值,如果大于 0.05 说明非平稳,做一阶差分后再检验,直到 p 值小于 0.05 为止。
from statsmodels.tsa.stattools import adfuller # 原始序列 ADF 检验 result_raw = adfuller(df['flu_cases']) print(f"原始序列 ADF p-value: {result_raw[1]:.4f}") # 一阶差分后再检验 df['diff_1'] = df['flu_cases'].diff().dropna() result_diff = adfuller(df['diff_1'].dropna()) print(f"一阶差分 ADF p-value: {result_diff[1]:.4f}")这段代码的逻辑很直白:先检验原序列,如果 p 值超过 0.05 就做差分再检验。参数上要注意dropna()必须加,否则差分后第一行的 NaN 会导致检验报错或结果失真。实际流感数据通常一阶差分就能平稳,如果一阶后仍不平稳,先检查数据里是否有趋势性缺失,而不是盲目做二阶差分——二阶差分会让序列失去可解释性,预测结果也容易过度平滑。
对于 LSTM 和 Transformer,差分不是必须的,但有一点要留意:如果原始序列的量纲差异大(比如有的年份周均 500 例,有的年份 5000 例),不做归一化会让深度模型训练效率极低。后面两章会分别讲各自的归一化策略。
2.3 训练集 / 验证集 / 测试集划分:时序数据严禁随机打乱
这是流感预测项目里新手最容易犯的错误。常规机器学习习惯用train_test_split随机划分,但时间序列一旦打乱顺序,模型学到的是「未来信息」,测试集分数虚高,实盘预测直接翻车。
正确的做法是按时间顺序切分。我一般按 7:1:2 切——前 70% 训练,接着 10% 做验证集调参,最后 20% 做测试集评估。这份源码默认也是这个比例,在配置里可以直接改。
# 按时间顺序切分,严禁 shuffle train_size = int(len(data) * 0.7) val_size = int(len(data) * 0.1) train_data = data[:train_size] val_data = data[train_size:train_size + val_size] test_data = data[train_size + val_size:]参数说明:val_size的作用是给深度模型做早停和调学习率用,ARIMA 不参与验证集调参,只用训练集拟合、测试集评估。流感数据通常只有 3~5 年即 150~260 条周度记录,测试集 20% 大约 30~50 条,足够算出有意义的误差指标。千万不要为了追求测试集更大而压缩训练集,ARIMA 在这种数据量下拟合不足会非常明显。
3. ARIMA 基线模型:用统计模型把下限打牢
3.1 定阶策略:ACF/PACF 图与自动网格搜索结合
ARIMA 的三个超参数 p、d、q 是绕不开的核心。d 在上一章已经通过 ADF 检验确定,p 和 q 可以用两种方式确定:看 ACF/PACF 图人工判断,或者用网格搜索自动选优。实际项目中我建议两者结合——先看图缩小范围,再用 AIC/BIC 精确定阶。
ACF 图在 lag 处截尾说明 q 取那个 lag 数;PACF 图截尾说明 p 取那个 lag 数。但流感数据往往在多个 lag 处都有显著相关性,看图判断容易犹豫不决,所以更可靠的是网格搜索。
import itertools from statsmodels.tsa.arima.model import ARIMA p_range = range(0, 5) d = 1 q_range = range(0, 5) best_aic = float('inf') best_order = None for p, q in itertools.product(p_range, q_range): try: model = ARIMA(train_data, order=(p, d, q)) result = model.fit() if result.aic < best_aic: best_aic = result.aic best_order = (p, d, q) except Exception as e: continue print(f"最佳 ARIMA 阶数: {best_order}, AIC: {best_aic:.2f}")参数说明:p 和 q 的搜索范围设为 0~4 是流感周度数据的常见经验值——超过 4 的滞后阶数在周度粒度下边际收益很小,反而增加过拟合风险。try-except不能省,ARIMA 在某些参数组合下会收敛失败,直接跳过比报错终止更高效。注意p从 0 开始包含纯 MA 模型,q从 0 开始包含纯 AR 模型,这样搜索空间是完整的。
实际跑下来,流感数据最常见的定阶结果是 ARIMA(1,1,1) 或 ARIMA(2,1,1)。如果搜索结果出现 p 或 q 大于 3,先怀疑是不是差分阶数不够或者数据里有未处理的异常值,而不是直接接受高阶梯模型。
3.2 预测与反差分还原:别拿差分后的数字去跟真实值比
ARIMA 在差分后的序列上训练,输出的是差分值,必须做逆差分还原才能和真实数据对比。这一点容易被忽略,尤其是在写评估代码时,直接拿forecast结果算 RMSE,得出的误差会小得离谱——不是模型好,是量纲错了。
# 预测未来 N 步 n_forecast = len(test_data) forecast_diff = result.forecast(steps=n_forecast) # 反差分还原:从训练集最后一个真实值开始累积 last_train_value = train_data.iloc[-1] forecast_restored = [] current = last_train_value for diff_val in forecast_diff: current = current + diff_val forecast_restored.append(current) forecast_restored = pd.Series(forecast_restored, index=test_data.index)核心逻辑在循环里:预测的第一个差分值加到训练集最后一个真实值上,得到第一个预测值;这个预测值再作为下一个的基准,继续累加。参数n_forecast必须和test_data长度一致,否则后面的评估代码会报索引错。还有一种更省事的做法是result.predict(start=..., end=...)传入绝对位置索引,statsmodels 会自动处理还原,但手动写循环能让你更清楚每一步在做什么,出问题时排查更快。
3.3 ARIMA 的边界:为什么流感爆发周它一定滞后
统计模型的本质是用历史模式的线性组合预测未来,所以它对「从未出现过的新模式」天然无感。流感爆发周如果数值远超历史最高水平,ARIMA 的预测值会明显偏低,且滞后一拍才往上走。这不是代码 bug,是模型能力的物理边界。
在这个项目里,ARIMA 最大的作用是做对照基线:如果 LSTM 或 Transformer 的测试集误差比 ARIMA 还差,那深度模型就白做了,需要回去查数据和训练配置。我见过不少项目最终结论写成「Transformer 优于 ARIMA」,但测试集上实际只好了几个百分点——这种情况下要反思是不是 Transformer 参数没调够,而不是急着下结论说模型更先进。
4. LSTM 模型:滑动窗口构建与训练细节
4.1 为什么用滑动窗口:流感预测的滞后性建模
LSTM 不是直接吃一整条序列,而是通过滑动窗口把序列切成「用过去 N 周预测未来 M 周」的样本对。窗口长度直接影响模型能看到的依赖范围——流感潜伏期加传染周期通常 2~4 周,所以窗口长度设为 8~12 周比较合理,既能覆盖一个完整的传播过程,又不会因为过长引入太多噪声。
import numpy as np import torch from torch.utils.data import TensorDataset, DataLoader def create_sequences(data, window_size=12, forecast_horizon=4): X, y = [], [] for i in range(len(data) - window_size - forecast_horizon + 1): X.append(data[i:i + window_size]) y.append(data[i + window_size:i + window_size + forecast_horizon]) return np.array(X), np.array(y) # 假设 data 已经做完归一化 X_train, y_train = create_sequences(train_data_normalized, window_size=12, forecast_horizon=4) X_val, y_val = create_sequences(val_data_normalized, window_size=12, forecast_horizon=4)参数说明:window_size=12表示用过去 12 周预测,forecast_horizon=4表示一次预测未来 4 周。这两个值是流感场景的推荐起点,具体可以根据数据长度调整——数据只有 150 条时,窗口太长会大幅减少样本数,这时缩到 8 更合适。create_sequences里循环的结束条件是len(data) - window_size - forecast_horizon + 1,少写一个减号就会导致索引越界。
需要注意归一化必须在切窗前完成,且只用训练集的均值和标准差。如果在全量数据上做归一化,等于把测试集的信息泄漏进了训练过程,验证集和测试集的指标都会虚高。
4.2 模型搭建:单层还是双层,Hidden Size 怎么定
流感数据量不大,LSTM 网络不宜过深。单层 LSTM 加一个全连接输出层是最稳妥的起点;如果训练集损失降不下去,再加第二层。Hidden Size 通常设置在 32~128 之间,数据量小就往下限靠,数据量足可以往上探。
import torch.nn as nn class LSTMForecaster(nn.Module): def __init__(self, input_size=1, hidden_size=64, num_layers=2, output_size=4): super().__init__() self.lstm = nn.LSTM( input_size=input_size, hidden_size=hidden_size, num_layers=num_layers, batch_first=True, dropout=0.2 if num_layers > 1 else 0 ) self.fc = nn.Linear(hidden_size, output_size) def forward(self, x): out, _ = self.lstm(x) # out: (batch, seq_len, hidden_size) out = out[:, -1, :] # 取最后一个时间步的隐状态 return self.fc(out)代码里两个关键点:batch_first=True让输入张量的形状变成(batch, seq_len, features),符合 PyTorch 惯例;取out[:, -1, :]是只保留序列最后一个时间步的隐状态作为全连接层的输入,因为我们要预测的是未来 4 周而非逐周输出。dropout=0.2只在层数大于 1 时生效,单层时设 dropout 反而会抑制学习。
预测未来 4 周有两种方式:一种是这里展示的直接多步输出,模型一次吐 4 个值;另一种是递归预测——先预测下周,把预测值拼接回输入,再预测下下周。直接多步更快、训练更稳,缺点是 4 周之间的相关性没显式建模;递归预测更灵活但误差会累积。流感预测我倾向于直接多步,因为 4 周内误差累积还不算严重。
4.3 训练循环:早停、学习率与过拟合的博弈
深度模型训练是这份源码里最需要耐心调的部分。常见做法是 Adam 优化器 + MSE 损失函数,学习率从 1e-3 起步,配合 ReduceLROnPlateau 在验证集 loss 停滞时自动降一半。
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) criterion = nn.MSELoss() scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, mode='min', factor=0.5, patience=10, verbose=True ) best_val_loss = float('inf') patience_counter = 0 early_stop_patience = 20 for epoch in range(200): model.train() train_loss = 0 for x_batch, y_batch in train_loader: optimizer.zero_grad() predictions = model(x_batch) loss = criterion(predictions, y_batch) loss.backward() optimizer.step() train_loss += loss.item() model.eval() val_loss = 0 with torch.no_grad(): for x_batch, y_batch in val_loader: predictions = model(x_batch) val_loss += criterion(predictions, y_batch).item() scheduler.step(val_loss) if val_loss < best_val_loss: best_val_loss = val_loss patience_counter = 0 torch.save(model.state_dict(), 'best_lstm.pth') else: patience_counter += 1 if patience_counter >= early_stop_patience: print(f"Early stopping at epoch {epoch}") break几个关键参数:patience=10是给学习率调整的耐心,early_stop_patience=20是给早停的耐心,后者必须是前者的整数倍以上才不会误杀。torch.save保存的是验证集最优模型而非最后一轮模型,这是深度学习中必须养成的好习惯——最后一轮往往已经过拟合了。训练轮数上限 200 是保险值,实际流感数据通常 60~100 轮内就会触发早停。
评估时要反归一化,把预测值和真实值都还原到原始量纲再计算 RMSE 和 MAE。这一步经常被漏掉,导致 LSTM 的误差比 ARIMA 还小,但画图时对不上号。归一化的参数在预处理时已经保存了,直接调用即可。
5. Transformer 模型:时序预测与 NLP 的差异处理
5.1 位置编码的意义:流感序列里的「顺序」不是句子里的「顺序」
Transformer 从 NLP 迁移到时序预测,最大的改动是位置编码。NLP 里位置编码提供的是词语顺序信息,语义上的先后虽然重要,但相邻 token 的关注度差距没那么敏感。时序数据完全相反:第 3 周的数值和第 4 周的数值之间的连续性极其重要,位置编码不设计好,模型会失去对「时间的流向」的感知。
流感预测的 Transformer 实现有两种常见位置编码方式:固定三角函数编码(原版 Transformer 的做法)和可学习位置编码。固定编码的优点是泛化性强——预测阶段如果序列长度超过训练时的长度,不会被位置向量卡住;可学习编码在短序列上拟合更好,但超出训练长度时需要外推,而这正是 Transformer 的软肋。
import math import torch.nn as nn class PositionalEncoding(nn.Module): def __init__(self, d_model, max_len=500): super().__init__() pe = torch.zeros(max_len, d_model) position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.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) # (1, max_len, d_model) self.register_buffer('pe', pe) def forward(self, x): return x + self.pe[:, :x.size(1), :]div_term的计算决定了不同维度上的频率衰减速度,这是原版论文的核心设计:维度越低频率越高,维度越高频率越低,让模型能同时捕捉短周期和长周期模式。register_buffer让位置编码随模型保存且不参与梯度更新。流感数据最长不过几年,max_len=500足够覆盖。
这里有一个容易踩的坑:Transformer 的d_model是嵌入维度,流感预测每个时间步只有一个特征值(周发病数),你会发现输入维度是 1,跟d_model=64对不上。解决方式是加一个线性映射层,把 input_size=1 先升维到 d_model,进入 Transformer 后再降维输出。
5.2 Encoder 架构参数:Layer、Head、Dropout 的合理范围
流感数据量小,Transformer 的架构必须克制。Encoder Layer 用 2 层即可,3 层以上在小数据集上基本过拟合。Attention Head 设在 4~8 之间,d_model64 配 4 个 head 是性价比最高的组合——每个 head 的维度 16,既保证注意力多样性,又不至于参数爆炸。
Dropout 是 Transformer 在时序预测上最重要的正则化手段。位置编码输出后加 0.1,每个 Encoder Layer 内部加 0.1~0.2,全连接层加 0.1。这些 dropout 位置看着分散,但每个都在切掉不同的过拟合路径——位置编码后的 dropout 防的是模型死记位置信息,Layer 内的 dropout 防的是注意力矩阵过拟合到训练集的特定数值组合。
以下是实现的核心片段,模型结构部分参考了常见做法:
import torch.nn as nn class TransformerForecaster(nn.Module): def __init__(self, input_size=1, d_model=64, nhead=4, num_layers=2, output_size=4): super().__init__() self.input_proj = nn.Linear(input_size, d_model) self.pos_encoder = PositionalEncoding(d_model) encoder_layer = nn.TransformerEncoderLayer( d_model=d_model, nhead=nhead, dim_feedforward=128, dropout=0.1, batch_first=True ) self.encoder = nn.TransformerEncoder(encoder_layer, num_layers=num_layers) self.output_proj = nn.Linear(d_model, output_size) def forward(self, x): x = self.input_proj(x) x = self.pos_encoder(x) x = self.encoder(x) x = x[:, -1, :] return self.output_proj(x)dim_feedforward=128是前馈网络的隐藏层宽度,经验法则是 d_model 的 2 倍上下。过大(比如 512)在小数据集上会立即过拟合,过小(比如 32)会削弱非线性表达能力。batch_first=True在这份源码里也用了,输入形状和 LSTM 保持一致,方便统一数据加载逻辑。
5.3 attention 可视化:怎么确认模型真的学到了季节性
跑完 Transformer 后,建议做一步 LSTM 没有的操作——把注意力权重可视化。这不是必须的,但能帮你判断模型是否真的学到了流感季节性模式,而不是在硬套位置编码。
具体做法:挑一个测试集样本,画出 12 个输入时间步对输出第 1 周的注意力权重热力图。如果模型学到的是有意义模式,你会看到权重集中在近 2~4 周,也就是潜伏期加传播周期的范围;如果权重均匀分散在所有时间步,说明模型没区分出信息重要性,大概率是欠拟合;如果权重极端集中在某一步,那是过拟合信号。
这一可视化对用户判断「Transformer 在这个项目里到底值不值得用」非常重要,也是项目答辩时最有说服力的材料。对着热力图讲「模型自动发现了流感传播的 2~4 周依赖窗口」,比贴一堆误差指标更能让人真正信服。
6. 流感预测避坑指南:从数据泄漏到维度错配
6.1 数据泄漏:归一化必须在划分之后
现象:LSTM 和 Transformer 的测试集 RMSE 小得惊人,画图时预测曲线和真实曲线几乎完全重合,但换一批新数据预测就完全崩掉。
原因:归一化时用了全量数据的均值和标准差,测试集的数值分布间接进入了训练过程。模型在训练时「见过」测试集的范围信息,相当于考试前看了答案。
解决:严格三步走——先划分训练集 / 验证集 / 测试集,再在训练集上计算均值标准差,最后用训练集的参数去归一化所有数据。即使验证集和测试集也归一化到训练集的分布上,这一步不能省略。
6.2 ARIMA 训练报错 LinAlgError:收敛失败的背后是数据或阶数问题
现象:model.fit()抛出奇异矩阵错误,或者某些(p, d, q)组合下 AIC 值突然变成 NaN。
原因:流感数据如果包含连续多周为 0 的区间(比如非流行季某些地区确实可能连续数周零报告),会导致自相关矩阵奇异;也可能是 p 或 q 取值过大,模型尝试拟合不存在的滞后结构。
解决:在网格搜索的循环里加try-except的容错只是一方面,根本解法是检查原始数据里的零值长段。如果有,先对整列做df['flu_cases'] = df['flu_cases'].clip(lower=1)再加 log 变换,把零值压成接近 0 的正数,能大幅减少收敛失败的概率。
6.3 Transformer 训练损失骤降为 0:维度错配的经典表现
现象:训练过程中 loss 一个 epoch 内直接从 1 跳到 0,打印出来的预测值全是同一个常数。
原因:output_proj的输入取的是x[:, -1, :],如果这段代码误写成了x[:, -1],张量维度就从(batch, d_model)变成了(batch, d_model)以外的形状,线性层接收不到正确输入。更隐蔽的是位置编码没有加到输入上,Transformer 退化成纯 MLP,拟合能力过剩直接记住了训练集均值。
解决:在前向传播里加形状断言assert x.shape[1] == window_size and x.shape[2] == input_size,一旦维度不对立刻报错而不是静默输出错误结果。位置编码的register_buffer很容易在代码迁移时被漏掉,检查一下模型加载后model.state_dict()里有没有pos_encoder.pe这个 key。
6.4 训练集 loss 下降但验证集 loss 停滞:学习率过低而非模型问题
现象:训练 loss 稳步下降到 0.01 附近,但验证集 loss 卡在 0.2 左右不再动,调大模型宽度也没用。
原因:常见问题在于学习率太低导致模型只能在局部最优附近徘徊,在流感数据这种小样本上,Adam 默认的 1e-3 起步通常足够了,但如果数据归一化后方差特别小,需要更大的学习率才能跨出初始区域。
解决:把学习率从 1e-3 调到 5e-3 试一轮,如果 loss 波动剧烈再降回 3e-3 微调。另一个实用手段是加 warmup——前 5 个 epoch 让学习率从 1e-5 线性升到目标值,这能让模型先稳定方向再加速收敛,在 Transformer 上效果比 LSTM 明显得多。
6.5 测试集长度不等导致评估报错:n_forecast 和 test_data 必须严格一致
现象:评估阶段sklearn.metrics.mean_squared_error(test_y, pred_y)报长度不匹配。
原因:ARIMA 的forecast(steps=n_forecast)和 Transformer 的output_size在配置里分别写死了,如果后者修改过(比如从 4 改成 8),而 n_forecast 没同步修改,两个数组长度就错位了。
解决:在所有预测代码里不要写死数字,统一用len(test_data)或output_size变量。这是一个基础但极高频的失误,源码里建议把所有预测步长都从配置文件的同一个变量读取,从源头杜绝错位。
7. 三个模型怎么组合才最有说服力:Ensemble 与预测区间验证
一个很不错但很多人没试过的玩法,是把三个模型的预测结果做加权平均。权重不靠拍脑袋,用验证集上的表现来定——每个模型的权重与其验证集 RMSE 成反比,效果往往比单独用最好的模型还要稳一点。这是因为 ARIMA 的线性特性和 LSTM/Transformer 的非线性捕捉到的信息有互补性,加权平均可以减少结构性偏差。权重计算方式建议用简单反比加归一化,不用引入复杂的优化算法,小数据集上复杂方法反而容易过拟合。
# 假设三个模型在验证集上的 RMSE rmse_arima = 120.5 rmse_lstm = 98.3 rmse_transformer = 104.7 weights = { 'arima': 1 / rmse_arima, 'lstm': 1 / rmse_lstm, 'transformer': 1 / rmse_transformer } total = sum(weights.values()) for k, v in weights.items(): weights[k] = v / total # 测试集集成预测 ensembled_pred = ( weights['arima'] * pred_arima + weights['lstm'] * pred_lstm + weights['transformer'] * pred_transformer )如果追求更大的说服力,可以在这个基础上给深度模型加置信区间。LSTM 的区间可以用 MC Dropout——预测时打开 dropout,跑 30 次取均值和方差,方差就是不确定性的近似表达;Transformer 则是调低 temperature 不适用,改用多次随机种子训练取分散度更合理。这个方法不算严谨的贝叶斯推断,但在工程场景里够用,能给出一个可量化的置信范围。
最后的验证建议是走一遍时间序列交叉验证——把数据切成多段,每段轮流做测试集,取多次评估的平均值作为最终指标。流感预测的模型评估只看一次测试集结果很容易被偶然性误导,时间序列交叉验证虽然计算量更大,但结论的说服力远超单次划分。跑完后把三种模型在每个fold上的误差画成箱线图,一眼就能看出哪一个模型在不同时间段上更稳定。这套流程走完,项目的结论就不只是「Transformer 更好」这种空话,而是明确的「在数据量小于 300 条时,LSTM 和 ARIMA 差距不大,Transformer 的优势体现在长窗口的依赖捕捉上」。
我在做类似项目时最大的教训是:深度模型的调参时间应该控制在总工作量的一半以内,另一半留给数据理解和结果分析。很多人在 LSTM 的 hidden size 上死磕两三天,最后发现提升 2% 的 RMSE 还不如把窗口长度从 8 改成 12 来得明显。记住这一点,你在流感预测这个方向上会顺畅很多。希望这篇实战拆解能帮到你。
本文还有配套的精品资源,点击获取