流感预警这件事,难点从来不在“发现”,而在“提前”。我在复现某区域流感预警Demo系统时,核心目标只有一个:在流感监测曲线还没来得及明显抬头之前,用AI气象数据融合的方式提前两周给出区域风险判断。很多人问我,流感监测数据明明已经有了,为什么还要融合气象?因为真正的预警必须在数据变红之前发声,而气象是少数能在感染链条形成之前就出现变化的“先行变量”。这个系统适合公共卫生相关团队、医院感染管理科室、时序预测算法的同学,也适合所有想搞懂“多源数据融合到底怎么落地”的开发者。这篇文章就是我完整跑通一次这个项目后的经验记录,从数据设计、模型选型、训练验证,到上线后踩过的坑,全部摊开讲。1. 项目背景:流感预警的难点与气象数据的价值1.1 流感监测为什么总是慢半拍很多做时序预测的同行一听到“流感预警”,第一反应是:这不就是把流感监测数据喂给LSTM嘛,有什么可做的?真上手之后才发现,恰恰是“监测数据”这几个字藏了最大的坑。流感监测的源头是哨点医院和实验室检测。从患者出现症状、到医院就诊、样本送检、结果上报、再到统计部门汇总,一级一级走下来,系统里能看到的最新周数据,反映的其实是两周前甚至三周前的感染情况。也就是说,等ILI%(流感样病例占门诊就诊百分比)曲线开始明显抬头的时候,这一波风险实际已经在社区里传播了一圈。这个延迟不是哪一层环节偷懒,而是上报机制本身决定的——背后有采样周期、审核流程、数据清洗多重步骤。更要命的是,监测数据在疫情初期往往存在“漏报窗口”。很多人症状比较轻,根本不会去医院,真实传播规模被低估。等到就诊人数暴增,数据曲线才陡峭起来,但这时候再去调配疫苗、药物、床位,已经晚了。所以我们需要一个不和监测数据同步延迟、反而能提前暴露风险的信息源,而这正是气象数据能发挥作用的地方。1.2 气象变量与流感传播的物理联系我一直觉得,气象数据能被用来做流感预警,靠的不是大数据玄学,而是几个非常扎实的物理机制。第一是病毒体外存活率。流感病毒在低温、低绝对湿度的环境里,附着在飞沫或者气溶胶表面的存活时间会明显拉长,感染性维持得更久。这里要特别提醒:绝对湿度比相对湿度更关键。空气里实际水汽含量低的时候,飞沫蒸发更快,剩下的小颗粒更容易长时间悬浮在空气中,增加吸入感染的概率。第二是人体防御机制。冷空气刺激会让鼻黏膜血管收缩,纤毛摆动变慢,呼吸道局部的免疫清除能力随之下降。相当于身体给病毒临时开了一扇门。第三是行为因素。气温低、日照弱的天气里,聚集在室内密闭空间的活动变多,空气流通变差,病毒扩散阻力更小。这个链条虽然不像前两个那么“硬核”,但对真实的流行病传播影响很大。第四是紫外线。冬春季日照时间短,紫外线对病毒表面的灭活作用减弱,环境中的病毒存活时间进一步延长。这四个机制叠在一起,使得气象变量——尤其是温度和绝对湿度——成为流感传播链条里可以被提前观测的先行指标。气象预报系统本身已经能比较准确地预测未来一到两周的温度、湿度和降雨趋势,把这个预测能力接进流行病预警模型,就相当于给流感预警装了一个“前向传感器”。1.3 “提前两周”具体意味着什么预警提前两周到底价值在哪?算一笔时间账就明白了。流感从感染到发病平均约2天,从发病到重症或者去就诊,中间还要几天;哪怕哨点数据是实时更新,疫情上升信号出现时,个体层面已经来不及通过接种疫苗来获得保护了。而提前两周,意味着还有时间做几件真正有效的事:提醒高危人群尽快完成接种、调节医院发热门诊和抗病毒药物的储备、在重点场所启动通风和环境消毒提醒、给公众发布风险提示。两周不是一个拍脑袋的数字,它的上限来自气象预报能力的边界——预报超过两周以后,可信度快速衰减;下限来自流感传播周期——太短了则根本来不及执行任何干预动作。所以我在这类系统里通常把预测窗口切成两段:近两周输出“风险等级”,更远的输出“趋势方向”,而不是强行给出一个精确病例数。精确病例数的预测误差太大,反而会让业务方失去信任;风险等级加趋势方向,既有可执行性,又有稳定性。2. 整体架构设计:从数据到预警的完整链路2.1 数据分层:标签、特征与辅助信号这个Demo把数据分成三层,角色完全不同,处理方式也不同。第一层是标签数据,也就是要预测的目标:每周ILI%百分比序列、实验室流感阳性检出率、医院发热门诊量。这三个指标其实是对同一波疫情的不同观测角度,我建议以ILI%为主、阳性率作为校正项,不要全部塞进模型当并列输入。第二层是气象特征:日平均温度、昼夜温差、相对湿度、绝对湿度(可以用温度和相对湿度计算得到)、风速、降雨量、日照时数。其中绝对湿度、平均温度、日照时数是优先级最高的三个变量。第三层是辅助数据:空气质量指数、重点场所人群热力指数、药品销量指数等等。辅助数据的价值不是“多一个特征”,而是帮助模型排除短期波动干扰。比如一场普通降温也可能让门诊量增加,如果此时药品销量指数没有联动上升,就可以判断不是流感暴发主导。很多人一开始就把几十个变量胡乱拼在一起,结果模型可解释性很差,预测也不稳定。数据分层的作用就是让标签数据决定模型学什么、气象特征决定提前预测的依据、辅助数据负责消减误报。2.2 时空对齐:这块处理不好,模型分数再高也是假的我见过不少项目,模型效果特别好,提交结果时预测曲线和真实曲线几乎重叠,结果一查发现是时空对齐出了问题。最基本的一点,气象数据通常是天级或者小时级,流感监测数据通常是周级,两边必须统一时间标尺。流感监测周通常从周一开始,气象数据按自然日汇总的话,周一对应的周标签就可能错位。空间上更麻烦:流感数据按行政区域统计,气象数据按网格发布,直接把一个网格套到整个区域上,人口分布不均匀的问题就被忽略了。我们当时的处理思路是,先把研究区域按行政边界切分,然后对每一个行政区,取区内气象网格值的面积加权平均。这比简单取中心点更合理,计算量也完全可控。时间滞后特征的构造也不能忽略。温度和绝对湿度对未来疫情的影响不是同步的,通常要滞后一到三周。建议在建模之前先做互相关分析,画出每个气象变量与ILI%在不同滞后周数上的相关系数,再根据峰值确定滞后期,而不是凭感觉把所有变量一股脑塞进模型。2.3 为什么“融合”比单一数据强有人会问,既然加入了气象特征,为什么不干脆做个纯气象模型?答案是流感监测序列里包含了很多气象之外的“真实行为信息”,比如节假日人口流动、疫苗接种进度、学校开学时间表;但纯监测序列模型又会在疫情真正暴发前毫无反应,因为序列本身还没有开始变化。气象数据和监测数据的价值是互补的:监测序列提供“现状基线”,气象数据提供“变化的前瞻信号”。两者融合,模型才能在曲线还平的时候提前做出方向性判断。这个逻辑和我们平时出门穿衣服类似——你既看今天实际冷不冷,也看未来几天的天气预报。只看温度计会被突然降温打得措手不及,只看预报又可能忽略了当下的特殊情况,两个信息一起看判断才准。3. 核心实现:模型选型、训练与验证3.1 模型选型:从回归到双通道时序网络这个任务本质上是一个多变量时序预测问题,不是图像问题,也不是简单分类问题。经典回归模型(比如线性回归、随机森林)用滑窗特征也能做,但缺点很明显:无法刻画疫情序列的长期依赖,也很难利用气象序列本身的演化趋势。SARIMA虽然适合单变量周期序列,但加入外部协变量时需要手工处理季节项和滞后结构,扩展性不好。常规的LSTM单通道模型效果会比回归好,但它把气象特征和流感历史拼在一个输入向量里,模型很难真正学到“气象变化如何传导到疫情”的因果结构。到后面测试下来,我发现最稳的方案是双通道序列模型:一个通道吃过去六到八周的ILI%历史序列,另一个通道吃过去一到两周的气象特征序列,两个通道分别用LSTM或GRU编码,最后把隐状态拼起来,映射到未来两周的输出。这个结构的直观解释是:流感通道负责“现在的基础水平”,气象通道负责“未来的推动力”。3.2 训练验证里的时间边界问题这个任务最大的验证陷阱是时间泄漏。一定不能用随机划分训练集和验证集,必须按时间先后切。我们用滚动验证:假设历史数据有三年,先拿前两年训练,在第三年第一周做预测;然后逐步把验证窗口向前推,每次都保证训练集只包含验证窗口之前的数据。标准化也必须在训练集上拟合,再应用到验证集。如果整个数据集一起归一化,模型分数会被高估,现实中根本达不到那个效果。评估指标上,我一般同时看RMSE、MAE、方向命中率、峰值误差和暴发识别F1。RMSE和MAE衡量数值偏差,但公共卫生预警更关心拐点方向对不对、峰值时间有没有偏。所以在一套预警系统里,方向命中率和F1往往比RMSE更敏感。模型在数值上差零点几可能不致命,但方向判断错了会直接导致决策失误。3.3 核心代码骨架下面这个骨架完全可以在小规模数据上跑通。它省略了很多工程细节,但保留了我认为最重要的双通道结构。输入是过去7周的流感序列和过去7天的气象特征,输出是未来两周的风险评分(0到1),后续再接阈值判断。import torch import torch.nn as nn class FluRiskNet(nn.Module): def __init__(self, meteo_dim, hidden_size=64): super(FluRiskNet, self).__init__() self.flu_lstm = nn.LSTM(1, hidden_size, batch_first=True) self.meteo_lstm = nn.LSTM(meteo_dim, hidden_size, batch_first=True) self.head = nn.Sequential( nn.Linear(hidden_size * 2, hidden_size), nn.ReLU(), nn.Dropout(0.3), nn.Linear(hidden_size, 14) ) def forward(self, flu_seq, meteo_seq): _, (h_flu, _) = self.flu_lstm(flu_seq) _, (h_meteo, _) = self.meteo_lstm(meteo_seq) feat = torch.cat([h_flu[0], h_meteo[0]], dim=-1) return self.head(feat) # 输出未来14个时间点输出维度是14,对应未来两周每一天或每一周的风险评分。实际使用时不直接拿原始回归值,而是对14个输出做统计汇总,比如取最大值、均值,再映射成风险等级。这个骨架刻意做得比较简单,好处是稳定、好调、不容易过拟合。如果数据量很大,可以在气象通道后面加一层注意力机制,让模型自己学哪些天气变量对未来疫情影响更大。3.4 关键超参数与训练技巧历史窗口长度是影响效果最明显的超参数。流感通道取六到八周比较合适,太短学不到季节性趋势,太长容易引入无关的历史噪声。气象通道通常取一到两周,因为气象对传播的影响时效性很强,三周以前的天气对当前疫情几乎没有解释力。训练时我用早停法,验证集损失连续五轮不下降就停止。学习率从1e-3开始,配合余弦退火调度。Dropout设置在0.3左右,太高会让模型欠拟合,太低则有训爆风险。另一个容易忽略的点是,模型输出的不是“未来两周每一天的独立预测”,而是“基于当前状态未来两周的变化趋势”。所以在构造训练样本时,我采用的是多步滑动窗口,每个样本的标签是从当前周开始的未来两周风险强度,而不是单个时间点的预测值。这样训练出来的模型更贴近预警业务的需求。4. 实操过程:从数据清洗到模型上线的完整记录4.1 从原始数据到可训练样本我选择以周尺度作为训练尺度,主要有两个原因:流感监测数据天然是周粒度;把气象日数据重采样到周,能抹掉短期随机波动,也更方便复现和解释。第一步,从公开气象接口拿过去三年每日气象数据,重采样成周,计算周平均温度、周平均绝对湿度、周平均风速、周累计日照时数。第二步,把流感监测数据按监测周对齐,计算ILI%周序列。第三步,对缺失周做向前填充,然后构造滞后特征,比如T-1周的绝对湿度、T-2周的昼夜温差。第四步,把三个样本集合严格按时间顺序切分。这里有一个关键细节:所有特征标准化都在训练集上完成,验证集和测试集的标准化参数从训练集继承。如果重新计算验证集的均值和标准差,就会无意中把验证集的信息泄漏给模型。from sklearn.preprocessing import StandardScaler scaler = StandardScaler() train_meteo_scaled = scaler.fit_transform(train_meteo) valid_meteo_scaled = scaler.transform(valid_meteo) test_meteo_scaled = scaler.transform(test_meteo)就这么三行代码,看起来简单,但能避免一个非常隐蔽的坑。同样的逻辑适用于缺失值填充:如果验证集缺失周用全序列均值填充,那也是泄漏。4.2 风险等级与预警阈值的设定模型输出的风险评分只是中间产物,真正对接业务的是风险等级。我参考了很多区域的做法:以历史同期非流行季节的ILI%均值作为基线,当预测值超过基线1.5个标准差,判定为低风险;超过3个标准差判定为高风险;而一旦预测连续两周上升,且上升幅度超过20%,直接触发“趋势预警”标记。这些阈值不是拍脑袋定的,而是用过去三年数据做过模拟回溯后确定的。具体做法是,把历史数据切到当前周之前,模型只利用当时能获得的信息进行预测,然后对比预测结果和真实疫情曲线,统计误报和漏报数量,反复调整阈值,直到误报漏报的加权损失最小。注意,阈值不是每个区域都一样的。不同气候特点的区域内,流感季节形态差异很大,统一阈值会导致误报率两极分化。所以我的习惯是给每个聚集区域单独标定一套阈值,外层再做一个全局一致性校验,防止相邻区域出现等级跳变。4.3 预警输出的可解释信息预警不能只给一个红灯绿灯,否则用户根本不知道要不要行动。在预警卡片上,我放了三个信息:风险等级、主要贡献因子、置信区间。主要贡献因子是解释模型为什么会给出这个判断。实现上,可以采用简单特征归因方法,比如对输入的气象特征逐一置零,观察输出变化量,排序后取前三名。这样业务方看到的是“低温、低湿度、近两周ILI%上升趋势”这类人类能理解的原因,而不是模型内部的抽象向量。置信区间来自多次Dropout推断:同一个样本跑二十遍,取预测分布的5%和95%分位数。这个操作成本很低,但价值很高,能够过滤掉模型在数据不足时的“糊弄式预测”,也可以让决策者在置信度不够高时选择只提醒、不干预。4.4 上线前必须经历的并行验证期系统上线不是一跑了之。我当时采取的模式是:新模型和旧规则模型并行运行两周,双方输出一致就用新模型的结论;双方不一致的时候,人工介入核查,看看差异出在哪些区域、哪些日期。并行验证期的意义不在于测试模型代码有没有bug,而在于建立业务方对新系统的信任。另一个必须做的兜底措施是,给预警加一个数据新鲜度标记。当输入数据更新链路中断超过24小时,系统自动在预警卡片上标注“数据延迟”,并把风险等级自动下调一档。这个设计非常朴素,但能防止因为数据断供导致预警长期停在“高风险”状态,消耗业务方的耐心。5. 常见问题与排查经验实录5.1 模型频繁闪烁报警最常遇到的问题就是预警等级在低风险和高风险之间来回跳。排查下来,通常原因是输入的气象变量噪声太大,比如某一天的温度骤降就被模型解读为疫情风险信号。处理方法是把气象特征做7天移动平均,或者对预测结果做时序平滑,比如采用指数加权平均,把当日输出和前几日的输出融合起来。模型本身是可持续的,但输出的后处理也真的很重要。这里有一个细节:平滑不能只用当日和前一日,权重衰减太慢会让预警变得迟钝,太急又无法消除抖动。我用的是半衰期约三天的指数平滑,即前三天的权重约为当日的一半。5.2 疫情真的来了,模型反而反应迟钝这种问题最让人头疼,也最容易引发对系统的信任危机。根因往往有两个:第一,训练数据里从来没有出现过类似强度的疫情,模型外推能力本来就不行;第二,过度依赖气象信号的模型,在气候条件没有明显变化时,宁可维持在低风险。对策是设置一个独立的快变量通道:当流感监测序列最近一周ILI%环比上升超过50%,直接用规则触发高风险,不再等模型预测结果。这是一个看起来很土、但很有效的兜底机制。规则触发的同时,在预警卡片上注明“由快变量规则触发”,方便业务方判断。5.3 数据缺口导致预测漂移节假日、医院系统切换、报告渠道异常,都会导致某周数据缺失或异常偏低。一旦本周ILI%数据没有更新,模型会把缺失值当作数值0处理,预测立刻被打低。解决办法是保存历史同期均值作为基础值,缺失时用基础值加一个小幅随机噪声填充,并打上缺失标记作为额外特征。让模型学习到“数据缺失时自动降低权重”,比简单填零要可靠得多。5.4 数据泄漏的隐蔽形式这里必须单独把数据泄漏拎出来说,因为它是预警系统里最隐蔽的坑。除了前面说的标准化泄漏,还有三种隐蔽形式:滚动特征用了未来的值、缺失值用未来真实值填充、验证集用于特征筛选。这些泄漏很难在现场一眼看出来,所以我在工程中坚持用流水线封装数据处理,把fit和transform分开,从不手写特征拼接步骤。为了验证有没有泄漏,我还会做一个破坏性测试:把标签序列随机打乱后重新训练,如果模型分数仍然很高,那大概率是有泄漏。5.5 排查速查表下面这个表是我在实际操作中一点一点攒出来的,排查问题的时候按图索骥,一般都能在半天内定位问题。现象可能原因快速定位方法修复方案预警等级频繁翻转气象特征噪声过大画出输入特征的周内波动曲线气象特征做7天移动平均疫情暴发但模型无动作训练集缺少类似极值样本查看模型对历史最大峰值的预测误差增加快变量规则触发通道某周预测值突然暴跌ILI%数据缺失被填零检查数据新鲜度标记是否触发用历史同期均值+缺失标记填充验证指标和线上差距大数据处理发生时间泄漏打乱标签做破坏性测试用严格流水线隔离fit/transform区域间预警差异过大阈值没有分区标定按区域分别绘制误差分布分区标定阈值并做一致性校验6. 我的几点实际体会与后续扩展6.1 预警系统最重要的是让使用者敢信做这类模型,最难的不是提升几个点的F1,而是让使用者敢在预警发出后真的采取行动。如果系统十天里有七次误报,再精准的技术也白搭。现在我做完一版模型,第一件事不是看指标,而是把历史预测结果和真实曲线画在同一张图上,请业务方在图上找误报和漏报。这样能暴露很多指标无法反映的问题,比如某些区域系统总是慢半拍、某些区域总是容易偏高,然后再针对性做区域级阈值校准。6.2 从融合气象到融合行为数据气象数据是很好的开端,但远期价值有限,因为真正影响传播的还有行为因素。我现在在实验里已经开始叠加脱敏后的移动热度、重点场所人流密度、口罩佩戴率这类行为代理变量。注意,这些变量的时效性更强,滞后窗口更短,通常只留一周以内,否则会把预警信号带偏。做这类融合遵循一个原则:宁可变量少,也不要盲目堆特征,时刻保持对模型因果链条的可解释性。变量一多,每个特征的贡献就被稀释,归因解释也就失去意义。6.3 一个容易忽略的细节:训练频率这类模型绝不是训练一次就能一直用。气象季节性结构和流感流行谱都会逐年漂移,我现在的节奏是每周增量更新一次模型,每个月做一次全量重训练,每个季度重新评估一次特征有效性。单纯做增量更新时,学习率要打得比初始训练小很多,否则旧知识会被新样本冲掉。这个细节听起来不起眼,但有一次我偷懒没调整学习率,模型在两周内就把季节性特征忘光了,输出曲线变成一条直线。从那以后,我每次训练都会写清楚学习率衰减计划,并记录模型每次更新前后的预测漂移量。希望这个细节对正在探索类似系统的人有帮助。
流感预警这件事,难点从来不在“发现”,而在“提前”。我在复现某区域流感预警Demo系统时,核心目标只有一个:在流感监测曲线还没来得及明显抬头之前,用AI气象数据融合的方式提前两周给出区域风险判断。很多人问我,流感监测数据明明已经有了,为什么还要融合气象?因为真正的预警必须在数据变红之前发声,而气象是少数能在感染链条形成之前就出现变化的“先行变量”。这个系统适合公共卫生相关团队、医院感染管理科室、时序预测算法的同学,也适合所有想搞懂“多源数据融合到底怎么落地”的开发者。这篇文章就是我完整跑通一次这个项目后的经验记录,从数据设计、模型选型、训练验证,到上线后踩过的坑,全部摊开讲。1. 项目背景:流感预警的难点与气象数据的价值1.1 流感监测为什么总是慢半拍很多做时序预测的同行一听到“流感预警”,第一反应是:这不就是把流感监测数据喂给LSTM嘛,有什么可做的?真上手之后才发现,恰恰是“监测数据”这几个字藏了最大的坑。流感监测的源头是哨点医院和实验室检测。从患者出现症状、到医院就诊、样本送检、结果上报、再到统计部门汇总,一级一级走下来,系统里能看到的最新周数据,反映的其实是两周前甚至三周前的感染情况。也就是说,等ILI%(流感样病例占门诊就诊百分比)曲线开始明显抬头的时候,这一波风险实际已经在社区里传播了一圈。这个延迟不是哪一层环节偷懒,而是上报机制本身决定的——背后有采样周期、审核流程、数据清洗多重步骤。更要命的是,监测数据在疫情初期往往存在“漏报窗口”。很多人症状比较轻,根本不会去医院,真实传播规模被低估。等到就诊人数暴增,数据曲线才陡峭起来,但这时候再去调配疫苗、药物、床位,已经晚了。所以我们需要一个不和监测数据同步延迟、反而能提前暴露风险的信息源,而这正是气象数据能发挥作用的地方。1.2 气象变量与流感传播的物理联系我一直觉得,气象数据能被用来做流感预警,靠的不是大数据玄学,而是几个非常扎实的物理机制。第一是病毒体外存活率。流感病毒在低温、低绝对湿度的环境里,附着在飞沫或者气溶胶表面的存活时间会明显拉长,感染性维持得更久。这里要特别提醒:绝对湿度比相对湿度更关键。空气里实际水汽含量低的时候,飞沫蒸发更快,剩下的小颗粒更容易长时间悬浮在空气中,增加吸入感染的概率。第二是人体防御机制。冷空气刺激会让鼻黏膜血管收缩,纤毛摆动变慢,呼吸道局部的免疫清除能力随之下降。相当于身体给病毒临时开了一扇门。第三是行为因素。气温低、日照弱的天气里,聚集在室内密闭空间的活动变多,空气流通变差,病毒扩散阻力更小。这个链条虽然不像前两个那么“硬核”,但对真实的流行病传播影响很大。第四是紫外线。冬春季日照时间短,紫外线对病毒表面的灭活作用减弱,环境中的病毒存活时间进一步延长。这四个机制叠在一起,使得气象变量——尤其是温度和绝对湿度——成为流感传播链条里可以被提前观测的先行指标。气象预报系统本身已经能比较准确地预测未来一到两周的温度、湿度和降雨趋势,把这个预测能力接进流行病预警模型,就相当于给流感预警装了一个“前向传感器”。1.3 “提前两周”具体意味着什么预警提前两周到底价值在哪?算一笔时间账就明白了。流感从感染到发病平均约2天,从发病到重症或者去就诊,中间还要几天;哪怕哨点数据是实时更新,疫情上升信号出现时,个体层面已经来不及通过接种疫苗来获得保护了。而提前两周,意味着还有时间做几件真正有效的事:提醒高危人群尽快完成接种、调节医院发热门诊和抗病毒药物的储备、在重点场所启动通风和环境消毒提醒、给公众发布风险提示。两周不是一个拍脑袋的数字,它的上限来自气象预报能力的边界——预报超过两周以后,可信度快速衰减;下限来自流感传播周期——太短了则根本来不及执行任何干预动作。所以我在这类系统里通常把预测窗口切成两段:近两周输出“风险等级”,更远的输出“趋势方向”,而不是强行给出一个精确病例数。精确病例数的预测误差太大,反而会让业务方失去信任;风险等级加趋势方向,既有可执行性,又有稳定性。2. 整体架构设计:从数据到预警的完整链路2.1 数据分层:标签、特征与辅助信号这个Demo把数据分成三层,角色完全不同,处理方式也不同。第一层是标签数据,也就是要预测的目标:每周ILI%百分比序列、实验室流感阳性检出率、医院发热门诊量。这三个指标其实是对同一波疫情的不同观测角度,我建议以ILI%为主、阳性率作为校正项,不要全部塞进模型当并列输入。第二层是气象特征:日平均温度、昼夜温差、相对湿度、绝对湿度(可以用温度和相对湿度计算得到)、风速、降雨量、日照时数。其中绝对湿度、平均温度、日照时数是优先级最高的三个变量。第三层是辅助数据:空气质量指数、重点场所人群热力指数、药品销量指数等等。辅助数据的价值不是“多一个特征”,而是帮助模型排除短期波动干扰。比如一场普通降温也可能让门诊量增加,如果此时药品销量指数没有联动上升,就可以判断不是流感暴发主导。很多人一开始就把几十个变量胡乱拼在一起,结果模型可解释性很差,预测也不稳定。数据分层的作用就是让标签数据决定模型学什么、气象特征决定提前预测的依据、辅助数据负责消减误报。2.2 时空对齐:这块处理不好,模型分数再高也是假的我见过不少项目,模型效果特别好,提交结果时预测曲线和真实曲线几乎重叠,结果一查发现是时空对齐出了问题。最基本的一点,气象数据通常是天级或者小时级,流感监测数据通常是周级,两边必须统一时间标尺。流感监测周通常从周一开始,气象数据按自然日汇总的话,周一对应的周标签就可能错位。空间上更麻烦:流感数据按行政区域统计,气象数据按网格发布,直接把一个网格套到整个区域上,人口分布不均匀的问题就被忽略了。我们当时的处理思路是,先把研究区域按行政边界切分,然后对每一个行政区,取区内气象网格值的面积加权平均。这比简单取中心点更合理,计算量也完全可控。时间滞后特征的构造也不能忽略。温度和绝对湿度对未来疫情的影响不是同步的,通常要滞后一到三周。建议在建模之前先做互相关分析,画出每个气象变量与ILI%在不同滞后周数上的相关系数,再根据峰值确定滞后期,而不是凭感觉把所有变量一股脑塞进模型。2.3 为什么“融合”比单一数据强有人会问,既然加入了气象特征,为什么不干脆做个纯气象模型?答案是流感监测序列里包含了很多气象之外的“真实行为信息”,比如节假日人口流动、疫苗接种进度、学校开学时间表;但纯监测序列模型又会在疫情真正暴发前毫无反应,因为序列本身还没有开始变化。气象数据和监测数据的价值是互补的:监测序列提供“现状基线”,气象数据提供“变化的前瞻信号”。两者融合,模型才能在曲线还平的时候提前做出方向性判断。这个逻辑和我们平时出门穿衣服类似——你既看今天实际冷不冷,也看未来几天的天气预报。只看温度计会被突然降温打得措手不及,只看预报又可能忽略了当下的特殊情况,两个信息一起看判断才准。3. 核心实现:模型选型、训练与验证3.1 模型选型:从回归到双通道时序网络这个任务本质上是一个多变量时序预测问题,不是图像问题,也不是简单分类问题。经典回归模型(比如线性回归、随机森林)用滑窗特征也能做,但缺点很明显:无法刻画疫情序列的长期依赖,也很难利用气象序列本身的演化趋势。SARIMA虽然适合单变量周期序列,但加入外部协变量时需要手工处理季节项和滞后结构,扩展性不好。常规的LSTM单通道模型效果会比回归好,但它把气象特征和流感历史拼在一个输入向量里,模型很难真正学到“气象变化如何传导到疫情”的因果结构。到后面测试下来,我发现最稳的方案是双通道序列模型:一个通道吃过去六到八周的ILI%历史序列,另一个通道吃过去一到两周的气象特征序列,两个通道分别用LSTM或GRU编码,最后把隐状态拼起来,映射到未来两周的输出。这个结构的直观解释是:流感通道负责“现在的基础水平”,气象通道负责“未来的推动力”。3.2 训练验证里的时间边界问题这个任务最大的验证陷阱是时间泄漏。一定不能用随机划分训练集和验证集,必须按时间先后切。我们用滚动验证:假设历史数据有三年,先拿前两年训练,在第三年第一周做预测;然后逐步把验证窗口向前推,每次都保证训练集只包含验证窗口之前的数据。标准化也必须在训练集上拟合,再应用到验证集。如果整个数据集一起归一化,模型分数会被高估,现实中根本达不到那个效果。评估指标上,我一般同时看RMSE、MAE、方向命中率、峰值误差和暴发识别F1。RMSE和MAE衡量数值偏差,但公共卫生预警更关心拐点方向对不对、峰值时间有没有偏。所以在一套预警系统里,方向命中率和F1往往比RMSE更敏感。模型在数值上差零点几可能不致命,但方向判断错了会直接导致决策失误。3.3 核心代码骨架下面这个骨架完全可以在小规模数据上跑通。它省略了很多工程细节,但保留了我认为最重要的双通道结构。输入是过去7周的流感序列和过去7天的气象特征,输出是未来两周的风险评分(0到1),后续再接阈值判断。import torch import torch.nn as nn class FluRiskNet(nn.Module): def __init__(self, meteo_dim, hidden_size=64): super(FluRiskNet, self).__init__() self.flu_lstm = nn.LSTM(1, hidden_size, batch_first=True) self.meteo_lstm = nn.LSTM(meteo_dim, hidden_size, batch_first=True) self.head = nn.Sequential( nn.Linear(hidden_size * 2, hidden_size), nn.ReLU(), nn.Dropout(0.3), nn.Linear(hidden_size, 14) ) def forward(self, flu_seq, meteo_seq): _, (h_flu, _) = self.flu_lstm(flu_seq) _, (h_meteo, _) = self.meteo_lstm(meteo_seq) feat = torch.cat([h_flu[0], h_meteo[0]], dim=-1) return self.head(feat) # 输出未来14个时间点输出维度是14,对应未来两周每一天或每一周的风险评分。实际使用时不直接拿原始回归值,而是对14个输出做统计汇总,比如取最大值、均值,再映射成风险等级。这个骨架刻意做得比较简单,好处是稳定、好调、不容易过拟合。如果数据量很大,可以在气象通道后面加一层注意力机制,让模型自己学哪些天气变量对未来疫情影响更大。3.4 关键超参数与训练技巧历史窗口长度是影响效果最明显的超参数。流感通道取六到八周比较合适,太短学不到季节性趋势,太长容易引入无关的历史噪声。气象通道通常取一到两周,因为气象对传播的影响时效性很强,三周以前的天气对当前疫情几乎没有解释力。训练时我用早停法,验证集损失连续五轮不下降就停止。学习率从1e-3开始,配合余弦退火调度。Dropout设置在0.3左右,太高会让模型欠拟合,太低则有训爆风险。另一个容易忽略的点是,模型输出的不是“未来两周每一天的独立预测”,而是“基于当前状态未来两周的变化趋势”。所以在构造训练样本时,我采用的是多步滑动窗口,每个样本的标签是从当前周开始的未来两周风险强度,而不是单个时间点的预测值。这样训练出来的模型更贴近预警业务的需求。4. 实操过程:从数据清洗到模型上线的完整记录4.1 从原始数据到可训练样本我选择以周尺度作为训练尺度,主要有两个原因:流感监测数据天然是周粒度;把气象日数据重采样到周,能抹掉短期随机波动,也更方便复现和解释。第一步,从公开气象接口拿过去三年每日气象数据,重采样成周,计算周平均温度、周平均绝对湿度、周平均风速、周累计日照时数。第二步,把流感监测数据按监测周对齐,计算ILI%周序列。第三步,对缺失周做向前填充,然后构造滞后特征,比如T-1周的绝对湿度、T-2周的昼夜温差。第四步,把三个样本集合严格按时间顺序切分。这里有一个关键细节:所有特征标准化都在训练集上完成,验证集和测试集的标准化参数从训练集继承。如果重新计算验证集的均值和标准差,就会无意中把验证集的信息泄漏给模型。from sklearn.preprocessing import StandardScaler scaler = StandardScaler() train_meteo_scaled = scaler.fit_transform(train_meteo) valid_meteo_scaled = scaler.transform(valid_meteo) test_meteo_scaled = scaler.transform(test_meteo)就这么三行代码,看起来简单,但能避免一个非常隐蔽的坑。同样的逻辑适用于缺失值填充:如果验证集缺失周用全序列均值填充,那也是泄漏。4.2 风险等级与预警阈值的设定模型输出的风险评分只是中间产物,真正对接业务的是风险等级。我参考了很多区域的做法:以历史同期非流行季节的ILI%均值作为基线,当预测值超过基线1.5个标准差,判定为低风险;超过3个标准差判定为高风险;而一旦预测连续两周上升,且上升幅度超过20%,直接触发“趋势预警”标记。这些阈值不是拍脑袋定的,而是用过去三年数据做过模拟回溯后确定的。具体做法是,把历史数据切到当前周之前,模型只利用当时能获得的信息进行预测,然后对比预测结果和真实疫情曲线,统计误报和漏报数量,反复调整阈值,直到误报漏报的加权损失最小。注意,阈值不是每个区域都一样的。不同气候特点的区域内,流感季节形态差异很大,统一阈值会导致误报率两极分化。所以我的习惯是给每个聚集区域单独标定一套阈值,外层再做一个全局一致性校验,防止相邻区域出现等级跳变。4.3 预警输出的可解释信息预警不能只给一个红灯绿灯,否则用户根本不知道要不要行动。在预警卡片上,我放了三个信息:风险等级、主要贡献因子、置信区间。主要贡献因子是解释模型为什么会给出这个判断。实现上,可以采用简单特征归因方法,比如对输入的气象特征逐一置零,观察输出变化量,排序后取前三名。这样业务方看到的是“低温、低湿度、近两周ILI%上升趋势”这类人类能理解的原因,而不是模型内部的抽象向量。置信区间来自多次Dropout推断:同一个样本跑二十遍,取预测分布的5%和95%分位数。这个操作成本很低,但价值很高,能够过滤掉模型在数据不足时的“糊弄式预测”,也可以让决策者在置信度不够高时选择只提醒、不干预。4.4 上线前必须经历的并行验证期系统上线不是一跑了之。我当时采取的模式是:新模型和旧规则模型并行运行两周,双方输出一致就用新模型的结论;双方不一致的时候,人工介入核查,看看差异出在哪些区域、哪些日期。并行验证期的意义不在于测试模型代码有没有bug,而在于建立业务方对新系统的信任。另一个必须做的兜底措施是,给预警加一个数据新鲜度标记。当输入数据更新链路中断超过24小时,系统自动在预警卡片上标注“数据延迟”,并把风险等级自动下调一档。这个设计非常朴素,但能防止因为数据断供导致预警长期停在“高风险”状态,消耗业务方的耐心。5. 常见问题与排查经验实录5.1 模型频繁闪烁报警最常遇到的问题就是预警等级在低风险和高风险之间来回跳。排查下来,通常原因是输入的气象变量噪声太大,比如某一天的温度骤降就被模型解读为疫情风险信号。处理方法是把气象特征做7天移动平均,或者对预测结果做时序平滑,比如采用指数加权平均,把当日输出和前几日的输出融合起来。模型本身是可持续的,但输出的后处理也真的很重要。这里有一个细节:平滑不能只用当日和前一日,权重衰减太慢会让预警变得迟钝,太急又无法消除抖动。我用的是半衰期约三天的指数平滑,即前三天的权重约为当日的一半。5.2 疫情真的来了,模型反而反应迟钝这种问题最让人头疼,也最容易引发对系统的信任危机。根因往往有两个:第一,训练数据里从来没有出现过类似强度的疫情,模型外推能力本来就不行;第二,过度依赖气象信号的模型,在气候条件没有明显变化时,宁可维持在低风险。对策是设置一个独立的快变量通道:当流感监测序列最近一周ILI%环比上升超过50%,直接用规则触发高风险,不再等模型预测结果。这是一个看起来很土、但很有效的兜底机制。规则触发的同时,在预警卡片上注明“由快变量规则触发”,方便业务方判断。5.3 数据缺口导致预测漂移节假日、医院系统切换、报告渠道异常,都会导致某周数据缺失或异常偏低。一旦本周ILI%数据没有更新,模型会把缺失值当作数值0处理,预测立刻被打低。解决办法是保存历史同期均值作为基础值,缺失时用基础值加一个小幅随机噪声填充,并打上缺失标记作为额外特征。让模型学习到“数据缺失时自动降低权重”,比简单填零要可靠得多。5.4 数据泄漏的隐蔽形式这里必须单独把数据泄漏拎出来说,因为它是预警系统里最隐蔽的坑。除了前面说的标准化泄漏,还有三种隐蔽形式:滚动特征用了未来的值、缺失值用未来真实值填充、验证集用于特征筛选。这些泄漏很难在现场一眼看出来,所以我在工程中坚持用流水线封装数据处理,把fit和transform分开,从不手写特征拼接步骤。为了验证有没有泄漏,我还会做一个破坏性测试:把标签序列随机打乱后重新训练,如果模型分数仍然很高,那大概率是有泄漏。5.5 排查速查表下面这个表是我在实际操作中一点一点攒出来的,排查问题的时候按图索骥,一般都能在半天内定位问题。现象可能原因快速定位方法修复方案预警等级频繁翻转气象特征噪声过大画出输入特征的周内波动曲线气象特征做7天移动平均疫情暴发但模型无动作训练集缺少类似极值样本查看模型对历史最大峰值的预测误差增加快变量规则触发通道某周预测值突然暴跌ILI%数据缺失被填零检查数据新鲜度标记是否触发用历史同期均值+缺失标记填充验证指标和线上差距大数据处理发生时间泄漏打乱标签做破坏性测试用严格流水线隔离fit/transform区域间预警差异过大阈值没有分区标定按区域分别绘制误差分布分区标定阈值并做一致性校验6. 我的几点实际体会与后续扩展6.1 预警系统最重要的是让使用者敢信做这类模型,最难的不是提升几个点的F1,而是让使用者敢在预警发出后真的采取行动。如果系统十天里有七次误报,再精准的技术也白搭。现在我做完一版模型,第一件事不是看指标,而是把历史预测结果和真实曲线画在同一张图上,请业务方在图上找误报和漏报。这样能暴露很多指标无法反映的问题,比如某些区域系统总是慢半拍、某些区域总是容易偏高,然后再针对性做区域级阈值校准。6.2 从融合气象到融合行为数据气象数据是很好的开端,但远期价值有限,因为真正影响传播的还有行为因素。我现在在实验里已经开始叠加脱敏后的移动热度、重点场所人流密度、口罩佩戴率这类行为代理变量。注意,这些变量的时效性更强,滞后窗口更短,通常只留一周以内,否则会把预警信号带偏。做这类融合遵循一个原则:宁可变量少,也不要盲目堆特征,时刻保持对模型因果链条的可解释性。变量一多,每个特征的贡献就被稀释,归因解释也就失去意义。6.3 一个容易忽略的细节:训练频率这类模型绝不是训练一次就能一直用。气象季节性结构和流感流行谱都会逐年漂移,我现在的节奏是每周增量更新一次模型,每个月做一次全量重训练,每个季度重新评估一次特征有效性。单纯做增量更新时,学习率要打得比初始训练小很多,否则旧知识会被新样本冲掉。这个细节听起来不起眼,但有一次我偷懒没调整学习率,模型在两周内就把季节性特征忘光了,输出曲线变成一条直线。从那以后,我每次训练都会写清楚学习率衰减计划,并记录模型每次更新前后的预测漂移量。希望这个细节对正在探索类似系统的人有帮助。