三个月前,我接了个天气预测的活儿,一开始觉得自己捡了个便宜——历史数据有、模型会调包,无非是拿TimechoAI拉一堆特征丢进回归模型完事。结果真上手才发现,天气预测这套东西,光整理那21个气象指标就差点把我逼疯。从数据接入、指标口径统一,到特征工程和模型上线,我整整折腾了三个月,中间一度怀疑自己是不是根本不懂气象,直到把每一个指标背后的物理含义和模型里的实际表现都摸透了,才算真正"整明白"。这篇我就把这三个月踩过的坑、验证过的结论,还有最后留下的指标清单一次性讲透。
如果你也想用TimechoAI或者类似工具做天气预测、时序预测,或者只是想知道"预测温度到底该看哪些特征",这篇应该能帮你少走我走掉的那一半弯路。
1. 三个月前我为什么一头扎进TimechoAI做天气预测
1.1 天气预测的活儿,难点从来不在模型
很多人跟我一开始的想法一样:天气预测不就是个回归问题吗?温度、湿度、气压、风速,一堆历史数据丢进LightGBM或者LSTM,输出未来24小时温度,完事。真做起来才明白,气象数据的复杂度远超一般业务数据。
天气数据有非常强的自相关性、周期性、空间相关性,而且21个基础指标之间不是独立变量——温度和湿度耦合,气压和风速耦合,云量和太阳辐射耦合。一个模型能不能在这堆纠缠的指标里学到稳定规律,直接决定预测准不准。而这一步的前提,是你得先把每个指标的含义、单位、物理范围、采集频率搞清楚,否则后面全是糊涂账。
模型反而是最简单的部分。数据整明白了,换模型只是换个损失函数的问题。
1.2 TimechoAI解决了我最头疼的数据底座问题
做天气预测,数据底座绕不开时序数据库。气象站的数据天然就是带时间戳的时序数据,一小时一条,一个站点几年下来就是几万条。如果用传统关系型数据库存,查询和聚合的效率一言难尽,更别说还要做滑动窗口、时间对齐这些时序专用操作。
我这边存量数据已经落在TDengine里面,选TimechoAI的原因很直接:它和TDengine是同一套体系,能直接在时序数据上做特征工程和模型训练,不用把几千万行数据导来导去。以前用Python脚本从数据库拉数、拼DataFrame、再写一堆清洗逻辑,现在在TimechoAI的工作台里,大部分数据接入和特征配置能直接编排,省掉了很多重复劳动。
当然,工具只是省力,该懂的指标一个都不能少。
2. 21个气象指标全拆解:哪些能直接拿,哪些得靠算
2.1 第一类:气象站直接观测的"一手指标"
先说结论,这21个指标我按来源分成三类:直接观测、衍生计算、统计发布。绝大部分指标是气象站传感器直接测出来的,也是最可靠的数据源。
我整理的完整清单如下表,你可以直接抄走当参考。
| 编号 | 指标 | 单位 | 类型 | 在天气预测里的作用 |
|---|---|---|---|---|
| 1 | 2m气温 | ℃ | 直接观测 | 温度预测的核心目标/特征 |
| 2 | 最高气温 | ℃ | 直接观测 | 反映白天升温上限 |
| 3 | 最低气温 | ℃ | 直接观测 | 反映夜间辐射冷却下限 |
| 4 | 相对湿度 | % | 直接观测 | 判断近饱和程度,降水/雾的关键 |
| 5 | 露点温度 | ℃ | 衍生计算 | 反映空气实际水汽含量 |
| 6 | 比湿度 | g/kg | 衍生计算 | 不受温度影响的水汽密度指标 |
| 7 | 降水量 | mm | 直接观测 | 降水目标/特征,区分雨强 |
| 8 | 降水概率 | % | 统计发布 | 概率预报,短期参考意义有限 |
| 9 | 海平面气压 | hPa | 直接观测 | 指认高/低压系统位置 |
| 10 | 地面气压 | hPa | 直接观测 | 海拔订正前的气压,反映本地条件 |
| 11 | 10m风速 | m/s | 直接观测 | 风预测目标/特征 |
| 12 | 10m风向 | ° | 直接观测 | 气团来源方向,影响温度/湿度 |
| 13 | 阵风 | m/s | 直接观测 | 极端风事件特征 |
| 14 | 总云量 | 0-1 | 直接观测 | 影响辐射和最高温,关键特征 |
| 15 | 低云量 | 0-1 | 直接观测 | 短临降水前兆 |
| 16 | 能见度 | km | 直接观测 | 雾、霾判定指标 |
| 17 | 太阳辐射 | W/m² | 直接观测 | 地表增温的热力来源 |
| 18 | 紫外线指数 | 无量纲 | 衍生计算 | 云量与辐射的综合产物 |
| 19 | 蒸发量 | mm | 直接观测 | 水循环与干旱监测 |
| 20 | 雪深 | cm | 直接观测 | 低温维持与融雪预报 |
| 21 | 体感温度 | ℃ | 衍生计算 | 温度/湿度/风速的综合感知量 |
直接观测指标里,最核心的是温度、湿度、气压、风、降水、云量这几组。每一组内部都存在物理关联,比如气压突然下降往往意味着低压系统靠近,后面大概率跟着风力和降水增强;云量增加会弱化白天的太阳辐射,抑制最高气温。这些物理关系,在特征工程里是可以直接利用的"先验知识"。
2.2 第二类:需要二次加工的气象衍生量
衍生计算指标里,露点温度、体感温度、比湿度这三个尤其重要,因为它们比原始观测量更"稳定",也更接近物理本质。
露点温度是指在当前水汽压下,空气冷却到水汽饱和时的温度。它跟相对湿度最大的区别是:露点不受气温影响,直接反映空气里有多少水汽。计算露点我用的Magnus公式:
γ = 17.62 b = 243.12 α = ln(RH/100) + (γ * T) / (b + T) Td = (b * α) / (γ - α)T是当前气温(℃),RH是相对湿度(%),Td就是露点(℃)。举个例子,气温30℃、相对湿度60%时,露点算出来大约是21.4℃。这意味着如果夜里气温降到21.4℃,空气就会饱和,出现凝露或雾。
体感温度把温度、湿度、风速揉在一起,公式有很多版本,我用过的一个简化版是这样的:
e = (RH/100) * 6.105 * exp(17.27 * T / (237.7 + T)) AT = T + 0.33 * e - 0.7 * ws - 4.0e是水汽压(hPa),ws是风速(m/s)。这个值在高温高湿天和低温大风天,跟实际体感误差不大,至少能抓住"湿热比干热难受得多"这个规律。
比湿度则是绝对湿度的表达,单位g/kg,不受气温体积膨胀影响,在高空分析和强降水研究里比相对湿度更实用。计算需要先求水汽压e,再结合气压P:
q = 0.622 * e / (P - 0.378 * e)2.3 同一指标在预测目标和特征之间随时切换
这21个指标里,没有谁是绝对的目标或绝对的特征,完全看你预测什么。
预测未来24小时最高气温,那么历史最高气温是特征;预测未来一周的逐小时温度,那么当前2m气温是特征,同时也是要预测的目标序列本身。预测降水时,温度、湿度、气压都是特征,降水量才是目标。我在前两个月里反复栽跟头,就是因为没有在一开始就把"这个任务的目标变量是谁""哪些指标不能提前知道"想清楚。
尤其要注意的是"未来才知道的指标"。比如你现在要预测明天下午2点的温度,那就不能用明天下午2点的实测最高气温当特征,这是最典型的数据泄漏,模型在训练集上效果好到飞起,一上线就崩。
3. 数据清洗与时间对齐:三个月里最隐蔽的隐性消耗战
3.1 21个指标的时间戳对齐,差点让我把键盘砸了
气象站不同传感器的上报频率可能不一致。有的整点报,有的每5分钟报,还有的夜间维护直接断档。如果直接把原始数据按各自时间戳丢给模型,TimechoAI这边首先就会因为时间频率不统一报错,就算不报错,特征之间的"错位"也会让模型学到一堆虚假关系。
我最后统一成整点小时频率。具体做法是:以小时为单位做重采样,每个指标先取该小时内的均值,能通过标准SQL聚合的就用SQL完成,剩下需要在时序管道里对齐的就用插值补齐。TDengine天然的时序索引让这类对齐操作比传统数据库快不少,TimechoAI里也可以直接用时间对齐算子处理。
这里有个细节:对齐之前一定要先确认时区。我刚开始没注意,把某站点的UTC时间和本地时间混在一个数据集里,直接导致"凌晨3点的温度"和"上午10点的温度"混在一起,模型完全学不到日变化规律。后来我强制所有表统一用UTC+8存储时间戳,并在接入时显式声明时区,这个坑才算填平。
3.2 缺失值补法有讲究:不能一股脑填充
气象数据的缺失比例通常不高,但缺失形态很关键。我统计下来,21个指标里,降水、能见度、太阳辐射的缺失率最高,经常集中在半夜或维修时段。
针对不同情况,我用了三种处理策略:
- 缺失2小时以内:线性插值。相邻时次的趋势是可靠的,线性插值不会引入明显误差。
- 缺失6小时以内:用前一天同时刻的值做基准,叠加上相邻时次的趋势修正。比如昨天凌晨2点到8点温度一直在降,今天同一时段缺失,就按"昨天基准 + 今天前几个小时的温差趋势"补。
- 缺失超过24小时:直接整段剔除。强行补出来的数据对模型造成的污染,比缺失本身更严重。
千万别在这时候偷懒用全局均值填充。天气数据有强自相关,全局均值会直接抹掉日变化和季节性,等于给模型灌迷魂汤。我一开始用均值补过一段温度,结果模型短期预测的MAE直接变差了0.6℃,后来排查半天才找到原因。
3.3 异常值不是垃圾,是气象仪器的"求救信号"
气象数据有一些超越物理极限的值,比如温度填了个-99,湿度填了个150%,这种是传感器故障,直接删。但更棘手的是"看似合理、实际突变"的值,比如50分钟前还是晴天,突然记录到300mm/h的降水量——这几乎不可能是真实天气,反而是仪器进了水或者被异物遮挡了。
我做了两层筛选:
第一层是物理范围检查,超出合理区间的直接剔除。我的取值范围给到比较保守的安全区间:
| 指标 | 合理范围 |
|---|---|
| 2m气温 | -55 ~ 55 ℃ |
| 相对湿度 | 0 ~ 100 % |
| 海平面气压 | 870 ~ 1085 hPa |
| 10m风速 | 0 ~ 60 m/s |
| 小时降水量 | 0 ~ 200 mm |
第二层是滑动窗口突变检测。用过去3小时的滑动均值算z-score,阈值设在4以上才算异常。为什么阈值不设成常见的3?因为天气系统本身就会产生快速变化,比如冷锋过境时,一小时降温8℃、风速翻倍,都是真实天气,阈值设太低会误删,导致模型丢失对"剧烈天气"的认知。
顺便说一句,被删除的异常点我不会不管,而是单独存一张异常记录表。后面如果发现某类异常频繁出现在同一时段,说明是仪器问题而不是天气问题,需要在特征里加上一个"该时段是否经常缺测"的标记,防止模型错误学习到周期性假特征。
4. 特征工程实战:如何把21个指标变成模型的长期口粮
4.1 滞后特征和滑动窗口:天气也是一个"记仇"的系统
天气系统有很强的惯性。今天的大气状态不会瞬间跳变,昨天的温度、湿度对明天的天气仍有显著影响。所以特征工程的第一步,就是给每个关键指标构造滞后特征和滑动窗口特征。
以预测未来24小时逐时温度为例,我给2m气温构造了这些滞后项:
- 滞后1小时、3小时、6小时、12小时:捕捉短时惯性
- 滞后24小时:捕捉"同一时刻的昨日温度"
- 滞后168小时:捕捉"同一时刻的上周温度",用来压制周尺度波动
滑动窗口则构造了过去3小时均值、过去6小时温变斜率、过去24小时最高/最低温、过去7天同时刻温度均值。这些窗口值的意义在于,它们不只是某个时间点的离散快照,而是把"近期趋势"和"历史背景"都折叠成了模型可用的特征。
TimechoAI里配置这些滞后和滑动窗口可以直接在特征面板上用算子完成,不用在Python手撸循环,效率高很多。但我提醒一句:滞后特征和窗口宽度不能贪多。滞后项超过7个后,特征之间的共线性会急剧上升,树模型还好,线性模型和部分神经网络会被共线性拖垮。
4.2 小时与季节的周期性编码:一个sin/cos解决边界突跳
天气预测任务里,小时和季节是必须进特征的,但直接用"hour_of_day=0"和"hour_of_day=23"这种数字,模型会认为两者相差很远,实际上它们在日周期里是相邻的。用"day_of_year=1"和"day_of_year=365"也同理,1月1号和12月31号在季节上是邻居,数值上却差了364。
解决办法是用三角函数做周期编码:
hour_sin = sin(2π * hour / 24) hour_cos = cos(2π * hour / 24) day_sin = sin(2π * day_of_year / 365) day_cos = cos(2π * day_of_year / 365)这样一来,0点和23点在二维编码空间里距离非常近,1月和12月也相邻,模型不会再被数字表面的大小误导。这个方法我强烈建议做时间序列的人必须使用,就算TimechoAI没有现成算子,自己写也只要两行代码。
4.3 手工交互特征:温度和露点差是降水的关键前兆
模型能自动学习特征交叉,但有些物理规律你不会希望它"慢慢摸索",直接告诉它更好。我最常用也最有效的一组交互特征,是温度与露点的差值,简称露点差。
当露点差小于2℃时,空气接近饱和,很容易形成雾或毛毛雨;小于4℃且配合上升气流条件,对流性降水概率明显增加;大于15℃则基本是干燥晴天。我把这个差值直接作为特征灌给模型,比把温度和湿度分开让模型自己乘来乘去,学得快且稳得多。
类似的交互特征还有:温度×太阳辐射(反映有效增温能力)、相对湿度×风速(反映蒸发冷却强度)、海平面气压与地面气压的差值(反映气压梯度,梯度越大风力越强)。
4.4 哪些指标能不做标准化就扔进模型?不存在的
除了树模型对量纲不敏感,其他模型几乎都需要标准化。我的做法是:对LightGBM/XGBoost这类梯度提升树,数值型指标保持原始量纲即可;对LSTM、GRU、TCN以及任何带正则化的线性模型,全部做标准化。
标准化的关键雷区在于:只能用训练集的均值方差去变换测试集,不能在整个数据集上先算均值方差再切分。否则训练集里混进了"未来的统计信息",同样是数据泄漏,而且特别隐蔽。TimechoAI的标准化算子默认支持按时间切分后的独立拟合,但用之前一定要确认管道的执行顺序,别让框架帮你把全局统计量算进去了。
5. 模型训练踩坑集:从移平均baseline到LightGBM和时序网络
5.1 先建个"气候学基线",不然你不知道模型是真的香还是假的好
天气预测有个特别容易让人误判的陷阱:随便跑一个模型,MAE看起来好像还行,但你可能打不过最朴素的"历史同期均值"。
我先建了一个气候学基线——用过去5年同一天同一小时的气温均值作为预测值。这个啥特征都不用的基线,在温度预测任务上MAE轻松做到2.4℃。如果模型的MAE连2.4℃都打不过,那说明特征工程和模型选择都有问题,别急着调参。
再往下建一个更复杂的基线:移平均预测,也就是用过去24小时温度的平均值作为明天同一时刻的预测。这个基线大概在2.1℃左右。我后来的LightGBM模型跑到了1.8℃,才算真正有说服力。
我建议所有做天气预测的人,不管用什么工具,第一步都先跑这两个基线,把"及格线"立起来,再去堆特征。
5.2 模型对比:树模型打底,时序网络攻坚
我在这三个月里试了四类模型,结果可能跟很多人想的不一样。
| 模型 | 优点 | 缺点 | 我这里的实测表现 |
|---|---|---|---|
| 线性回归 | 简单、可解释 | 学不了非线性关系 | MAE 2.5℃左右,跟基线差不多 |
| LightGBM | 训练快、特征重要性方便、对表格特征非常强 | 外推能力弱,没见过的高温区间容易失效 | MAE 1.8℃,主力模型 |
| LSTM | 能学长期依赖 | 数据量不足时严重过拟合,调参成本高 | MAE 2.1℃,反而不如LightGBM |
| TCN | 感受野可控、训练比LSTM快 | 超参数更敏感,需要足够的时序长度才有优势 | 还没完全调通,短期看逼近LightGBM |
核心结论:在中小规模气象数据集上(单站一年也就8760小时记录),树模型加手工特征通常是性价比最高的方案。LSTM这类模型虽然听着高级,但数据量不够时真的不如把特征工程做好再喂给LightGBM。
5.3 时序切分和数据泄漏:机器学习的老规矩,在时间序列上会加倍还回来
常规机器学习里random split是把样本随机打散成训练集和测试集,这一套在时序预测里完全不能用。天气数据相邻时次高度相关,如果测试集和训练集混在一起,模型等于见过"答案附近的上下文",测试指标会虚高得非常离谱。
我最后用的是滚动切分:按时间顺序,前70%做训练集,中间15%做验证集,最后15%做测试集。验证集特意选了包含完整四季的一年,保证模型在冬夏冷热极端情况下都能被评估到。
数据泄漏还有几个隐蔽来源,逐个排查:
- 用全局均值方差做标准化(第4.4节说的);
- 用了未来时次作为滞后特征(比如预测明天却用了明天夜里的气压值);
- 对缺失值用"未来值"反向填充,时间序列里最常见的隐形泄漏,我踩过一次,直接把模型回测分数从1.8℃虚高成了1.4℃,部署后才现出原形。
模型训练完之后别急着高兴,先看一眼误差的时间分布。如果误差集中在夜间或夏季,说明日周期特征还没学透;如果误差在降温天气明显放大,说明气压变化类特征强度不够。这些都比单纯看整体MAE更有指导意义。
6. 花了三个月才看明白:这21个指标的"含金量"排序
6.1 换一个预测目标,指标重要性就全变了
21个指标没有固定的重要性排行榜,一切取决于预测目标。我同时做了三套预测任务,特征重要性排序完全不同:
- 预测最高气温,最核心的特征是前一天最高气温、总云量、太阳辐射、14时气温。云量多的时候太阳辐射被削弱,高温就上不去,这跟物理直觉完全一致。
- 预测小时降水量,相对湿度、露点差、低云量、气压变化率重要性最高,风速和风向只在中等到偏弱水平。
- 预测风速,前一时段风速的滞后项重要性一骑绝尘,气压梯度差和阵风历史值是第二梯队。风的持续性比温度还强,短临预测里前6小时风速几乎决定了未来几小时的大致水平。
所以如果你问我"哪个指标最重要",我只能反过来问你:你到底要预测什么?
6.2 SHAP值告诉我:温度预测不是"温度重要"这么简单
到了第三个月我才开始认真用SHAP值看特征依赖图,这才算真正把指标"看明白"了。特征重要性排名能告诉你谁重要,但SHAP依赖图能告诉你每个特征在什么区间、朝哪个方向影响预测。
举两个例子。总云量对最高气温预测的SHAP依赖图显示,云量从0到0.3时每增加一点,预测最高温就显著下降;但当云量超过0.7以后,继续增加云量对温度的影响明显减弱,因为云已经厚到完全遮住太阳,再厚就没有额外降温效果了。这是典型的非线性饱和效应。
再比如风速,SHAP显示只有在风速小于3m/s区间,风速增加才会显著降低体感温度对预测的影响,而超过4m/s后影响趋缓。这种"只在特定区间起作用"的信息,光看特征重要性表是完全看不出来的。
6.3 最终我留下的核心指标清单
三个月折腾下来,我把最初21个指标精简成了一个主打特征集,总共11个,其余10个并没有丢弃,只是不再进入高频模型:
| 核心指标 | 保留理由 |
|---|---|
| 2m气温(及其滞后项) | 温度预测最直接的基础特征 |
| 最高气温/最低气温 | 刻画日变化幅度 |
| 相对湿度 | 关键辅助特征,对降水/体感都重要 |
| 露点温度 | 更稳定地反映水汽含量 |
| 海平面气压/地面气压 | 天气系统位置的代理变量,气压差可构造梯度特征 |
| 10m风速/风向 | 风场预测和目标,也影响体感/蒸发 |
| 总云量/低云量 | 辐射与降水前兆 |
| 太阳辐射 | 地表增温热力源 |
被精简掉的10个指标里,降水概率短期在站点级预测里价值有限,紫外线指数和太阳辐射高度共线,蒸发量和雪深对城市站点温度预测贡献微弱。这说明一个道理:指标不是越多越好,冗余特征会抬高计算成本,还会增加过拟合风险。
我最后在TimechoAI里把整个管道固化成了标准流程:TDengine数据接入 → 时间对齐与清洗 → 特征工程(滞后/窗口/周期编码/交互特征)→ LightGBM训练 → SHAP解释 → 结果落库。整个流程跑一轮从原来的几天,压缩到两三个小时,而且可复现。
最后再分享一个小技巧:每次改完特征或者模型配置,都要记录一版当时的验证集误差和特征集合,哪怕只是在文档里随手记两行。这三个月里,我因为不记录版本,曾经把一个"看起来变好实际是数据泄漏"的配置当成进步反复回滚了好几次。不管是天气预测还是别的时序任务,可复现性比偶尔一次的精度提升值钱得多。