
1. 从静态到动态为什么我们需要一个“会变”的传染病模型如果你接触过传染病建模SIR模型大概率是你的第一课。它经典、简洁用一个微分方程组就能勾勒出易感者S、感染者I、康复者R三类人群的流动图景。经典的SIR模型假设传染率β和康复率γ是常数这就像把现实世界里一场复杂的疫情简化成了一台匀速运转的机器。在理想的教学和初步分析中这没问题它能帮你理解基本再生数R0β/γ这个核心概念以及疫情最终会走向平息的内在逻辑。但现实世界从来不是匀速的。回想一下任何一次真实的疫情爆发你会发现防控措施是在不断调整的从最初的自由传播到呼吁戴口罩、保持社交距离再到后来的局部或全面封锁甚至疫苗接种的逐步铺开。这些人为干预会直接、剧烈地改变病毒在人群中的实际传播效率。换句话说那个关键的传染率β它不是一个刻在石头上的数字而是一个随着时间、随着政策、随着公众行为不断波动的变量。同样康复率γ也可能因为医疗资源的挤兑或补充而发生变化。如果我们还用固定参数的模型去拟合或预测结果往往会与实际情况相差甚远要么严重高估疫情的峰值和规模要么完全无法解释疫情为何出现“平台期”或“双峰”等复杂形态。这就是参数时变SIR模型的价值所在。它不再把模型参数当作已知的输入而是将其视为一个需要从实际数据中“反推”出来的函数。我们的核心任务变成了给定一段时间的疫情数据比如每日新增感染数我们能否利用数学工具反向推断出传染率β(t)和康复率γ(t)随时间变化的规律这不仅能让我们对疫情的发展态势有更精准的把握更能定量评估不同阶段防控措施的实际效果。比如我们可以计算出在封城措施实施后的一周内有效传染率下降了百分之多少这比单纯说“措施有效”要有力得多。今天我就以Matlab为工具带你完整走一遍构建、求解和分析一个参数时变的SIR模型的全过程。我们会从最基础的模型方程写起探讨如何将时变参数融入其中然后重点攻克核心难题——如何利用实际数据去“反演”这些时变参数。这不仅仅是一个数学练习更是一次将理论模型贴合真实世界的思维训练。你会发现Matlab强大的数值计算和优化工具箱是完成这项任务的绝佳搭档。2. 模型基石时变SIR方程的数学表述与物理意义让我们先夯实理论基础。一个经典的SIR模型微分方程组如下dS/dt -β * S * I / N dI/dt β * S * I / N - γ * I dR/dt γ * I其中S、I、R分别代表易感者、感染者和康复者的人数N S I R是总人口假设为常数。β是传染率表示一个感染者单位时间内能传染的人数γ是康复率其倒数1/γ代表平均感染期。现在我们将它升级为时变版本。关键在于我们认为β和γ是时间t的函数dS/dt -β(t) * S * I / N dI/dt β(t) * S * I / N - γ(t) * I dR/dt γ(t) * I这个改动看似微小却带来了本质的不同。在常数参数模型中我们给定β和γ然后求解S、I、R随时间的变化曲线这是一个正向问题。而在时变参数模型中我们通常已知或部分已知一段时间内的I(t)数据例如每日报告的新增或现存病例数目标是反推出函数β(t)和γ(t)这是一个反问题或者叫参数估计问题。为什么选择β(t)和γ(t)作为时变对象这基于我们对现实的理解β(t) - 有效传染率它综合反映了病毒的生物学传染力、人群接触频率和防控措施强度。封城、戴口罩、减少聚集都会直接导致β(t)下降。因此β(t)的曲线可以直观反映社会干预的力度和效果。γ(t) - 有效康复/移除率它代表感染者离开“感染池”的速率。除了自然康复还包括被隔离、住院从而减少社区传播甚至病故。医疗资源的充足与否会影响这个速率。当医院饱和时重症患者得不到及时救治平均病程可能拉长导致γ(t)暂时降低。在开始编程之前还有一个重要的简化。对于短期疫情相对于人口变化我们常假设总人口N很大且疫情感染比例不高此时可以近似认为S ≈ N。这样感染者的方程可以简化为 dI/dt ≈ [β(t) - γ(t)] * I 这个形式清晰地表明感染者的增长速率取决于有效再生数 R_e(t) β(t) / γ(t)。当R_e(t) 1时疫情增长R_e(t) 1时疫情衰退。这为我们后续理解反演结果提供了直观的视角。注意这个简化S≈N主要用于思路分析和公式推导在后续完整的数值求解中我们仍然会使用完整的包含S的方程以保证模型在感染比例较高时也能适用。3. 工具箱的选择Matlab为何适合解决此类反问题面对这个“由果推因”的反问题我们需要一个强大的计算环境。Matlab在这方面具有天然优势主要体现在其集成的算法工具箱和便捷的编程范式上。首先求解常微分方程ODE是Matlab的看家本领。无论是时变还是常数参数定义好微分方程组后我们可以使用ode45基于Runge-Kutta方法这类求解器轻松获得数值解。这解决了我们正向模拟的问题。其次也是最关键的部分即反演时变参数。这通常转化为一个优化问题。我们的思路是先为时变参数β(t)和γ(t)假设一个具体的函数形式例如分段常数、多项式、指数函数等其中包含一些未知的系数。以这些系数作为优化变量进行模型的正向求解得到模拟的感染者曲线 I_model(t)。将 I_model(t) 与真实的感染者数据 I_data(t) 进行比较计算误差如最小二乘误差。利用优化算法自动调整那些未知系数使得模拟曲线与真实数据之间的误差最小。Matlab的优化工具箱Optimization Toolbox为此提供了现成的、强大的求解器特别是lsqcurvefit和fmincon。lsqcurvefit专门为非线性最小二乘曲线拟合设计。它几乎是为我们这个任务量身定做的输入是时间序列数据输出是模型参数目标是最小化模型输出与数据的平方误差。它使用起来非常直观。fmincon功能更通用的约束非线性优化器。当我们需要对参数施加物理约束时例如β(t)和γ(t)必须大于0fmincon就派上用场了。此外Matlab的符号数学工具箱可以帮助我们进行模型公式的推导和简化而强大的绘图功能则能让我们直观地对比拟合效果分析参数变化趋势。整个“定义模型 - 编写代码 - 优化求解 - 可视化分析”的流程可以在一个统一的脚本或函数中流畅完成这是用其他语言或工具可能需要更多拼接才能实现的。4. 实战第一步构建模型函数与准备“人造”数据在处理真实世界杂乱的数据之前我强烈建议先用模拟数据“人造”数据进行演练。这有两个巨大好处第一你可以完全掌控“地面真相”从而验证你的反演算法是否有效第二可以测试算法对数据噪声的鲁棒性。4.1 定义时变参数与正向求解假设我们想模拟一场持续100天的疫情。我们人为定义β(t)和γ(t)的变化规律以此来模拟防控措施的介入β(t)前40天自由传播设为0.3第41天起实施强力干预在20天内线性下降到0.1之后保持。γ(t)保持恒定在0.1即平均感染期10天。我们用Matlab来实现这个正向模型function dydt time_varying_sir_ode(t, y, beta_func, gamma_func) % y(1): S, y(2): I, y(3): R N 1e7; % 假设总人口1000万 beta beta_func(t); % 计算当前时刻的beta gamma gamma_func(t); % 计算当前时刻的gamma dSdt -beta * y(1) * y(2) / N; dIdt beta * y(1) * y(2) / N - gamma * y(2); dRdt gamma * y(2); dydt [dSdt; dIdt; dRdt]; end接下来定义β(t)和γ(t)的函数并调用ode45求解% 定义时间跨度 tspan [0, 100]; % 初始条件999.99万易感者100感染者0康复者 y0 [9.9999e6; 100; 0]; % 定义时变函数 beta(t) beta_func (t) 0.3 - 0.2 * max(0, min(1, (t-40)/20)); % 分段线性函数 % 定义时变函数 gamma(t) gamma_func (t) 0.1; % 常数 % 求解ODE [t_sim, y_sim] ode45((t,y) time_varying_sir_ode(t, y, beta_func, gamma_func), tspan, y0); % 提取感染者数量 I(t) I_simulated y_sim(:, 2);这样我们就得到了在没有噪声干扰下的、完美的感染者时间序列I_simulated。它的曲线会呈现出一个明显的拐点对应第40天开始的干预。4.2 为数据添加噪声模拟现实情况真实数据永远存在噪声包括报告延迟、检测能力波动、统计误差等。为了更贴近现实我们给完美的模拟数据加上一些随机噪声% 添加5%的高斯随机噪声 noise_level 0.05; I_noisy I_simulated .* (1 noise_level * randn(size(I_simulated))); % 确保感染人数不为负 I_noisy max(I_noisy, 0); % 这是我们用来做“反演”的“观测数据” I_observed I_noisy;现在我们手上有了一份“观测数据”I_observed它来自于一个我们已知β(t)和γ(t)变化规律的模型。我们的挑战就是仅凭I_observed和时间信息能否反推出接近我们预设的β(t)和γ(t)函数5. 核心挑战设计参数化形式与构建反演优化问题这是整个过程中最具技巧性的一步。我们不可能反演出每一个时间点无限自由的β(t)和γ(t)那会导致“过拟合”且问题不可解。我们必须对时变函数的形式做出合理的假设用有限的参数去描述它。这本质上是在模型的灵活性与稳定性之间做权衡。5.1 如何参数化时变函数常见的参数化方法有几种选择哪一种取决于你对疫情进程的先验认知分段常数函数这是最直观、也最稳健的方法。假设β(t)和γ(t)在几个关键的时间段内是常数。例如以防控政策变化的时点作为分界点。这只需要优化每个时间段内的常数值即可。优点是结果稳定、易于解释缺点是无法刻画参数连续、平滑的变化。分段线性/多项式函数假设在每个时间段内参数是时间的线性或低次多项式函数。这可以捕捉参数的趋势性变化。优化变量是每个多项式的系数。样条函数使用B样条等基函数来拟合参数曲线。这提供了很高的灵活性可以用较少的控制点生成平滑的曲线。但需要谨慎选择节点位置和控制点数量否则容易过拟合。参数化函数直接假设一个具体的函数形式如指数衰减β(t) β0 * exp(-k*t)或逻辑函数。这适用于有明确物理背景的场景。对于演示我们选择分段常数来拟合β(t)并假设γ(t)为常数。我们根据对疫情阶段的粗略判断假设在t50天左右有一个变化点实际上真实变化点在40天这里我们故意不精确知道。% 定义我们待优化的参数向量 p % p(1): 第一阶段0-50天的 beta % p(2): 第二阶段50-100天的 beta % p(3): 整个阶段的 gamma (假设为常数) p0 [0.25, 0.15, 0.12]; % 优化的初始猜测值5.2 构建目标函数与调用优化器我们需要编写一个函数它接受参数向量p根据p定义β(t)和γ(t)然后运行SIR模型得到模拟的I(t)最后计算模拟值与观测值之间的误差。function error objective_function(p, t_data, I_data) % p: 优化参数 [beta1, beta2, gamma] % t_data: 观测数据的时间点 % I_data: 观测的感染者数据 % error: 模拟与观测的误差向量 % 根据参数p定义时变函数 beta_func_fit (t) p(1) * (t 50) p(2) * (t 50); gamma_func_fit (t) p(3); % 常数 % 设置ODE求解 y0_fit [9.9999e6; I_data(1); 0]; % 初始感染者取自观测数据第一点 tspan_fit [min(t_data), max(t_data)]; % 求解ODE。使用odeset提高求解器在数据点处输出的精度。 options odeset(RelTol, 1e-6, AbsTol, 1e-9); [t_ode, y_ode] ode45((t,y) time_varying_sir_ode(t, y, beta_func_fit, gamma_func_fit), tspan_fit, y0_fit, options); % 将ODE的解插值到与观测数据相同的时间点上 I_sim_fit interp1(t_ode, y_ode(:,2), t_data, pchip); % 计算残差误差 error I_sim_fit - I_data; end现在我们可以使用lsqcurvefit来最小化误差的平方和。我们需要为参数设置合理的上下界lb, ub例如β和γ必须为正数。% 定义参数上下界 lb [0.01, 0.01, 0.01]; % 下限 ub [1.0, 1.0, 0.5]; % 上限根据实际情况设定 % 调用 lsqcurvefit % 注意lsqcurvefit要求目标函数形式为 fun(p, xdata) - ydata。 % 我们的目标函数已经符合xdata对应t_dataydata对应I_data。 options_opt optimoptions(lsqcurvefit, Display, iter, Algorithm, trust-region-reflective); [p_optimized, resnorm, residual, exitflag, output] lsqcurvefit((p, t) simulate_I(p, t), p0, t_sim, I_observed, lb, ub, options_opt); % 定义一个包装函数使接口符合lsqcurvefit要求 function I_sim simulate_I(p, t_data) % 这个函数调用 objective_function但只返回模拟的I值不返回误差。 % 首先需要获取全局的或外部传入的 I_data 用于初始条件这里我们简化处理。 % 更严谨的做法是将初始条件也作为参数或固定值处理。 % 此处为演示假设初始感染者I0已知为100。 I0 100; beta_func (t) p(1) * (t 50) p(2) * (t 50); gamma_func (t) p(3); y0 [9.9999e6; I0; 0]; tspan [min(t_data), max(t_data)]; [t_ode, y_ode] ode45((t,y) time_varying_sir_ode(t, y, beta_func, gamma_func), tspan, y0); I_sim interp1(t_ode, y_ode(:,2), t_data, pchip); end运行优化后p_optimized就包含了我们反演得到的最优参数估计值。6. 结果分析、验证与模型局限性的深入探讨得到优化参数后工作只完成了一半。更重要的是分析和验证这些结果。6.1 拟合效果可视化与参数对比首先将优化参数代入模型进行正向模拟并与原始“观测数据”进行对比。% 使用优化后的参数进行最终模拟 beta_opt_func (t) p_optimized(1) * (t 50) p_optimized(2) * (t 50); gamma_opt_func (t) p_optimized(3); [t_final, y_final] ode45((t,y) time_varying_sir_ode(t, y, beta_opt_func, gamma_opt_func), tspan, y0); I_fitted y_final(:,2); % 绘图对比 figure; subplot(2,1,1); plot(t_sim, I_observed, b., DisplayName, 观测数据 (含噪声)); hold on; plot(t_final, I_fitted, r-, LineWidth, 2, DisplayName, 模型拟合曲线); xlabel(时间 (天)); ylabel(感染者数量 I(t)); legend(Location, best); title(感染者数量拟合对比); grid on; subplot(2,1,2); % 绘制真实的beta(t)和反演得到的beta(t) t_plot linspace(0,100,200); beta_true arrayfun(beta_func, t_plot); beta_est arrayfun(beta_opt_func, t_plot); plot(t_plot, beta_true, k--, LineWidth, 1.5, DisplayName, 真实的 \beta(t)); hold on; plot(t_plot, beta_est, g-, LineWidth, 2, DisplayName, 反演的 \beta(t)); xlabel(时间 (天)); ylabel(\beta(t)); legend(Location, best); title(传染率 \beta(t) 的反演结果对比); grid on;通过看图我们可以直观判断拟合曲线是否很好地捕捉了数据的主要趋势和拐点**反演的β(t)**曲线是否接近我们预设的“地面真相”分段常数的假设是否在变化点附近产生了合理的“台阶”6.2 计算有效再生数 Re(t)利用反演得到的β(t)和γ(t)我们可以计算关键指标——时变有效再生数 Re(t) β(t) / γ(t)。Re_estimated beta_est / p_optimized(3); % 对于分段常数beta和常数gamma figure; plot(t_plot, Re_estimated, m-, LineWidth, 2); hold on; yline(1, r--, LineWidth, 1.5, DisplayName, Re1 临界线); xlabel(时间 (天)); ylabel(R_e(t)); title(时变有效再生数 R_e(t)); legend(R_e(t), 临界线); grid on;这条Re(t)曲线极具政策指导意义。它明确显示出疫情从Re1增长到Re1衰退的转折时间定量评估了干预措施将传播潜力压制到临界线以下的效果。6.3 不确定性分析与模型局限性我们必须清醒认识到反演结果的局限性模型结构误差SIR模型本身是高度简化的它假设完全混合的人群、均匀的接触、没有年龄结构、没有无症状感染、没有潜伏期等。这些简化都会导致系统误差。参数化假设的敏感性我们假设β(t)是分段常数且变化点在50天。如果真实变化点是40天或60天我们的反演结果就会产生偏差。在实际应用中变化点的选择需要结合防控政策出台的时间点或者尝试多种分段方案选择拟合效果最好的一个。数据质量的影响我们添加了5%的噪声现实数据的噪声可能更大且可能存在系统性的报告偏差如周末效应、检测能力突变。这要求我们在优化时考虑更稳健的损失函数如Huber损失而不仅仅是最小二乘。初始条件的敏感性模型对初始感染者数量I0可能很敏感。在实际操作中I0有时也需要作为一个待优化参数或者通过早期数据单独估计。辨识性问题在某些情况下β和γ可能存在“此消彼长”的耦合关系导致优化算法陷入局部最优或不同的参数组合能产生相似的I(t)曲线。这就需要引入额外的先验信息或数据如康复者数据R(t)来约束解空间。实操心得不要追求对数据的完美拟合。一个对噪声过度拟合的复杂模型其预测能力往往不如一个稍微粗糙但结构清晰的简单模型。反演出的β(t)曲线其趋势和相对变化量比其绝对值更重要。例如关注“干预后β下降了约60%”这个结论比纠结“β是从0.28降到0.11还是从0.32降到0.13”更有实际意义。7. 进阶思路让模型更贴近现实的几种尝试在掌握了基本方法后你可以从以下几个方向深化让模型更具实用价值引入更复杂的模型结构将SIR扩展为SEIR加入潜伏期E、SEIRD加入死亡D、或加入年龄分层、空间异质性。这能更好地描述像COVID-19这样具有潜伏期、且传播不均的疾病。Matlab中求解高维ODE组同样高效。采用更灵活的时变参数化方法尝试使用样条函数或高斯过程来表征β(t)。这可以减少对变化点位置先验知识的依赖。Matlab的曲线拟合工具箱Curve Fitting Toolbox和统计与机器学习工具箱提供了相关函数。利用贝叶斯方法进行参数估计除了优化得到单一的最优参数值我们还可以用马尔可夫链蒙特卡洛MCMC等方法得到参数的概率分布从而量化反演结果的不确定性。这能告诉你“β(t)在95%的可能性下落在哪个区间”。这对于决策支持至关重要。融合多源数据不仅仅使用感染者I(t)数据尝试同时拟合每日新增报告数、累计死亡数、血清学调查数据抗体阳性率等。这能提供更多的约束提高反演结果的可靠性。对应的目标函数需要整合不同数据源的加权误差。实时更新与预测将整个过程自动化当有新数据到来时重新运行优化更新对β(t)和Re(t)的估计并对未来短期趋势做出基于当前参数的预测。这构成了一个简单的疫情实时评估系统。最后我想强调的是数学模型尤其是时变参数模型是我们理解复杂疫情动态的一个强大“透镜”。它不能替代流行病学家的专业判断但可以提供定量的、动态的辅助洞察。通过Matlab实现这一过程不仅锻炼了解决反问题的计算能力更培养了一种将动态机制与观测数据相融合的系统思维。在实际操作中保持对模型假设的警惕对数据质量的审慎以及对结果解读的谦逊和掌握算法本身同等重要。