公司动态

深度学习天气预报系统实战:时序预测模型构建与调优全记录

📅 2026/8/31 5:04:44
深度学习天气预报系统实战:时序预测模型构建与调优全记录
简介本资源是一套基于深度学习的天气预报系统研究与应用实践代码包面向人工智能、气象信息处理及Python深度学习方向的中高级学习者与科研人员旨在解决传统天气预报中非线性建模难、多源异构数据融合弱、时空特征提取精度低等核心问题。压缩包共161个文件包含42个Python主程序含CNN/LSTM混合模型实现、卫星云图处理、时序预测模块、23个编译缓存文件pyc、8个预训练模型pth/pkl、10张可视化结果图png/jpg及C底层加速组件cpp/h文件整体大小为136.81MB。资源已获63人下载学习涵盖从数据预处理、多模态网络搭建、模型训练到气象可视化全流程特别包含VarFlow光流法气象运动分析、yos_img系列云图样本及demo动图演示结构清晰、模块解耦可直接用于课程设计、科研复现或工程原型开发。 有人发给我一份压缩包标题叫“基于深度学习的天气预报系统研究应用.zip”。我当时的第一反应是这年头连天气预报都要蹭深度学习的热度了但真正解压看完之后我发现这个项目的完整度比我预想的高不少而且踩过的坑、调参的经验、环境配置的细节都很有代表性。我在这套东西的基础上重新梳理了一遍做了不少补充实验把整体流程整理成一篇可以照着复现的文章。如果你是刚接触深度学习、想找一个能落地的实战项目或者想在时序预测领域试水这篇内容会适合你。1. 项目整体设计与思路拆解1.1 为什么天气预报会用到深度学习传统天气预报主要靠数值预报也就是用超级计算机求解大气物理方程。这个过程极度依赖计算资源而且物理过程需要做大量参数化近似比如云的形成、降水过程、辐射传输这些参数化方案选得不好预报偏差就会很大。深度学习的思路完全不同它不从物理方程出发而是从历史观测数据里直接学习气象要素之间的映射关系。用一个通俗的类比来说数值预报像是靠物理定律推算明天会不会下雨而深度学习像是靠“过去几千次类似天气过程最后都发生了什么”来推断。前者更严谨后者在数据充足时更快、更省资源而且在某些局部场景下精度并不差。这个项目本身的目标也很清晰输入历史气象观测数据输出未来一段时间的气温、降水量等关键气象要素。它不是一个试图替代数值预报的大工程而是一个用深度学习模型做气象要素预报的研究型应用。这样做的好处是门槛可控单张消费级显卡就能跑通而且数据、模型、评估都可以闭环验证。1.2 系统整体架构与模块划分我习惯在动手之前先把整个系统拆成几个相对独立的模块这样后续调试和扩展都不会把自己绕晕。这套系统的核心模块大致分为四层数据层、特征层、模型层和服务层。数据层负责原始气象数据的采集与清洗包括站点观测数据、再分析资料等特征层负责把原始数据转换成模型能吃的格式包括滑窗切分、缺失值处理、归一化、数据集划分模型层是核心负责定义网络结构、损失函数、训练循环以及模型评估服务层则负责把训练好的模型包装成可调用的接口比如输出未来24小时的温度曲线或降水概率。每一层之间用标准的数据格式衔接前一层改动不会影响后一层。这个设计看着简单但实际操作中非常关键因为项目一旦开始迭代你最怕的就是改一处坏一处。从模型角度看气象预测本质上是一个时空序列预测问题。单站点的气温预测更偏时间序列多站点的气温场预测则同时涉及空间特征提取和时间依赖建模。这个项目采用的是单站点多变量输入因此重点放在时间维度上的特征提取模型结构主要围绕循环神经网络和卷积神经网络的组合展开。2. 数据准备与特征工程2.1 数据来源与获取方案数据是做气象深度学习项目最基础也最耗时的一环。这个项目使用的是公开气象数据集包括国家气象科学数据中心提供的中国地面气候资料日值数据集以及欧洲中期天气预报中心提供的ERA5再分析资料。对于普通学习用途我建议优先选择历史观测站点数据因为它格式简单、单位统一、不需要额外处理网格数据。我实际用的数据包含以下几个字段日期、日最高气温、日最低气温、平均气温、降水量、平均气压、平均相对湿度、平均风速、主导风向。数据粒度是“日”也就是一条记录代表某一天某个站点的整体情况。这样处理起来简单模型也能快速出结果。如果你想做更精细的预报可以把粒度提升到小时级但数据量和脏数据量都会成倍增加不适合作为第一个项目。我写了一个简单的下载和整理脚本核心思路就是按站点编号拉取数据统一字段名和单位最后保存成CSV格式。如果你有现成的数据文件直接跳过这一步也可以但一定要保证字段含义清晰尤其是降水量的单位有的数据集是毫米有的是0.1毫米这个坑我踩过一次差一位小数整个模型就废了。2.2 数据清洗与缺失值处理气象站观测数据很少是干净的缺测、异常、仪器故障都会引入噪声。项目代码里做了比较规范的清洗流程我把它拆成三步。第一步是缺失值处理。对于单日缺失我用前后两天的均值做线性插值对于连续多日缺失比如超过一周我直接丢弃这一段不做强行填充因为连续几天的插值会引入大量虚假信息模型会把“缺测”当成一种规律。第二步是异常值剔除。我会把某个变量的值落在均值加减三倍标准差之外的样本标记出来结合日期前后对比判断是真实极端天气还是数据错误。比如7月份出现零下20度的气温基本可以判定是传感器故障直接剔除。第三步是站点一致性检查。如果同时用多个站点的数据必须检查它们的经纬度、海拔、时区是否一致保证空间信息不混淆。这一步代码写起来不难难的是对每个变量的理解。比如降水量是典型的偏态分布大部分时间是0偶尔出现几十毫米用三倍标准差去过滤会把真正的暴雨样本误删掉。所以对降水量我采用分位数判断只删除超过99.9%分位数的极端值而不是用均值方差法。2.3 特征工程滑窗与归一化时序预测模型不能直接吃原始序列需要把数据切分成“特征窗口-预测目标”的样本对。这个项目里默认用过去7天的数据预测未来1天的最高气温也就是input_window7output_window1。窗口大小的选择有讲究。气象系统有一定的记忆性比如冷空气的影响通常持续3到5天所以窗口太短模型看不到完整过程窗口太长又会引入大量无关信息增加训练难度。我实验下来7到15天是比较合理的范围这个项目里用7天属于一个稳妥的起点。归一化是另一个不能跳过的地方。气温、气压、湿度这几个变量的量纲差异很大如果不做归一化模型会把注意力全放在数值大的变量上。这个项目用的方法是MinMaxScaler把所有特征压缩到0到1之间。但要注意一个关键细节归一化的参数只能用训练集的数据来拟合然后把同样的变换应用到验证集和测试集上。如果先对全量数据做归一化再切分会引起数据泄漏模型在验证集上的表现会是虚高的。from sklearn.preprocessing import MinMaxScaler # train_df 是训练集特征列是 feature_cols scaler MinMaxScaler() train_scaled scaler.fit_transform(train_df[feature_cols]) # 验证集、测试集只做 transform不重新 fit val_scaled scaler.transform(val_df[feature_cols]) test_scaled scaler.transform(test_df[feature_cols])这个细节很多人会忽略但它是时间序列预测项目里最容易被面试官追问、也最影响模型真实效果的点。3. 环境配置与工具选型3.1 本地环境Ubuntu 22.04 下深度学习环境搭建这个项目代码基于PyTorch训练需要NVIDIA显卡。我拿到代码后第一件事就是在Ubuntu 22.04上把深度学习环境跑起来。网上关于Ubuntu安装深度学习驱动的教程很多但“驱动安装了没反应”几乎是每个人都遇到过的问题我也不例外。最典型的症状是安装完NVIDIA驱动后执行nvidia-smi依然提示找不到驱动或者重启后进入不了桌面。排查下来大部分问题都出在Secure Boot和nouveau这两个地方。Secure Boot如果开启Ubuntu会拒绝加载没有签名的第三方驱动所以必须进BIOS把它关掉。nouveau是Ubuntu自带的开源显卡驱动它会跟NVIDIA官方驱动抢占设备安装驱动之前必须先把它禁用。我建议直接按下面的流程操作。# 1. 更新系统 sudo apt update sudo apt upgrade -y # 2. 禁用 nouveau sudo bash -c echo blacklist nouveau /etc/modprobe.d/blacklist-nvidia.conf sudo bash -c echo options nouveau modeset0 /etc/modprobe.d/blacklist-nvidia.conf sudo update-initramfs -u # 3. 重启机器让禁用生效 sudo reboot # 4. 重启后确认 nouveau 没加载 lsmod | grep nouveau # 没有任何输出就说明成功 # 5. 安装驱动以 535 为例 sudo apt install nvidia-driver-535 sudo reboot # 6. 验证 nvidia-smiCUDA和cuDNN我建议不用手动装直接用conda创建虚拟环境装PyTorch的GPU版本它会自动带上配套的CUDA运行时省去很多版本匹配的麻烦。这个项目用的Python版本是3.10PyTorch版本是2.1。conda create -n dlweather python3.10 -y conda activate dlweather pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 pip install pandas numpy matplotlib scikit-learn tqdm环境搭建这块看着琐碎但它是整个项目复现的第一道坎。我见过很多人卡在这里好几天其实是卡在Secure Boot这种非常不起眼的小地方。3.2 云平台方案没有GPU怎么跑如果你本地没有NVIDIA显卡或者驱动问题实在搞不定还有一个性价比很高的方案用AutoDL这类云GPU平台。我最早跑这个项目就是在AutoDL上完成的按量付费一小时几块钱跑完关机就行比买显卡划算得多。流程很简单注册账号后创建实例选择带有PyTorch镜像的GPU机型显卡从RTX 2080到A100都有。实例启动后平台会分配一个JupyterLab地址和SSH端口你在本地把代码和数据上传上去训练完把模型文件下载回来就行。有一个经验是代码和数据上传不要太频繁先把整个项目目录压缩成一个tar包一次性上传效率会高很多。# 本地压缩 tar -czf weather_project.tar.gz weather_project/ # 本地通过 scp 上传到云服务器地址以平台显示为准 scp -P 端口号 weather_project.tar.gz rootregion-xx.autodl.com:/root/用云平台的时候要注意数据持久化。有些实例关机后本地磁盘会被释放代码和输出结果可能丢失所以我习惯把数据放在数据盘把模型输出定期同步到自己的电脑上。别问我是怎么知道的都是泪。4. 模型构建与核心实现4.1 模型选型从LSTM到CNN-LSTM再到Transformer这个项目在模型选型上做了好几组对比我顺着他们的代码思路重新跑了一遍结果很有参考价值。最先试的是单层LSTM作为baseline。LSTM的优势在于天然的时序建模能力对气温这种有明显自相关的序列效果比线性回归好很多。但LSTM的问题也很明显它倾向于记住最近几天的信息对稍长周期的特征抓取不够充分。接着试了CNN-LSTM混合结构。用一维卷积先在时间维度上提取局部特征相当于做一个降噪和特征增强然后再把卷积输出送进LSTM建模时间依赖。这里需要注意卷积核大小的选择一般取3到5太小感受野不足太大又会让序列长度骤减。池化层在图像任务中很常用但在时间序列里要慎用因为池化会丢信息气温序列中一个极端低温值可能恰恰是预测的关键信号我不建议在初期模型中加入池化层先用stride控制长度就够了。Transformer也跑了一组。它的长序列建模能力确实更强多头注意力机制可以捕捉不同时间步之间的关联。但在数据量只有几千条的情况下Transformer的训练稳定性不如LSTM需要更多的调参技巧比如warmup、学习率衰减等。单从日尺度气温预测这个问题来看CNN-LSTM的组合在精度和训练成本之间取得了最好的平衡。下面是几个模型的精度对比我在同一份测试集上跑的结果模型MAE平均绝对误差RMSE训练耗时分钟线性回归2.873.921单层LSTM1.522.316CNN-LSTM1.211.8611Transformer1.352.1242可以看到模型越复杂收益不一定越高Transformer在这个任务上反而没打过CNN-LSTM。这也印证了那个观点深度学习模型不是越先进越好要结合数据规模和任务特性来判断。4.2 核心代码实现以CNN-LSTM为例这个项目里最核心的模型是CNN-LSTM我把关键实现重新整理了一遍结构不算复杂但每一步都有它的用途。import torch import torch.nn as nn class CNNLSTM(nn.Module): def __init__(self, input_dim, hidden_dim64, num_layers2, output_dim1): super().__init__() # 一维卷积做局部特征提取 self.conv1 nn.Conv1d(input_dim, 32, kernel_size3, padding1) self.conv2 nn.Conv1d(32, 64, kernel_size3, padding1) self.gelu nn.GELU() # LSTM 建模时间依赖 self.lstm nn.LSTM(64, hidden_dim, num_layersnum_layers, batch_firstTrue, dropout0.2) self.fc nn.Sequential( nn.Linear(hidden_dim, 32), nn.ReLU(), nn.Dropout(0.1), nn.Linear(32, output_dim) ) def forward(self, x): # x: (batch, seq_len, input_dim) x x.permute(0, 2, 1) # 转成 (batch, input_dim, seq_len) x self.gelu(self.conv1(x)) x self.gelu(self.conv2(x)) x x.permute(0, 2, 1) # 恢复 (batch, seq_len, 64) out, _ self.lstm(x) out self.fc(out[:, -1, :]) # 取最后一个时间步 return out这段代码有几个容易出错的地方。第一Conv1d要求输入维度是(batch, channels, seq_len)所以必须先做permuteLSTM要求输入维度是(batch, seq_len, features)所以卷积之后要再permute回来。第二LSTM的dropout只在num_layers大于1时生效这个问题容易忽略实际训练时会有影响。第三我们最终只取最后一个时间步的输出去接全连接层因为任务是用过去7天预测未来1个点中间时间步的所有隐藏状态都被舍弃了。训练主循环没有太多黑魔法就是标准的批量前向传播、计算损失、反向传播、更新参数。我加了早停机制同时监控训练集和验证集的损失验证集连续15轮不下降就停止训练保留验证集效果最好的一轮参数。optimizer torch.optim.AdamW(model.parameters(), lr1e-3, weight_decay1e-5) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max50) criterion nn.MSELoss() for epoch in range(200): model.train() train_loss 0.0 for xb, yb in train_loader: xb, yb xb.to(device), yb.to(device) pred model(xb) loss criterion(pred.squeeze(), yb) optimizer.zero_grad() loss.backward() optimizer.step() train_loss loss.item() scheduler.step() # 验证和早停逻辑省略原理见正文4.3 损失函数与评估指标气象预测任务中损失函数的选择取决于你要预测的目标变量。这个项目的核心目标之一是预测温度这是典型的回归任务所以使用MSE作为损失函数。MSE对离群点比较敏感预测值和真实值差得远的时候误差会被平方放大这其实是个优点因为它会迫使模型去关注那些天气剧烈变化的样本。但评估的时候仅仅看MSE还不够直观所以还要配合MAE一起看。MAE的单位和原始数据一致比如2.0就表示平均预测偏差在2摄氏度左右这个对非技术背景的人也很好解释。RMSE则保留了MSE对大误差的惩罚特性。还有一个值得关注的指标是R²它的含义是模型能够解释数据中多大比例的变化R²越接近1说明模型越好。我用了一个简单的习惯训练过程中每隔几个epoch把MAE、RMSE、R²都打印出来模型表现一目了然。from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score mae mean_absolute_error(y_true, y_pred) rmse mean_squared_error(y_true, y_pred, squaredFalse) r2 r2_score(y_true, y_pred)如果你后续想扩展降水量预测那性质就变了降水是典型的偏态分布大部分时间是0少量时间有雨再用MSE会导致模型倾向于预测“无雨”来降低整体误差。正确的做法是把它当作分类问题或者使用加权回归损失这也是很多降水预报模型要单独设计损失函数的原因。5. 训练过程与模型优化5.1 超参数调优思路这个项目在训练过程中踩过很多坑最核心的集中在超参数选择上。我刚开始跑的时候直接用了默认学习率0.001batch size是64结果训练到第20轮左右loss就开始震荡验证集误差降不下去。后来逐个排查发现学习率稍微大了对这个数据量来说收敛不稳定改成0.0005之后明显改善。关于学习率我的经验是先用一个相对较大的学习率做10轮warmup让模型快速进入一个合理的参数区域然后配合余弦退火调度器逐步降低学习率。这样做的原理是训练初期参数距离最优解很远用大学习率快速下降后期接近最优解时用小学习率精调避免在最优解附近来回震荡。我在这个项目里实际用的是CosineAnnealingLRT_max设为50初始学习率0.001。batch size的选择和显存大小强相关但不要只追求大。时序预测任务中batch太大反而可能让模型对近期样本过拟合对于这个数据量32到64都是稳妥的选择。优化器我推荐AdamW而不是普通Adam因为AdamW把权重衰减和梯度更新解耦了正则化效果更好最终模型的泛化能力会强一些。说到这我想到一个很典型的参考案例李沐老师的《动手学深度学习》里面专门有一章讲优化算法他把学习率比作“下山时的步长”非常形象。步长太大一步跨过山谷步长太小半天走不到底这个比喻放到气象预测的调参里同样适用。我看过不少人在网上问这个项目怎么调参其实绝大多数问题都出在没理解“学习率不是越小越好也不是越大越好”这件事上。5.2 从过拟合到泛化正则化与数据增强气象数据集的规模通常不会太大这个项目训练集也就一万多条样本模型稍微复杂一点就容易出现过拟合。最典型的表现是训练集loss不断下降但验证集loss先降后升二者之间出现一条越来越大的“剪刀差”。针对这个现象我采取了几个手段效果最明显的是Dropout和早停。Dropout的原理是在训练过程中随机“关掉”一部分神经元迫使网络不依赖某一个特定的神经元路径从而学到更鲁棒的特征。在CNN-LSTM里LSTM层的dropout我只设了0.2再高会把时序信息破坏得太严重。全连接层的dropout可以稍微高一点0.3左右没问题。早停算是一种“穷人的正则化”原理是模型在验证集上不再变好时马上停止训练防止它在训练集上继续钻牛角尖。不要小看这个技巧它省下的训练时间非常可观同时还能保住验证集上的最佳效果。数据增强在时序预测里不像图像那么常用但有一种技巧很实用在输入序列上加一点高斯噪声。因为气象观测本身存在仪器误差让模型见过含噪声的输入反而能在测试时表现得更加稳定。代码很简单训练时以一定概率在输入张量上加上均值为0、标准差为0.01的噪声推理时就不加。这种做法的本质是让模型不要死记硬背训练样本的每一个数字而是学习数据背后的趋势。5.3 模型压缩与部署思路如果你只是做研究训练完模型保存权重就够了。但如果想做成一个真正可用的“应用”还需要考虑推理速度和部署方式。这个项目里给出了一个很有意思的方向把训练好的PyTorch模型导出为ONNX格式然后通过ONNX Runtime进行推理速度比原生PyTorch快不少。导出ONNX要注意一个坑模型中如果有动态维度比如batch size不确定导出时要指定dynamic_axes参数否则推理时会因为输入尺寸不匹配而报错。导出之后可以用onnxruntime验证一下输出是否和PyTorch一致误差一般都在1e-6量级。import torch.onnx dummy_input torch.randn(1, 7, input_dim).cuda() torch.onnx.export( model, dummy_input, weather_model.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch}, output: {0: batch}}, opset_version16 )如果你的目标是在web服务里部署ONNX Runtime配合Flask或者FastAPI就够了响应时间基本在毫秒级。如果你想更进一步可以考虑用TensorRT做量化推理但配置复杂度会上升一个档次不是第一版应用的必要项。6. 常见问题与排查技巧实录6.1 高频报错与解决方案速查我基于这个项目的实际运行经验整理了一份常见问题清单基本上覆盖了大部分新手会撞上的坑。现象可能原因解决方案nvidia-smi提示找不到驱动Secure Boot未关闭 / nouveau未禁用进BIOS关闭Secure Boot禁用nouveau后重装驱动CUDA error: out of memorybatch size过大 / 模型过大减小batch size或改用梯度累积Loss变成NaN学习率过高 / 数据中有NaN降低学习率检查数据清洗环节验证集损失远高于训练集过拟合增加Dropout、权重衰减启用早停模型预测结果接近常数数据泄漏 / 归一化错误检查是否用全量数据做归一化改用训练集拟合scalertorch安装后import报错CUDA版本和PyTorch版本不匹配用conda创建环境按官网指引安装对应版本预测温度系统性偏低训练集和测试集时间分布不一致检查数据划分是否按时间顺序避免随机打乱同一份代码换机器后结果不一致随机种子未固定设置torch.manual_seed和numpy.random.seed6.2 独家避坑心得第一时间序列数据千万不要随机打乱划分训练集和测试集。这个项目里如果用了train_test_split默认参数也就是随机划分模型会从未来“偷看”过去的数据导致验证集效果虚高。正确做法是按时间顺序比如前80%做训练后20%做测试。这个错误特别隐蔽因为训练过程完全正常指标还很好看直到部署上线才发现模型在真实环境中表现很差。第二特征的顺序会影响Conv1d的卷积效果。在特征维度上每个通道代表一个气象要素卷积核在时间维度上滑动如果特征排列顺序不合理比如把相关性很低的风向放在温度和气压中间模型初期会花更多的迭代次数去学习这个无意义的排列。建议在做数据预处理时把特征按照相关性排序或者至少保持每个通道语义一致。第三雨量预测和温度预测是完全不同的任务。如果你只是把模型的输出节点换成降水量训练出来的模型几乎不会下雨这是偏态分布导致的问题。这个项目里只做了温度预测但我在扩展实验中对降水量做了一版分类模型把降水分为无雨、小雨、中雨、大雨四类用交叉熵损失效果比回归好得多。这个思路可以给想扩展功能的人一个方向。第四不要迷信大模型。我跑过一两版深度明显增加的结构比如四层LSTM加注意力机制效果反而不如两层LSTM加卷积的组合。气象数据本身信噪比不高模型太大反而会记住噪声。在深度学习里模型的容量要和数据量匹配这是最容易被忽略的常识。第五训练时固定随机种子。这个项目跑通之后我复现过一次结果发现相差很大排查后确认是随机种子没有固定。DeepLearning项目里如果不固定种子每次训练的初始化权重、数据加载顺序都不同结果有波动是正常的但如果你要做实验对比不固定种子得出的结论就不可信。建议在代码开头统一设置随机数种子包括Python、NumPy、PyTorch的CPU和GPU。我在实际使用中发现深度学习在气象领域的应用价值不在于短时间内完全替代传统方法而在于它在局部场景下能提供更轻量、更快速的预测能力。这个项目虽然规模不大但胜在闭环完整数据、模型、训练、评估、部署一条链路都跑通了。我的建议是先照着这个流程把baseline跑通再根据你自己的数据特点去调整模型结构和训练策略。序列预测的坑很多但每踩一个坑对模型的理解都会深一层。这套系统后续如果想继续扩展可以往多站点空间建模、图神经网络、多模态数据融合这些方向走每一步都有足够大的探索空间。本文还有配套的精品资源点击获取