简介:这份资源是面向气候科学与机器学习交叉领域研究者、数据科学学习者的端到端管道项目,聚焦干旱预测与理解这一具体问题,提供从数据准备到方法评估的完整流程。项目以 Jupyter Notebook 为主要载体,结合 Copernicus 气候数据与 2019 年相关实践,将创建统一训练测试数据格式、相互比较与评估不同机器学习方法等任务拆分为 src 目录下的多个类,便于按模块复用与扩展。压缩包约 49.31MB,内含 Notebook、Python 源码及环境配置文件,可通过 conda 环境快速搭建运行环境,适合具备 Python 基础的使用者上手。已有 142 人学习,读者可借此理解气候数据预处理、模型对比实验设计与评估思路,并基于现有管道替换数据或方法开展自己的干旱预测实验。
1. 干旱预测的玄学困局:为什么传统指标总在关键时刻翻车
干旱这件事,最反直觉的地方在于——它不是一个"有没有下雨"的问题。某地连续四十天无降水,放在湿润区是灾难前兆,放在干旱区却是常态。传统干旱监测依赖 SPI、SPEI 这类标准化降水指数,本质是把降水量做统计标准化,再套一个阈值分级。这套方法用了半个多世纪,工程上足够稳,但它有一个绕不过去的硬伤:它只看"水进来多少",不看"水怎么被消耗、地表能撑多久"。
这就导致一个经典翻车场景:气象干旱指标还在"正常"区间,农业干旱已经悄悄发生了。因为高温、强蒸散、土壤质地、植被覆盖这些因素,SPI 根本装不进去。机器学习介入干旱预测的价值,恰恰在于它能把这些异质变量塞进同一个模型里,学出"降水—蒸散—土壤—植被"之间的非线性耦合关系。ml_drought 这个项目就是干这件事的:用 Copernicus 体系下的遥感与再分析数据,配合 Jupyter Notebook 做端到端的干旱建模。搞懂它为什么能 work,比会跑通一个 notebook 重要得多。
2. 从气象干旱到综合干旱:机器学习到底在拟合什么
2.1 干旱不是单一变量,而是一个多尺度滞后系统
要理解 ml_drought 的设计,先得接受一个事实:干旱是有"记忆"的。今天土壤缺水,可能是过去三个月降水亏缺累积的结果,也可能是上周一场异常高温把蒸散拉爆了。这种时间上的滞后与累积,正是传统阈值法最无力的地方——它把每个时间窗口独立看待,而真实系统里,前一个月的状态会显著影响这个月的响应。
机器学习模型在这里的角色,不是"预测下雨",而是学习一个状态转移函数:给定过去若干时间步的降水、温度、蒸散、土壤湿度、植被指数,输出未来某个时间点的干旱等级或连续干旱指数。常见的实现思路是把问题构造成监督学习里的序列回归或序列分类,用滑动窗口把时间序列切成样本。窗口长度这个参数非常关键,它直接决定了模型能"记住"多久的历史。窗口太短,模型学不到累积效应;窗口太长,样本数骤减且噪声被放大。我一般会这么理解:窗口长度应该覆盖当地干旱的形成周期,比如半干旱区取 3 到 6 个月,湿润区可以短一些。
这里有个容易被忽略的设计动机:为什么不用纯气象指标做特征,非要引入遥感?因为遥感提供的是"地表真实响应",而不是"大气输入"。降水是输入,土壤湿度和植被指数是输出侧的状态。把输入和状态一起喂给模型,等于让模型同时看到"因"和"果",它才能学到中间的传导机制。这也是 Copernicus 数据体系在这个项目里不可替代的原因——它同时提供气象再分析产品和地表遥感产品,时间与空间分辨率能对齐。
2.2 特征工程:把物理先验塞进模型输入
纯数据驱动的模型在样本有限时很容易学偏,所以 ml_drought 这类项目通常会在特征层面注入物理先验。最典型的是构造"水分亏缺"类特征,比如降水减去潜在蒸散的累积值,这本质上就是 SPEI 的计算逻辑,但把它作为一个特征而不是最终指标。这样做的好处是模型不必从零学"水少了会旱"这个常识,而是站在一个已经有物理意义的基线上继续学非线性修正。
另一个常见做法是对不同滞后阶分别构造特征。比如降水取 lag1、lag3、lag6 三个累积窗口,蒸散取 lag1 和 lag3,土壤湿度取 lag1。这样模型在结构上就具备了捕捉多尺度响应的能力。代码层面通常是一个特征拼接函数:
import pandas as pd import numpy as np def build_lagged_features(df, var_lags): """ df: 含时间索引的原始变量表,列名如 precip, et, soil_moisture var_lags: dict,形如 {'precip': [1, 3, 6], 'et': [1, 3], 'soil_moisture': [1]} 返回:拼接了滞后累积特征的新表 """ out = df.copy() for var, lags in var_lags.items(): for lag in lags: # 滚动累积,模拟干旱的累积效应;min_periods 保证不引入虚假早期值 out[f'{var}_cum{lag}'] = df[var].rolling(window=lag, min_periods=lag).sum() return out.dropna() # 丢掉因滚动产生的缺失头部这段逻辑的关键在rolling(...).sum()而不是shift()。shift只取单点滞后,rolling sum才表达"累积亏缺"这个物理含义。min_periods=lag是为了避免窗口没填满时用部分数据算出偏小的累积值,那会污染训练集。参数上,var_lags的取值不是拍脑袋,应该参考当地干旱的形成时间尺度,并且要在验证集上做敏感性检查——窗口选错,模型在极端干旱年份的表现会明显塌陷。
2.3 标签怎么定义,决定了模型在学什么
这是整个建模里最容易被低估的一环。干旱预测的标签通常有两种来源:一是用现成的干旱指数(如 SPEI)分级作为分类标签,二是用遥感干旱指数(如植被条件指数 VCI)作为回归目标。前者偏气象,后者偏农业。ml_drought 这类项目如果目标是"了解干旱",往往会把两者都做,形成对照。
分类标签的阈值切分有个坑:干旱等级本身是高度不平衡的,极端干旱样本极少。直接训练分类器,模型会倾向于全预测"正常",准确率看着很高但毫无用处。常见的处理是重采样、类别加权,或者干脆把问题转成回归——预测连续干旱指数,再在评估阶段按阈值转等级。回归的好处是保留了序信息,模型不会把"中度"和"极端"当成两个无关类别。代价是回归对极端值的拟合更难,需要在损失函数上做文章,比如对高干旱区间加权。
3. Copernicus 数据管线:遥感与再分析怎么对齐到同一张表
3.1 时空重采样:为什么对齐比建模还费劲
多源数据建模,八成的工程量花在对齐上。Copernicus 体系里,气象再分析产品通常是规则网格、逐小时或逐日;遥感产品可能是不同的空间分辨率、不同的过境时间,还有云污染导致的缺测。要把它们拼成一张"时间 × 站点/网格"的表,必须做时空重采样。
空间上,常见做法是把所有变量重采样到同一个较粗的网格,或者重采样到站点位置做点提取。时间上,遥感产品如果是 8 天合成或 16 天合成,需要决定是取合成期均值还是做时间插值到日尺度。这里没有唯一正确答案,但有一个原则:重采样的方向要跟物理过程匹配。降水适合累积,温度适合平均,植被指数适合取合成期最大值(减少云影响)。用错聚合方式,等于在特征里注入系统性偏差。
# 示意:按变量类型选择聚合方式,避免"一刀切"均值 agg_rules = { 'precip': 'sum', # 降水在时间窗口内累积 'temp': 'mean', # 温度取平均 'et': 'sum', # 蒸散同样累积 'ndvi': 'max', # 植被指数取最大值,抑制云污染 } resampled = raw.resample('1D').agg(agg_rules)resample('1D')把不同原始频率统一到日尺度,agg字典则保证每个变量用它该用的聚合函数。这一步如果偷懒全用mean,降水会被严重低估,模型学到的"干旱"就失真了。
3.2 缺测与云污染:数据质量决定模型上限
遥感数据的缺测不是随机缺失,而是系统性缺失——云多的地方、雨季,缺测更集中。如果直接dropna(),等于把湿润季节的样本大量删掉,训练集分布会偏向干旱季,模型在湿润条件下的泛化能力被悄悄削弱。这是血泪经验:很多人模型指标好看,一换年份就崩,根子就在缺测处理。
常见的补救思路有三种。一是时间插值,用前后有效观测线性填补,适合短缺口。二是气候态填补,用同期的多年均值替代,适合长缺口但会平滑掉异常信号。三是把缺测本身作为一个指示特征(missing indicator),让模型知道这个时间点数据不可靠。第三种在样本量足够时最稳,因为它不伪造数据,而是把不确定性显式交给模型。ml_drought 这类项目在 notebook 里通常会把这几种策略做成可切换的选项,方便对比。
提示:缺测处理策略要在训练集和测试集上用同一套逻辑,且填补统计量只能从训练集计算,否则会造成信息泄漏,验证指标虚高。
3.3 数据泄漏:时间序列建模里最隐蔽的坑
时间序列的交叉验证不能随机打乱。随机切分会让模型在训练时"看到未来",测试指标虚高得离谱。正确做法是按时间切块,比如前 70% 时间做训练,后 30% 做测试,或者用滚动前向验证(walk-forward)。滚动前向验证更贴近真实部署场景:每次用截至某时刻的历史训练,预测下一段,然后窗口前移。
这个原则听起来简单,但实操里经常翻车在特征工程阶段——比如标准化用了全量数据的均值和方差,或者滞后特征在切分边界处跨到了测试集。这些都属于隐性泄漏。稳妥的做法是把特征构造和标准化都封装进一个 pipeline,在每次切分内部 fit,而不是在全局 fit 一次。
4. 模型选型与训练:为什么梯度提升树常是这类任务的默认答案
4.1 树模型 vs 神经网络:样本量决定路线
干旱预测的样本量通常不大——几十年的月度数据,站点或网格数量有限,有效样本可能就几千条。在这个量级上,深度神经网络容易过拟合,而梯度提升树(如 XGBoost、LightGBM)对中小样本、异质特征、非线性交互的适应性更好,且对特征尺度不敏感,省掉了大量预处理。这不是说神经网络不行,而是说在样本受限时,树模型的性价比更高。
树模型的另一个优势是可解释性。特征重要性、部分依赖图能告诉你"模型到底在看什么",这对干旱这种需要物理解释的领域很重要。如果模型说未来干旱加剧,但特征重要性显示它主要靠某个无关变量,那这个预测就不可信。ml_drought 的 notebook 里通常会包含特征重要性可视化,这不是装饰,而是模型可信度的一部分。
4.2 超参数与验证:别让调参调出过拟合
树模型的超参数里,对干旱预测影响最大的是树的深度/叶子数、学习率、以及正则化项。深度太深,模型记住训练年份的噪声;太浅,学不到多变量交互。常见做法是先固定一个较小学习率(如 0.05),用早停(early stopping)在验证集上确定树的数量,再网格搜索深度和正则参数。
验证集必须来自时间上的后段,且不能和测试集重叠。如果数据量实在少,可以用滚动前向验证的每一折做超参数评估,取平均表现最好的配置。这里有个容易忽略的点:早停的监控指标要和最终评估指标一致。如果最终看的是极端干旱的召回率,早停却盯着整体 MSE,那模型会偏向拟合平均状态,极端事件照样漏。
# 示意:时间序列友好的早停配置 model = lgb.LGBMRegressor( learning_rate=0.05, num_leaves=31, min_child_samples=20, # 防止叶子过小,抑制过拟合 subsample=0.8, colsample_bytree=0.8, n_estimators=2000, ) model.fit( X_train, y_train, eval_set=[(X_val, y_val)], eval_metric='l2', callbacks=[lgb.early_stopping(50), lgb.log_evaluation(100)], )min_child_samples在小样本场景下尤其重要,它限制了每个叶子至少覆盖多少样本,避免模型为个别极端年份单独开叶子。subsample和colsample_bytree引入随机性,提升泛化。早停的50轮耐心值不宜太小,否则验证曲线的小波动会导致过早停止。
4.3 评估指标:准确率会骗人,要看极端事件
干旱预测的评估不能只看 RMSE 或准确率。极端干旱样本少,模型即使完全漏掉它们,整体指标依然好看。必须单独看极端区间的表现,比如高干旱等级上的召回率、F1,或者回归任务里对高值段的 MAE。另一个有用的做法是画预测-观测散点图,看模型在高值端是否系统性低估——树模型因为对极端值做平均,天然倾向于压缩极值,这个偏差必须被看见。
如果目标是业务预警,还要考虑"提前量"和"误报代价"的权衡。提前太久,不确定性大、误报多;提前太短,预警价值低。这个权衡没有通用答案,得结合具体应用场景定,但评估时必须把不同提前量下的指标都列出来,而不是只报一个最好看的数。
5. 避坑清单:干旱建模里那些让人后悔药的细节
5.1 现象:验证指标很好,换一年就崩
原因通常是数据泄漏或分布偏移。检查标准化是否在全局 fit、滞后特征是否跨了切分边界、缺测填补是否用了未来信息。解决:把所有预处理封装进 pipeline,按时间切分内部 fit;对每一年的表现单独评估,看是否存在系统性偏移。
5.2 现象:模型几乎不预测极端干旱
原因是类别不平衡加树模型的极值压缩。解决:回归任务对高值段加权损失,分类任务用类别权重或重采样;评估时单独看极端区间指标,别被整体准确率麻痹。
5.3 现象:降水特征重要性异常低
原因是聚合方式用错,比如降水用了均值而非累积,导致特征信息量被抹平。解决:按变量物理含义选聚合函数,降水、蒸散用累积,温度用均值,植被指数用最大值;改完重跑特征重要性对照。
5.4 现象:不同年份的干旱指数尺度不一致
原因是标签来源或计算窗口不统一,比如有的年份用 3 个月 SPEI,有的用 6 个月。解决:标签定义必须全时段一致,窗口长度在建模前固定,并在文档里写清楚,避免后续对比时口径混乱。
5.5 现象:notebook 能跑,换数据就报错
原因是硬编码了列名、时间格式或网格尺寸。解决:把数据读取和列映射抽成配置,输入校验前置,缺列或时间不连续时尽早报错,而不是等到模型训练阶段才崩。
6. 设计取舍:为什么"能解释"比"高一分"更值得追求
干旱预测这类问题,模型指标和实际价值之间隔着一层东西:可信度。一个 RMSE 低 5% 但特征重要性讲不通的模型,在业务侧很难被采纳;反过来,一个指标稍逊但每一步都能对应到物理过程的模型,更容易被信任和迭代。ml_drought 用 Jupyter Notebook 组织全流程,本身就是一种取舍——它牺牲了工程化的封装度,换来了每一步的可检查性。你可以逐单元格看数据长什么样、特征怎么构造、模型学了什么,这对理解和调试的价值,远大于一个打包好的黑匣子。
另一个取舍在数据源。Copernicus 体系的数据质量高、变量全,但下载和预处理成本不低。如果换成更轻量的数据源,建模速度会快,但特征丰富度下降,模型能学到的耦合关系变少。这个权衡没有标准答案,取决于你是要做快速原型还是做可复用的分析基线。我的判断是:如果目标是"了解干旱机制",数据丰富度优先;如果目标是"快速出预警",可以先窄后宽,先跑通最小闭环再逐步加变量。
验证一个干旱模型是否真的 work,我习惯做三件事:一是按年份滚动评估,看指标稳定性而不是平均值;二是看极端事件上的表现,漏报比误报更值得警惕;三是做特征消融,去掉某类变量看性能掉多少,掉得合理才说明模型真的在用物理信号。这三步走完,模型能不能信,心里基本有数。
从那以后我每次碰时间序列建模,都强制先画一张时间轴上的训练/验证/测试切分图,确认没有任何未来信息漏进去,再谈调参。这个习惯救过我很多次。希望帮到你。
本文还有配套的精品资源,点击获取