简介:《基于强暴雨预报知识的机器学习尝试》是一篇面向气象预报与人工智能交叉领域研究者的参考文献,聚焦如何把强暴雨预报经验转化为机器学习可用的知识环境。文献以江西省强暴雨为对象,介绍借助IMFOS-0520-CH微机与JMFOS系统实现规则自动生成的过程,涉及类比学习与归纳学习两类方式,并阐述和谐性、完备性两条原则,以及起报条件、54个因子库、历史个例库等关键环节,最终生成150条预报规则并完成五年试报,兼具专业指导价值与方案参考意义。资源包仅含1个PDF文件,约142KB,便于检索、打印与引用,适合作为论文写作与算法设计时的案例素材。目前已有84人学习下载。
1. 强暴雨预报知识与机器学习尝试:先别急着上模型
强暴雨预报知识和机器学习放在一张样本表里,最先暴露的通常不是模型结构,而是正样本太少。把 1 小时降水达到 20 mm 以上记成正样本,一个汛期也许只有几百到几千条,负样本却有几十万条;模型 AUC 看起来不错,落到 TS、POD、FAR 上却常常没法给预报员用。标题里的 .pdf 只是载体,真正要落地的是把强暴雨预报知识转成机器学习模型能吃的标签、特征和检验口径。适合已经会 Python 机器学习基础、手上有雷达、自动站、探空或再分析样本,准备做短时强降水、强对流短临项目的人;也适合机器学习入门后想找一个真实不平衡时序问题练手的人。路径按四步走:定义标签,构造物理量和雷达特征,用传统机器学习算法跑基线,再用概率校准和邻域检验决定能不能发提示。
2. 把强暴雨预报知识转成机器学习特征:CAPE、K指数、雷达回波与时间窗
2.1 强暴雨标签怎么定:1h、3h、6h 降水阈值与格点匹配
标签不是把降水画个圈就完事。短临强降水常见做法是按站点或格点定义未来 1 h、3 h、6 h 累计降水阈值,再用半径 1 到 3 个格点的邻域放宽命中。若直接要求格点完全重合,正样本会少到模型学不动;若邻域开太大,FAR 会抬头,预报员很快就不看。更关键的是特征截止时间:起报时刻之后的数据一律不能进入特征,哪怕它只是“未来 10 分钟”的雷达回波。
| 预报对象 | 常见标签阈值 | 标签窗口 | 特征截止 | 建模注意 |
|---|---|---|---|---|
| 短时强降水 | 1 h 累计 ≥ 20 mm | 未来 1 h | 起报时刻 | 样本少,邻域 1 格较常见 |
| 强降雨 | 3 h 累计 ≥ 50 mm | 未来 3 h | 起报时刻 | 与特征窗口留出间隔 |
| 暴雨 | 6 h 累计 ≥ 100 mm | 未来 6 h | 起报时刻 | 地形和边界影响大 |
| 极端强降水 | 1 h 累计 ≥ 50 mm | 未来 1 h | 起报时刻 | 需要代价敏感和概率输出 |
格点匹配时还要统一投影和分辨率。雷达拼图、数值模式、自动站常来自不同网格,直接按行列号对齐会错位几公里到十几公里。我一般先把所有数据插值到同一套等经纬度或兰勃特投影网格,再用最近邻或双线性重采样,标签格点与特征格点必须共用同一套坐标。
2.2 用 CAPE、K指数、风切变、可降水量做 Python 机器学习特征
CAPE、K 指数、深层风切变、可降水量、抬升凝结高度、CIN、SRH 是传统机器学习里性价比很高的一组物理量。它们不是让模型背公式,而是给模型先验:低层暖湿、中层干冷、抬升条件具备、风切变合适,强降水和对流才有组织。下面这段代码把常见探空和再分析变量拼成特征,字段名按你手上的表结构替换即可。
import numpy as np import pandas as pd def add_thermo_features(df): # K指数:中低层温差 + 低层露点 - 中层干层,越大通常越有利于对流 df["k_index"] = (df["t850"] - df["t500"]) + df["td850"] - (df["t700"] - df["td700"]) # 0-6 km 风切变,这里用 850 hPa 与 500 hPa 近似,实际项目按高度层插值 df["ws850"] = np.sqrt(df["u850"] ** 2 + df["v850"] ** 2) df["ws500"] = np.sqrt(df["u500"] ** 2 + df["v500"] ** 2) df["shear_0_6"] = np.sqrt( (df["u500"] - df["u850"]) ** 2 + (df["v500"] - df["v850"]) ** 2 ) # 可降水量单位统一到 mm,缺失用训练期气候中位数填补 df["pw_mm"] = df["pw"].fillna(df["pw"].median()) # 对流抑制和抬升凝结高度常与触发条件有关 df["cin_abs"] = df["cin"].abs() df["lcl_km"] = df["lcl"] / 1000.0 return df # 调用示例 # df = pd.read_parquet("storm_samples.parquet") # df = add_thermo_features(df)逻辑说明:先把热力不稳定、水汽、动力切变拆成独立列,让树模型或线性模型自己找非线性关系;不要直接把“是否强暴雨”写进特征。参数说明:t850、td850、t700、td700、t500的温度露点单位要一致,摄氏度或开尔文都可以,因为计算的是温差;u850、v850、u500、v500单位统一为 m/s;pw若原始单位是 cm,要先乘 10。缺失值只能用训练期统计量填,不能把验证期和测试期一起算中位数,否则就是数据泄漏。
提示:物理量特征不是越多越好。先保留 10 到 20 个有明确天气学含义的变量,再让特征重要性筛选,比一上来堆几百列更接近可复现的机器学习项目。
2.3 雷达回波特征与光流外推:哪些量能进模型
雷达侧的特征要围绕“当前回波状态”和“过去演变”展开。组合反射率最大值、平均值、面积、回波顶高、垂直积分液态水、低层径向速度、质心位置、移动矢量、过去 30 分钟增强率,都是短临里常见且容易落表的量。光流外推可以用来估计回波移动,但只能使用当前帧和过去帧;如果把未来帧拿进来算光流,再拿去预测未来降水,离线指标会好得离谱,上线就掉下来。
深度学习和机器学习结合时,可以先用卷积网络从雷达序列里提空间特征,再把 CAPE、K 指数、风切变拼进全连接层。不过对大多数刚起步的机器学习项目,先把统计特征和梯度提升树跑通,比直接上复杂时空网络更容易定位问题。传统机器学习算法在几百到几万条样本上往往更稳,特征含义也更容易和预报员解释。
2.4 样本不平衡下的滑动窗口与时间切分
样本构造通常按起报时刻滑动:每 10 分钟或每 1 小时取一个样本,特征用起报时刻之前的数据,标签用之后 1 h、3 h、6 h 的累计降水。正样本少时,不要全局随机过采样,因为同一次强降水过程会产生大量相似样本,全局采样会把同一个过程的样本同时撒进训练和测试。更稳妥的做法是:先在训练折内做加权或欠采样,验证和测试保持原始分布;切分按时间顺序,训练集在前,验证集居中,测试集在后。
具体步骤可以这样定:
- 按
valid_time排序,先划出训练、验证、测试三个时间段。 - 只在训练段内统计正负比,求出
scale_pos_weight或样本权重。 - 对每个起报时刻,检查特征最大时间是否小于标签开始时间。
- 对同一降水过程做分组标记,交叉验证时按过程分组,避免过程内泄漏。
3. 强暴雨机器学习建模最小闭环:时间切分、梯度提升树与概率阈值
3.1 用时间切分替代随机切分的最小代码
时间序列问题里,随机切分是第一个大坑。它会用 8 月样本训练、7 月样本测试,让模型看到未来气候背景。下面这段代码用valid_time做时间切分,训练一个梯度提升树基线,并输出 AUC、PR-AUC 和 Brier 分数。
import numpy as np import pandas as pd from sklearn.ensemble import HistGradientBoostingClassifier from sklearn.metrics import roc_auc_score, average_precision_score, brier_score_loss df = df.sort_values("valid_time").reset_index(drop=True) split_train = pd.Timestamp("2021-01-01") split_valid = pd.Timestamp("2022-01-01") train = df[df["valid_time"] < split_train] valid = df[(df["valid_time"] >= split_train) & (df["valid_time"] < split_valid)] test = df[df["valid_time"] >= split_valid] features = [ "k_index", "shear_0_6", "pw_mm", "cape", "cin_abs", "lcl_km", "srh", "ref_max", "ref_area", "echo_top" ] target = "label_3h_50mm" X_train, y_train = train[features], train[target] X_valid, y_valid = valid[features], valid[target] X_test, y_test = test[features], test[target] # 训练段正负比,用于样本加权 neg = (y_train == 0).sum() pos = (y_train == 1).sum() spw = neg / max(pos, 1) w_train = np.where(y_train == 1, spw, 1.0) model = HistGradientBoostingClassifier( max_iter=300, learning_rate=0.05, max_leaf_nodes=31, l2_regularization=1.0, random_state=42 ) model.fit(X_train, y_train, sample_weight=w_train) valid_prob = model.predict_proba(X_valid)[:, 1] test_prob = model.predict_proba(X_test)[:, 1] print("valid AUC", roc_auc_score(y_valid, valid_prob)) print("valid PR-AUC", average_precision_score(y_valid, valid_prob)) print("valid Brier", brier_score_loss(y_valid, valid_prob))逻辑说明:训练段在前、验证段居中、测试段在后,模拟业务上线时只能看到历史;sample_weight提高正样本权重,缓解极端不平衡。参数说明:max_iter控制树的数量,强暴雨样本少时可先 200 到 500;learning_rate越小通常需要更多树;max_leaf_nodes越大模型越容易记住过程细节;l2_regularization用来压过拟合。AUC 高不代表强暴雨命中好,PR-AUC 和 Brier 更值得盯。
3.2 传统机器学习算法基线:逻辑回归和梯度提升树怎么选
先用简单模型确认特征方向,再上复杂模型,是机器学习实战里省时间的顺序。逻辑回归能看出每个物理量是正贡献还是负贡献,梯度提升树能抓阈值和非线性,随机森林方差小一些,深度模型适合样本多、雷达序列完整的团队。
| 模型 | 可解释性 | 不平衡表现 | 训练速度 | 适合场景 |
|---|---|---|---|---|
| 逻辑回归 | 高 | 依赖权重和校准 | 快 | 入门基线、特征方向检查 |
| 随机森林 | 中 | 较稳,概率偏粗 | 中 | 多特征、小样本 |
| 梯度提升树 | 中 | 好,需调参 | 中 | 表格特征主力 |
| 一维卷积或 LSTM | 低 | 依赖数据和损失 | 慢 | 雷达序列、时空特征充足 |
如果只是机器学习入门,可以手写逻辑回归梯度下降理解损失函数;但做业务原型时,直接用成熟库更省事。机器学习模型可以自己写吗?可以,但先把数据切分、特征时间对齐和评估指标做对,比手写网络结构重要得多。
3.3 概率校准与强暴雨阈值扫描
强暴雨预报最终要给的是概率,不是 0/1。梯度提升树输出的概率常常偏尖,需要用验证集做校准,再扫描阈值。下面代码在验证集上找 TS 最高的阈值,再拿去测试集确认。
import pandas as pd import numpy as np thresholds = np.arange(0.05, 0.95, 0.05) rows = [] for th in thresholds: pred = (valid_prob >= th).astype(int) hits = int(((pred == 1) & (y_valid == 1)).sum()) misses = int(((pred == 0) & (y_valid == 1)).sum()) fas = int(((pred == 1) & (y_valid == 0)).sum()) ts = hits / (hits + misses + fas + 1e-9) far = fas / (hits + fas + 1e-9) rows.append({"threshold": round(th, 2), "TS": ts, "FAR": far, "hits": hits}) scan = pd.DataFrame(rows).sort_values("TS", ascending=False) print(scan.head(10)) best_th = float(scan.iloc[0]["threshold"]) test_pred = (test_prob >= best_th).astype(int) test_hits = int(((test_pred == 1) & (y_test == 1)).sum()) test_misses = int(((test_pred == 0) & (y_test == 1)).sum()) test_fas = int(((test_pred == 1) & (y_test == 0)).sum()) test_ts = test_hits / (test_hits + test_misses + test_fas + 1e-9) print("test TS", test_ts, "best_th", best_th)逻辑说明:阈值只能在验证集或历史回测集上选,不能拿测试集反复挑;测试集只用一次,才是接近真实的检验。参数说明:thresholds从 0.05 到 0.95 扫描,业务上还要看 FAR 能不能接受;如果预报员更怕空报,可以把阈值抬高;如果更怕漏报,就降低阈值并接受更多空报。概率校准可用CalibratedClassifierCV,但要在训练段内部再做一次时间切分,不能直接对全量数据校准。
3.4 类别权重、代价敏感和降水分级输出
强暴雨样本少,类别权重是最直接的代价敏感手段。更细一点,可以把降水分级:20 到 30 mm、30 到 50 mm、50 mm 以上分别建模,或者用 ordinal 思路输出各级概率。业务上不一定只发一个“有/无”,而是给 0 到 20 mm、20 到 50 mm、50 mm 以上三档概率,让预报员看到量级倾向。机器学习模型优化方案里,重采样、代价矩阵、分档建模和多模型集成可以一起用,但每次只改一个变量,否则回测结果没法归因。
4. 强暴雨预报模型检验与排错:TS、FSS、数据泄漏和时间错位
4.1 TS、POD、FAR、ETS 怎么算与怎么看
强降水的检验不能只看准确率。晴雨样本比例悬殊时,全报“无强暴雨”也能有很高准确率。短临业务更常看 TS、POD、FAR、ETS,分别回答命中多少、漏报多少、空报多少、扣掉随机命中后还有多少技巧。
| 指标 | 计算方式 | 关注点 |
|---|---|---|
| TS | 命中 /(命中 + 漏报 + 空报) | 综合命中与空报 |
| POD | 命中 /(命中 + 漏报) | 漏报是否严重 |
| FAR | 空报 /(命中 + 空报) | 空报是否过多 |
| ETS | 扣除随机命中后的 TS | 与气候概率比较 |
| Brier | 概率预测的均方误差 | 概率是否校准 |
| BSS | 相对气候概率的 Brier 技巧 | 是否比气候背景强 |
这些指标要按时间窗、降水量级、区域分开算。一个模型在 1 h 20 mm 上 TS 不错,不代表在 1 h 50 mm 上也能用。机器学习模型输出概率后,先做可靠性曲线,再看阈值扫描表,最后按过程逐个复盘。
4.2 邻域法 FSS 检验:高分辨率强暴雨预报不能只看点对点
强暴雨落区空间误差一两格,点对点评分就会很难看。邻域法 FSS 用一定窗口放宽匹配,更贴近短临预报的实际使用方式。下面是一个简化二值邻域命中示例,真实业务里还会用分数场和基线 FSS。
import numpy as np from scipy.ndimage import maximum_filter def fss_binary(obs, pred, window=5): # obs、pred 为二维 0/1 网格,window 为邻域边长 o = maximum_filter(obs, size=window) p = maximum_filter(pred, size=window) hits = np.sum((o == 1) & (p == 1)) misses = np.sum((o == 1) & (p == 0)) fas = np.sum((o == 0) & (p == 1)) return hits / (hits + misses + fas + 1e-9) # 示例:obs 和 pred 从格点场转成 0/1 后传入 # score = fss_binary(obs_grid, pred_grid, window=5)逻辑说明:先用maximum_filter把观测和预报在邻域内膨胀,再算命中、漏报、空报,得到邻域 TS 类似量。参数说明:window根据检验需求和分辨率定,常见 3、5、9;窗口越大分数通常越高,所以不同模型必须用同一窗口比较。FSS 不能替代点对点 TS,两者要一起看。
4.3 数据泄漏与时间错位的 3 个排查动作
第一,检查特征时间戳。任何特征的生成时间晚于起报时刻,都要删掉或向后平移。第二,检查标签窗口重叠。用 08 时起报预测 08 到 11 时,就不能把 09 时观测放进特征。第三,检查预处理顺序。标准化、插补、特征选择如果放在训练测试切分之前,测试集统计量就会漏进训练。下面这段检查代码可以放进日常流程。
def check_time_leakage(df, feature_cols, issue_col="init_time", label_start_col="label_start"): bad = [] for col in feature_cols: if col in df.columns and df[col].dtype.kind in "iufc": pass # 真正要检查的是每列的特征有效时间,而不是列名 max_feat_time = df["feature_max_time"] label_start = df[label_start_col] leak_rows = df[max_feat_time >= label_start] return leak_rows[["init_time", "feature_max_time", "label_start"]] # leak = check_time_leakage(df, features) # print(leak.head())逻辑说明:这段代码不是靠列名判断,而是靠每列特征的最大有效时间与标签开始时间比较;只要特征最大时间大于等于标签开始时间,就存在泄漏嫌疑。参数说明:feature_max_time是每个样本特征窗口的最后一个时次,label_start是标签窗口起点;init_time用于回溯是哪次起报。
4.4 模型调参顺序:先对齐时间,再谈超参数
调参顺序建议是:数据泄漏排查、时间切分检查、标签定义检查、阈值选择、特征筛选、超参数。很多团队一上来调max_depth、learning_rate,结果真正问题是雷达特征用了未来帧。超参数只在前四步干净后才有意义。每轮实验保留一份配置表:特征版本、训练时间段、验证时间段、正负比、阈值、TS、FAR、Brier。这样换模型或换汛期时,能快速定位是数据变了还是参数变了。
5. 强暴雨机器学习模型的进阶用法:概率集成、漂移监控与预报员会商
5.1 概率集成与多模式融合
单模型在强暴雨上容易受某类过程影响,概率集成能把逻辑回归、梯度提升树、随机森林甚至数值模式降水概率融合。权重不要拍脑袋,用验证集 BSS 或 TS 调,测试集只做最终确认。
import numpy as np # 假设三个模型已在同一验证集和测试集上输出概率 probs_valid = np.vstack([valid_prob_lr, valid_prob_gbdt, valid_prob_rf]) probs_test = np.vstack([test_prob_lr, test_prob_gbdt, test_prob_rf]) # 权重按验证集表现分配,这里只是示例 weights = np.array([0.2, 0.5, 0.3]) ensemble_valid = np.average(probs_valid, axis=0, weights=weights) ensemble_test = np.average(probs_test, axis=0, weights=weights) print("ensemble valid mean", ensemble_valid.mean()) print("ensemble test mean", ensemble_test.mean())逻辑说明:集成前必须保证各模型使用同一起报时刻、同一标签、同一验证时间段,否则概率不可加。参数说明:weights可以按 BSS 网格搜索,但搜索空间不要太大;如果某模型在验证集上 FAR 很高,权重应压低。多模式融合时,数值模式降水概率可以作为一列特征,而不是直接替换机器学习输出。
5.2 在线更新与漂移监控
汛期前后气候背景、雷达标定、模式版本都会变,模型上线后要做漂移监控。常见做法是每周算一次特征分布 PSI、预测概率分布 KL 或 KS,再按月回算 TS、FAR。若某几个物理量分布明显偏移,先查数据源,不要急着重新训练。在线更新可以采用滑动训练窗口,比如始终用最近 3 年同期加最近 1 个月数据,既保留季节规律,也吸收近期样本。
| 监控项 | 频率 | 触发动作 |
|---|---|---|
| 特征均值与分位数 | 每周 | 查数据源和插值 |
| 预测概率分布 | 每周 | 查模型输入漂移 |
| TS、FAR、Brier | 每月 | 评估是否重训 |
| 正样本率 | 每汛期 | 检查标签口径 |
5.3 解释性、会商与业务阈值
预报员不会只看一个概率数字。把 SHAP 值、部分依赖和个例特征贡献做成会商材料,能解释“为什么这次模型给高概率”:是 CAPE 和可降水量配合,还是雷达回波面积快速增长。业务阈值也要分会商场景:内部参考可以低阈值多提示,对外发布要高阈值压空报。最后把 0 到 3 h 强降水概率做成每 10 分钟刷新、带邻域概率和不确定区间的面板,比单纯追一个二分类准确率更接近强暴雨预报真正要解决的问题。
本文还有配套的精品资源,点击获取