
简介这套代码是一套面向气候科学的端到端机器学习管道源自 GitHub 上的 ml-clim/ml_drought 项目核心目标是借助机器学习提高干旱预测能力并加深对干旱机制的理解。管道将数据标准化、训练集划分、模型训练、结果比较与评估整合为统一流程各步骤按模块组织在 src 目录下并用环境配置文件锁定依赖使用者只需具备基础 Python 知识即可沿管道完成从数据处理到模型评估的闭环实验适合气候研究人员和希望将机器学习落地到气象场景的开发者使用。资源包为 zip 压缩格式大小约 49.31MB页面未单独列出文件总数与类型明细从项目说明来看包内包含 environment.yml 环境配置文件、Python 源码和用于演示的 Jupyter Notebook 样例可通过 conda 一键创建 esowc-drought 环境并复现实验结果。已有 142 人学习。读者可以得到一套可复用的干旱预测工作流学习如何模块化组织数据预处理、建模与评估环节并方便地迁移到其他气候变量的机器学习研究中项目结构和模块划分清晰也适合作为后续扩展与二次开发的参考实现。1. ml_drought 到底是什么一个能复现的机器学习干旱预测项目第一次打开ml_drought这个仓库时我的第一反应是又一个用机器学习打包的 demo——直到我顺着它的 Notebook 跑完一遍才发现它把干旱预测这件事拆成了完整的链路从 Copernicus 的 ERA5 再分析数据下载到标准化降水蒸散指数SPEI计算再到随机森林训练与时间序列验证。项目挂在 github.com 上的ml-clim组织下2019 年的代码Jupyter Notebook 为主不 fancy但管线干净。它要解决的核心问题是不靠物理数值模式仅用机器学习对再分析数据做统计建模能不能提前几个月识别干旱前兆。复现成本很低一台普通笔记本就能跑通适合刚接触气候数据、又想上手机器学习完整流程的人也适合想知道机器学习在气象里到底怎么落地的从业者。这一篇我按拆过的资源来写尽量把每一步都说得能直接操作。2. 数据管线Copernicus 再分析数据怎么变成训练集2.1 你需要先装齐的工具清单ml_drought 的 Notebook 依赖不算重核心是xarray、netCDF4、pandas、scikit-learn外加一个下载 ERA5 数据用的cdsapi。我第一次复现时直接pip install一把梭结果版本冲突卡了半小时后来固定了环境才顺畅。建议先建一个干净的虚拟环境再装python -m venv ml_drought_env source ml_drought_env/bin/activate # Windows 下用 ml_drought_env\Scripts\activate pip install pandas numpy xarray netCDF4 scikit-learn matplotlib pip install cdsapi # Copernicus CDS 下载专用cdsapi是访问 Copernicus Climate Data Store 的官方 Python 客户端没有它你没法自动拉 ERA5 数据xarray负责读取 NetCDF 格式的再分析场netCDF4是它的底层引擎。装完记得验证一下导入是否正常缺底层库时xarray导入会报ImportError这时候按报错信息补包就行。2.2 CDS API 配置下载 ERA5 最常见的卡点下载 ERA5 不需要人工点网页但需要先在 CDS 网站注册账号、拿到 API Key然后写入本地配置文件。这个 Key 在~/.cdsapirc里格式固定url: https://cds.climate.copernicus.eu/api key: 你的UID:你的API Key我一般会在下载脚本里加一个重试机制因为 ERA5 的下载队列经常排队单次请求失败很常见。下面的脚本是 ml_drought 这类项目里通用的取数写法按月度降水、2m 气温、蒸散量三个变量拉 1979 年至今的数据import cdsapi c cdsapi.Client() c.retrieve( reanalysis-era5-single-levels-monthly-means, { product_type: monthly_averaged_reanalysis, variable: [ total_precipitation, 2m_temperature, potential_evaporation ], year: [str(y) for y in range(1979, 2020)], month: [f{m:02d} for m in range(1, 13)], time: 00:00, format: netcdf, }, era5_monthly.nc, )这段代码的意图很直白请求月度平均的 ERA5 单层数据把三个变量一次性打包成一个 NetCDF 文件。variable列表里的三项决定了你后续能算什么指数——total_precipitation是降水项potential_evaporation用来算蒸散2m_temperature是温度项。year和month用列表推导式生成比手写长字符串省事。如果网络环境不稳建议外面再套一层for循环按年逐次请求失败则等 30 秒重试避免整段任务因为一次超时全部作废。2.3 把 NetCDF 读成表格区域裁剪与时间维处理ERA5 下载下来是全球 0.25° 网格直接整场建模既慢又没必要。ml_drought 的做法是先裁剪目标区域再聚合成以月为单位的 DataFrame。xarray的sel方法可以按经纬度切片ds.mean(dim...)可以算区域平均。这一步的细节决定后面特征矩阵长什么样import xarray as xr import pandas as pd ds xr.open_dataset(era5_monthly.nc) # 裁剪地中海区域lat 在前lon 在后是 xarray 的默认维度顺序 region ds.sel(latslice(30, 45), lonslice(-10, 40)) # 按区域平均把 3D 场压缩成时间序列 df region.mean(dim[lat, lon]).to_dataframe().reset_index() # 构造时间索引 df[time] pd.to_datetime(df[time]) df df.set_index(time).sort_index() df.to_csv(mediterranean_era5.csv)sel(latslice(30, 45), lonslice(-10, 40))是区域裁剪的常用写法注意slice的顺序是上界在前、下界在后反过来会得到空数据。mean(dim[lat, lon])把整个区域的场平均成单条时间序列这个操作很粗暴但作为快速原型够用如果后续要做空间分布预测得保留经纬度维度改成按格点单独建模。最后.to_dataframe()把xarray.Dataset拍平成pandas.DataFrametime变量会自动变成索引。存成 CSV 之后机器学习部分就跟普通表格数据没什么区别了。注意ERA5 的time变量在月度平均数据里是每月 1 号的 00:00下游算滞后特征时直接用shift即可不需要重采样对齐。3. 特征工程与模型选型干旱标签怎么来模型怎么挑3.1 SPEI 计算把连续气象变量变成干旱等级干旱不是一个可以直接测量的量气象上常用标准化降水蒸散指数SPEI来定义干不干、干到什么程度。ml_drought 的做法是把降水与潜在蒸散的差值拟合成概率分布再映射成标准正态分位数。SPEI 的经典范围是 -2 到 2正值偏湿负值偏干。计算的核心是对差值序列做 gamma 分布拟合scipy.stats.gamma足够处理from scipy.stats import gamma import numpy as np # 用降水与蒸散差构造水分盈余序列 df[water_balance] df[total_precipitation] - df[potential_evaporation] # 逐月拟合 gamma 分布转换为标准化分位数 data df[water_balance].values # gamma 分布要求正值做偏移保证输入为正 offset -np.min(data) 0.1 fit_data data offset shape, loc, scale gamma.fit(fit_data) spi_values gamma.cdf(fit_data, shape, locloc, scalescale) df[spei] norm.ppf(spi_values) # 标准正态逆变换这是 SPEI 的一种简化实现正统做法还要按月份分组拟合因为 1 月和 7 月的水分平衡分布并不同。gamma.fit返回三个参数shape控制分布形态loc是位置偏移scale是尺度这三者直接决定了 CDF 转换的结果。norm.ppf把累积概率映射到标准正态分位数得到最终的 SPEI。实战中我更推荐直接用spei库或climate_indices库但手动算一遍能帮你理解标签是怎么来的。得到 SPEI 后按阈值切分成类别常见的分级如下SPEI 范围分类是否算干旱事件 0.5偏湿否-0.5 ~ 0.5正常否-1.0 ~ -0.5轻度干旱弱干旱-1.5 ~ -1.0中度干旱是 -1.5重度干旱是3.2 滞后特征让模型看见过去而不是猜未来机器学模型不会自动理解时间你要把前几个月的降水、气温、SPEI 显式地拼到当前样本里模型才能学到连续三个月降水偏低之后第四个月出现干旱的概率上升这类规则。pandas.shift是构造滞后特征最直接的工具# 构造 1、3、6 个月的滞后特征 for lag in [1, 2, 3, 6]: df[fspei_lag{lag}] df[spei].shift(lag) df[ftp_lag{lag}] df[total_precipitation].shift(lag) df[ft2m_lag{lag}] df[2m_temperature].shift(lag) # 删掉前 6 个月没有完整滞后特征的样本 df df.dropna()shift(lag)把整列下移lag1表示上个月的值lag6表示半年前的值。选择 1、2、3、6 这几个滞后窗口是气候预测里的常见做法1 到 3 个月捕捉短期惯性6 个月捕捉季节尺度信号。dropna()必不可少否则前 6 行样本的滞后特征全是NaN模型训练时会报错或者静默丢弃。你也可以加入滚动均值比如rolling(3).mean()但要注意滚动窗口和滞后长度的信息重复问题加太多冗余特征会让模型过拟合。3.3 为什么从随机森林和逻辑回归开始ml_drought 仓库里最早用的是随机森林和逻辑回归这是 2019 年前后气候机器学习项目的标准搭配。逻辑回归的优势是系数可以直接解释成某个滞后特征每增加一个单位干旱概率变化多少非常适合理解机制随机森林则能捕捉非线性关系对特征缩放不敏感几乎不需要调参就能出基线。它们的训练成本都极低在几千条样本上秒级完成适合做基准对比from sklearn.ensemble import RandomForestClassifier from sklearn.linear_model import LogisticRegression feature_cols [c for c in df.columns if lag in c] X df[feature_cols] y (df[spei] -1.0).astype(int) # 1 表示干旱事件 models { logistic: LogisticRegression(max_iter1000, class_weightbalanced), random_forest: RandomForestClassifier( n_estimators300, max_depth6, min_samples_leaf10, class_weightbalanced, random_state42, ), }y的构造方式影响建模目标把 SPEI 小于等于 -1.0 视为一次干旱事件这是二分类如果你想要分级预测改成三分类或多分类即可。class_weightbalanced是针对干旱样本偏少的常用手段让模型在训练时给少数类更高的权重逻辑回归里它等价于调整决策阈值随机森林里它影响叶子节点的分裂权重。max_depth6和min_samples_leaf10是随手设置的保守参数目的是防止树长太深、在干湿年份交替的数据上记忆噪声。4. 训练与验证Jupyter Notebook 里的全流程怎么组织4.1 时间序列交叉验证随机 shuffle 是第一个大坑气候数据按时间排序相邻月份高度相关。如果直接用train_test_split(shuffleTrue)训练集里混着未来的样本模型会偷看答案验证分数虚高。ml_drought 这类项目必须用时间序列交叉验证常见做法是按年份切块前 N 年训练、后 1 年验证然后滚动import numpy as np from sklearn.model_selection import TimeSeriesSplit tscv TimeSeriesSplit(n_splits5) for train_idx, val_idx in tscv.split(X): X_train, X_val X.iloc[train_idx], X.iloc[val_idx] y_train, y_val y.iloc[train_idx], y.iloc[val_idx] # 每一折都用过去预测未来TimeSeriesSplit的机制是把样本顺序切分成递增的训练块——第一折训练集占前 20%第二折占前 40%以此类推测试块永远在训练块之后。这保证验证过程不会用到未来信息。n_splits5意味着至少需要 6 年以上数据数据太短时减到 3。这个环节的误用是换汤不换药有人用了TimeSeriesSplit但特征里却包含了未来月份的滞后值交叉验证形同虚设这一点我放到第五章细说。4.2 训练、评估与混淆矩阵别只看准确率干旱事件在总样本里占比通常不到 20%准确率这个指标会被绝大多数月份不干旱的多数类拉高没有参考价值。ml_drought 的 Notebook 里用的是混淆矩阵加 F1 分数配合精确率和召回率一起看from sklearn.metrics import classification_report, confusion_matrix rf models[random_forest] rf.fit(X_train, y_train) y_pred rf.predict(X_val) print(classification_report(y_val, y_pred, target_names[正常, 干旱])) print(confusion_matrix(y_val, y_pred))classification_report输出每一类的精确率、召回率、F1干旱类的召回率尤其重要——漏报一次干旱事件比误报一次正常月份代价高得多。随机森林还自带特征重要性rf.feature_importances_可以直接画条形图看哪些滞后特征起了作用。这一步属于结果可解释的起点也是项目名里 understand drought 的含义所在。4.3 从 Notebook 到参数化脚本换个区域不用改代码Jupyter Notebook 适合探索不适合反复跑实验。我复现 ml_drought 之后做的第一件事就是把 Notebook本文还有配套的精品资源点击获取