简介:本资源面向计算机、人工智能、数据科学等专业的在校学生与教师,提供一套基于传染病动力学模型SEIR与LSTM神经网络实现新型冠状病毒肺炎预测的Python项目源码,可作为课程设计、期末大作业或毕业设计的参考方案。压缩包共31个文件,约1.79MB,包含12个Python脚本、14张结果图、2份Excel数据表及2份Markdown说明文档,脚本覆盖SEIR基础建模、不同干预强度下的疫情推演以及LSTM时序预测等模块,图片直观展示预测曲线,数据表提供真实疫情数据支撑。项目通过控制接触率体现防控干预的意义,并以LSTM输入前三天数据预测第四天走势,注释详尽、结构清晰。目前已有384人学习下载,适合入门进阶与二次开发,读者可据此掌握动力学建模与神经网络预测的完整实现思路。
1. 传染病动力学遇上深度学习:SEIR+LSTM 到底在预测什么
2020 年之后,但凡做过疫情数据建模的人都会碰到同一个尴尬:纯 SEIR 曲线在拐点处总是慢半拍,纯 LSTM 又容易在数据陡增陡降时给出离谱外推。把两者拼起来,用 SEIR 生成符合传染病机理的序列、再用 LSTM 去拟合残差和时序依赖,是我试过之后觉得最稳的一条路。这个方案适合两类人:一类是手上有确诊、治愈、死亡等公开时序数据,想搭一套能跑通、能解释、能复现的预测流水线;另一类是想拿一个真实场景练手 SEIR 和 LSTM 联合建模的 Python 工程师。它不解决"预测未来三个月"这种玄学问题,但能把短期趋势拟合和机理约束同时抓住,比单跑一个模型靠谱得多。
标题里那份源码包,核心就是三件事:SEIR 微分方程数值求解、LSTM 网络搭建与训练、两者结果的融合与评估。下面我按"先讲清机理、再动手复现、最后说坑"的顺序拆开,每一步都给能直接抄的命令和参数。你不需要先看完所有理论,跟着代码跑一遍再回头看公式,理解会快很多。
2. SEIR 动力学模型:把传染病机理写成可求解的方程
2.1 四个仓室和三个关键参数
SEIR 把人群分成四类:易感者 S、潜伏者 E、感染者 I、移除者 R。它的价值在于用极少的参数描述传播结构,而不是靠数据硬拟合。核心方程是:
dS/dt = -beta * S * I / N dE/dt = beta * S * I / N - sigma * E dI/dt = sigma * E - gamma * I dR/dt = gamma * I三个参数决定曲线形状:beta 是传染率,sigma 是潜伏期转感染率的倒数(1/潜伏期),gamma 是恢复率(1/感染期)。N 是总人口。很多人一上来就调 beta,其实 sigma 和 gamma 对拐点位置的影响更直接,血泪经验是先把这两个按流行病学常识定死,再动 beta。
2.2 用 scipy 求解 SEIR 并生成训练序列
import numpy as np from scipy.integrate import odeint import matplotlib.pyplot as plt def seir_model(y, t, N, beta, sigma, gamma): S, E, I, R = y dS = -beta * S * I / N dE = beta * S * I / N - sigma * E dI = sigma * E - gamma * I dR = gamma * I return dS, dE, dI, dR N = 1000000 # 总人口 I0, E0, R0 = 10, 30, 0 S0 = N - I0 - E0 - R0 beta, sigma, gamma = 0.45, 0.2, 0.1 # 传染率、潜伏转阳率、恢复率 t = np.linspace(0, 160, 160) # 160 天 solution = odeint(seir_model, [S0, E0, I0, R0], t, args=(N, beta, sigma, gamma)) S, E, I, R = solution.T这段代码用odeint做数值积分,args把固定参数传进去。t的步长决定输出粒度,做 LSTM 输入时通常取 1 天一步。beta=0.45对应基本再生数 R0≈beta/gamma=4.5,属于早期新冠的典型量级。跑完你会看到 I 曲线先指数上升后回落,这个形状就是后面 LSTM 要学的目标。
2.3 参数怎么定:别拍脑袋,用数据反推
beta、sigma、gamma 不是随便填的。常见做法是拿公开的累计确诊和治愈数据,用最小二乘拟合这三个参数。我一般会固定 sigma 和 gamma(潜伏期 5 天、感染期 10 天对应 0.2 和 0.1),只对 beta 做网格搜索,看哪条 SEIR 曲线和真实 I 序列的 RMSE 最小。这样既保留了机理约束,又不会让模型完全脱离数据。注意:SEIR 假设人群均匀混合、无干预,真实数据有封控、检测延迟,所以拟合残差里恰恰藏着 LSTM 要补的信息。
3. LSTM 网络:用 Python 搭一个能收敛的时序预测器
3.1 为什么用 LSTM 而不是普通前馈神经网络
普通前馈神经网络把每个时间点当独立样本,丢掉了时序依赖。LSTM 的遗忘门和细胞状态能记住几十步之前的信息,对疫情这种有潜伏期、有惯性的序列更合适。热搜里常出现的"lstm预测""lstm模型代码",核心就是这个门控结构。你不需要手写门,Keras 或 PyTorch 都封装好了,关键是输入窗口和归一化。
3.2 构造滑动窗口数据集
import numpy as np from sklearn.preprocessing import MinMaxScaler def make_dataset(series, window=14): X, y = [], [] for i in range(len(series) - window): X.append(series[i:i+window]) y.append(series[i+window]) return np.array(X), np.array(y) scaler = MinMaxScaler() I_scaled = scaler.fit_transform(I.reshape(-1, 1)).flatten() X, y = make_dataset(I_scaled, window=14) X = X.reshape((X.shape[0], X.shape[1], 1)) # LSTM 需要 [样本, 时间步, 特征]window=14表示用过去 14 天预测第 15 天,这个值可以调:太小抓不住趋势,太大引入噪声。MinMaxScaler把数据压到 0-1,LSTM 对量纲敏感,不归一化基本不收敛。reshape那一步是新手最容易翻车的地方,LSTM 输入必须是三维张量,少一维直接报错。
3.3 搭建并训练 LSTM
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout model = Sequential([ LSTM(64, return_sequences=True, input_shape=(14, 1)), Dropout(0.2), LSTM(32), Dense(1) ]) model.compile(optimizer='adam', loss='mse') history = model.fit(X, y, epochs=100, batch_size=16, validation_split=0.2, verbose=1)两层 LSTM,第一层return_sequences=True把序列传给下一层,第二层只输出最后一步。Dropout(0.2)防过拟合,疫情数据样本少,不加这个验证损失会先降后升。epochs=100配合validation_split=0.2,看验证损失不再下降就停。batch_size=16是小数据集的稳妥选择。训练完用model.predict拿预测,再scaler.inverse_transform还原量纲。
4. 把 SEIR 和 LSTM 拼起来:残差建模与结果融合
4.1 两种融合思路和选型理由
第一种是串联:SEIR 先拟合出机理曲线,LSTM 学真实值减 SEIR 的残差,最后相加。第二种是并联:SEIR 和 LSTM 各出一版预测,加权平均。我推荐串联,因为残差通常更平稳,LSTM 学起来容易,而且机理部分可解释。并联的权重不好定,调参成本高,除非你有明确证据说两个模型误差互补。
4.2 残差建模的完整流程
# 假设 seir_I 是 SEIR 输出的 I 序列,real_I 是真实序列 residual = real_I - seir_I res_scaled = scaler.fit_transform(residual.reshape(-1, 1)).flatten() Xr, yr = make_dataset(res_scaled, window=14) Xr = Xr.reshape((Xr.shape[0], Xr.shape[1], 1)) res_model = Sequential([ LSTM(32, input_shape=(14, 1)), Dense(1) ]) res_model.compile(optimizer='adam', loss='mse') res_model.fit(Xr, yr, epochs=80, batch_size=16, validation_split=0.2, verbose=0) pred_res = res_model.predict(Xr) pred_res = scaler.inverse_transform(pred_res).flatten() final_pred = seir_I[14:] + pred_res # 对齐时间步后相加残差序列先归一化再滑窗,和前面 LSTM 流程一致。final_pred就是融合结果。注意时间对齐:SEIR 输出从第 0 天开始,滑窗后第一个预测对应第 14 天,相加时要把 SEIR 序列切片对齐,否则整体偏移,这是最常见的翻车点。
4.3 评估指标和参数对照
| 指标 | 含义 | 建议阈值 |
|---|---|---|
| RMSE | 均方根误差 | 越小越好,和量纲相关 |
| MAE | 平均绝对误差 | 抗离群值 |
| MAPE | 平均绝对百分比误差 | 低于 10% 算可用 |
| R² | 拟合优度 | 高于 0.85 可接受 |
评估时一定要在测试集上算,不能只看训练集。我一般留最后 20% 做测试,如果 MAPE 超过 15%,先检查归一化有没有在训练集上 fit、测试集上 transform,这个顺序反了结果会虚高。
5. 避坑与排查:跑 SEIR+LSTM 最容易翻车的 5 个地方
5.1 现象:LSTM 损失不下降,一直卡在 0.25 左右
原因:数据没归一化,或者输入维度不对。LSTM 对原始量级敏感,确诊数上千时梯度直接爆炸。 解决:确认MinMaxScaler在训练集上 fit,输入 reshape 成(样本, 时间步, 1),打印X.shape核对。
5.2 现象:SEIR 曲线和真实数据完全对不上
原因:beta 拍脑袋填的,或者 N 用了全国人口但数据只是某个城市。 解决:N 必须和数据的空间范围一致,beta 用网格搜索拟合,别手动猜。先画图对比再往下走。
5.3 现象:融合后预测比单跑 LSTM 还差
原因:残差没对齐时间步,或者 SEIR 本身偏差太大,残差里全是系统误差,LSTM 学不动。 解决:打印seir_I和real_I的前 20 个值核对对齐;如果 SEIR 的 RMSE 已经很大,先调 SEIR 参数再融合。
5.4 现象:验证损失先降后升,训练损失一直降
原因:过拟合,数据量太小,模型记住了噪声。 解决:加 Dropout、减小 LSTM 单元数、早停(EarlyStopping),别硬跑满 epochs。
5.5 现象:预测值全是直线,没有波动
原因:窗口太大或者归一化后数据方差太小,模型学到的是均值。 解决:把 window 从 14 降到 7 试试,检查 scaler 的data_range_,如果接近 0 说明序列本身太平。
6. 进阶技巧:用滚动预测和置信区间把结果用起来
单步预测只能看一天,实际用的时候需要滚动预测:把预测值填回输入窗口,一步步往后推。代码上就是把make_dataset改成循环,每次预测完把结果 append 进序列再取下一个窗口。注意滚动超过 7 步误差会累积,我一般只滚 7 到 14 天,再远就不可信了。
def rolling_forecast(model, last_window, steps=7): preds = [] window = last_window.copy() for _ in range(steps): x = window.reshape((1, len(window), 1)) p = model.predict(x, verbose=0)[0, 0] preds.append(p) window = np.append(window[1:], p) # 滑窗前进 return np.array(preds)last_window是归一化后的最后 14 天数据,steps控制往前推几天。每预测一步就把最老的点挤出去、新点补进来,保持窗口长度不变。这个函数返回的还是归一化值,记得用scaler.inverse_transform还原。
另一个实用技巧是给预测加置信区间。简单做法是用 Dropout 在推理时也开启(training=True),跑 50 次取均值和分位数,得到近似贝叶斯区间。这样你给出的不是一个点,而是一个范围,决策时更有参考价值。
最后说个我自己的习惯:每次改完参数,先把 SEIR 单独画出来看形状对不对,再跑 LSTM 看损失曲线,最后才融合。跳过任何一步,后面出问题都要回头重查,反而更慢。这套流程我跑了不下二十次,稳定性和可解释性都比单模型强。希望帮到你。
本文还有配套的精品资源,点击获取