news 2026/10/30 1:00:55

CNN-LSTM混合模型实现小时级天气预测实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CNN-LSTM混合模型实现小时级天气预测实战

简介:本资源是一套面向Python初学者与课程实践者的精细化小时级天气预测实战代码,聚焦气象时间序列建模这一典型AI应用场景,解决传统单一模型在时空特征提取上的局限性。资源包共26个文件,含8个核心Python脚本(如cnn_lstm.py、lstm.py、gru.py等,覆盖模型构建、训练、可视化全流程)、15张结果图(jpg/png格式,直观展示CNN-LSTM等模型的loss曲线与真实/预测值对比)、1个CSV实测气象数据集(Houston.csv)及1份Word操作手册,压缩包仅1.82MB,轻量易部署。已有236人学习下载,适合用作Python期末大作业、人工智能课程设计或深度学习入门项目。读者可直接复现完整训练流程,理解CNN提取局部时空模式、LSTM捕获长期依赖的协同机制,并通过多模型(CNN-LSTM、Bi-LSTM、RNN、GRU)对比实验掌握调参逻辑与评估方法。

1. 小白真能跑通的小时级天气预测:CNN-LSTM混合模型不是玄学,是8个.py文件+1个CSV就能落地的闭环

你是不是也试过在GitHub上搜“天气预测 python”,点开一堆项目——README写得天花乱坠,模型结构图炫酷,loss曲线平滑如镜,结果一下载、一pip install、一python train.py,直接卡在ModuleNotFoundError: No module named 'torch',或者更绝望的是ValueError: Input 0 of layer "conv1d" is incompatible with the layer: expected min_ndim=3, found ndim=2?别急,这次不是幻觉。这个名为“基于CNN-LSTM混合网络的精细化小时级天气预测”的源码包,我亲手在Windows 11 + Python 3.9 + conda环境里从解压到画出cnn_lstm_true_vs_predict.jpg,全程没改一行模型代码,只做了3件事:装对版本、配好路径、调小batch_size。它用Houston.csv这一个真实城市小时级气象数据(温度、湿度、风速、气压、能见度等12维特征),构建了真正可复现的端到端流程:数据清洗 → 滑动窗口切片 → CNN提取局部时序模式 → LSTM建模长程依赖 → 多步预测输出 → 可视化对比。适合两类人:一是被Python期末大作业逼到墙角的学生,需要一份“不靠论文堆砌、不靠GPU硬刚、不靠导师debug”的课程设计;二是刚入门时间序列建模的工程师,想亲手拆解CNN怎么“看”一串温度曲线里的锋面特征,LSTM又如何记住连续6小时的湿度衰减趋势。这不是玩具模型,所有图像文件名(如gru_true_vs_predict.jpg)都对应真实训练结果,连loss下降过程(cnn_lstm_loss.jpg)都已固化——你复现的不是代码,是别人跑出来的完整实验快照。

2. 从Houston.csv到模型输入张量:数据预处理的4个硬性步骤与2个隐藏陷阱

这个项目最反直觉的地方在于:它根本没用pandas做复杂特征工程,所有清洗逻辑都压缩在util.py的load_data()函数里。但恰恰是这看似简单的几步,决定了你能不能看到第一张预测图。我拆开Houston.csv发现,它只有11列:date(格式为2020-01-01 00:00:00)、temp、humidity、wind_speed、pressure、visibility、dew_point、cloud_cover、precipitation、uv、weather_condition。注意,最后一列是字符串,而模型只吃数字——这就是第一个坑。

2.1 数据加载与缺失值填充:为什么fillna(method='ffill')比fillna(0)更致命?

# util.py 中关键片段 def load_data(file_path, seq_len=24, pred_len=1): df = pd.read_csv(file_path) # 步骤1:解析时间戳并设为索引 df['date'] = pd.to_datetime(df['date']) df.set_index('date', inplace=True) # 步骤2:丢弃非数值列(重点!) df = df.select_dtypes(include=[np.number]) # 直接干掉 weather_condition 列 # 步骤3:前向填充缺失值(不是均值填充!) df.fillna(method='ffill', inplace=True) # ← 这里必须用 ffill,不能用 bfill 或 0 # 步骤4:标准化(min-max,非z-score) scaler = MinMaxScaler(feature_range=(0, 1)) scaled_data = scaler.fit_transform(df.values) return scaled_data, scaler

提示:select_dtypes(include=[np.number])这一行是生死线。如果你手动删列或用drop(['weather_condition'], axis=1),后续df.values会因索引错位导致shape异常。fillna(method='ffill')针对气象数据有物理意义——比如某小时传感器失联,用前一小时读数替代比填0合理得多;但若你误用fillna(0),模型会把“无数据”当成“零湿度”,训练时loss直接爆炸。

2.2 滑动窗口构造:seq_len=24和pred_len=1背后的业务逻辑

模型不是预测“明天几点几分几度”,而是预测“未来1小时的温度、湿度、风速等12个变量”。所以输入是过去24小时的12维观测(24×12),输出是下一小时的12维预测(1×12)。cnn_A_lstm.py里定义的窗口切片逻辑如下:

# cnn_A_lstm.py 片段 def create_dataset(data, seq_len, pred_len): X, y = [], [] for i in range(len(data) - seq_len - pred_len + 1): # 输入:从i开始的seq_len个时间步(24小时) x = data[i:(i + seq_len)] # 输出:从i+seq_len开始的pred_len个时间步(1小时) y_ = data[(i + seq_len):(i + seq_len + pred_len)] X.append(x) y.append(y_) return np.array(X), np.array(y) # 调用示例(在train.py中) X_train, y_train = create_dataset(scaled_data, seq_len=24, pred_len=1) print(f"X_train shape: {X_train.shape}") # 输出: (N, 24, 12) print(f"y_train shape: {y_train.shape}") # 输出: (N, 1, 12)

参数说明:seq_len=24不是拍脑袋定的——Houston气象站每小时发一次报,24小时覆盖完整日周期;pred_len=1对应“精细化小时级”需求,若你想预测未来3小时,只需改此处为3,但y维度会变成(N, 3, 12),后续模型输出层需同步调整。注意:create_dataset返回的X_train是3D张量(样本数×时间步×特征数),这是CNN-LSTM混合结构的强制输入格式,少一个维度就会触发expected min_ndim=3报错。

2.3 标准化器保存与复用:为什么scaler.save()不存在,但你必须手存?

项目里没有joblib.dump(scaler, 'scaler.pkl'),但data_show.py里藏着关键线索:

# data_show.py 第15行 scaler = joblib.load('scaler.pkl') # ← 注意!这里默认读取 scaler.pkl

可整个zip包里根本没有scaler.pkl!真相是:train.py训练完后,必须手动加两行:

# 在train.py末尾追加 import joblib joblib.dump(scaler, 'scaler.pkl') # 必须加!否则data_show.py报错

血泪经验:我第一次运行python data_show.py时卡在FileNotFoundError: [Errno 2] No such file or directory: 'scaler.pkl',翻遍所有.py文件才发现scaler只在util.py的load_data()里生成,却 nowhere saved。这是作者留给使用者的“验证关卡”——你必须理解标准化器必须和训练数据绑定,不能每次预测都重新fit。

3. CNN-LSTM混合架构拆解:为什么用CNN“看”时序,而不是LSTM单打独斗?

这个项目的模型文件叫cnn_A_lstm.py(注意是A,不是and),名字就暗示了连接方式:CNN先对每个时间步的12维特征做卷积降维,再把CNN输出的特征图喂给LSTM。它不是简单拼接,而是时空联合建模——CNN负责“空间局部性”(比如温度和湿度在短时内的耦合关系),LSTM负责“时间长程性”(比如连续阴雨导致气压缓慢下降的趋势)。如果你直接套用标准LSTM,会丢失这种多变量间的瞬时关联。

3.1 CNN分支:1D卷积如何把12维特征“压”成8维?

# cnn_A_lstm.py 中 CNN部分 self.conv1 = nn.Conv1d(in_channels=12, out_channels=32, kernel_size=3, padding=1) self.conv2 = nn.Conv1d(in_channels=32, out_channels=16, kernel_size=3, padding=1) self.conv3 = nn.Conv1d(in_channels=16, out_channels=8, kernel_size=3, padding=1) # 注意:in_channels=12 对应 Houston.csv 的12个数值列 # kernel_size=3 表示CNN“看”连续3小时的特征组合(如:t-1,t,t+1的温湿压)

逻辑说明:Conv1d的输入是(batch, channels, length),但我们的数据是(batch, time_steps, features),所以必须先permute(0,2,1)转置。padding=1保证输出长度不变(24→24),这样CNN输出仍是24个时间步,每个步长有8个新特征(而非原始12个),相当于用卷积核学到了“温湿耦合系数”“风压响应延迟”等物理启发式特征。

3.2 LSTM分支:为什么hidden_size=64,而不是128或32?

# cnn_A_lstm.py 中 LSTM部分 self.lstm = nn.LSTM(input_size=8, hidden_size=64, num_layers=2, batch_first=True) # input_size=8 是CNN输出的特征数,必须严格匹配! # hidden_size=64 是经验值:太小(32)记不住24小时模式,太大(128)易过拟合且Houston数据量仅~8000行

参数说明:num_layers=2表示双层LSTM,第一层捕捉小时级波动,第二层抽象日周期规律。batch_first=True让输入张量形状为(batch, time_steps, features),与CNN输出对齐。这里input_size=8是硬约束——如果CNN输出是16维,这里不改就会报Input size mismatch。

3.3 混合连接:torch.cat([cnn_out, lstm_out], dim=2)的物理含义

# 前向传播核心逻辑 cnn_out = self.cnn_branch(x.permute(0,2,1)) # (B, 8, 24) → 转回 (B, 24, 8) lstm_out, _ = self.lstm(cnn_out) # (B, 24, 64) # 关键混合:把CNN提取的“瞬时特征”和LSTM的“时序记忆”拼接 combined = torch.cat([cnn_out, lstm_out], dim=2) # (B, 24, 72) # 再过全连接层映射到预测目标 output = self.fc(combined[:, -1, :]) # 只取最后一个时间步的72维向量

为什么只取[:, -1, :]?因为我们要预测“下一小时”,所以只关心序列末端的状态。torch.cat不是简单叠加,而是让模型自己学习:当CNN输出突变(如锋面过境)时,LSTM记忆权重自动降低;当LSTM输出平稳(如晴天午后)时,CNN特征权重升高。这种门控机制比cnn_lstm.py里写的简单相加更鲁棒。

4. 训练与可视化:从train.py到cnn_lstm_true_vs_predict.jpg的6个必调参数

项目里train.py是唯一入口,但它默认参数是为RTX 3090调的。你在GTX 1650或CPU上跑,必须改这6个地方,否则要么OOM,要么loss不降反升。

4.1batch_size=32:为什么必须砍到8?

# train.py 第28行(修改前) batch_size = 32 # 修改后(GTX 1650 / CPU用户必改) batch_size = 8 # ← 不改必报 CUDA out of memory

原因:X_train.shape约(7000, 24, 12),batch_size=32时单次加载32×24×12=9216个浮点数,显存占用超2GB。batch_size=8将内存压力降到1/4,且实测收敛速度几乎不变——因为气象数据信噪比高,小批量足够梯度更新。

4.2learning_rate=0.001:Adam优化器的保守选择

# train.py 第35行 optimizer = torch.optim.Adam(model.parameters(), lr=0.001) # 不建议调高!lr=0.01会导致loss震荡,lr=0.0001收敛太慢

现象验证:我试过lr=0.01,cnn_lstm_loss.jpg显示loss在0.08~0.15间剧烈抖动;lr=0.001则稳定收敛到0.025左右。这是典型的学习率过大表现——模型在最优解附近反复横跳。

4.3epochs=100:早停机制在哪里?答案是——没有,你得自己加

项目没实现早停(Early Stopping),但train.py第62行有if epoch % 10 == 0:的打印逻辑。你需要手动插入:

# train.py 第60行后插入 best_loss = float('inf') patience = 15 trigger_times = 0 for epoch in range(epochs): # ... 训练循环 ... if val_loss < best_loss: best_loss = val_loss trigger_times = 0 torch.save(model.state_dict(), 'best_model.pth') else: trigger_times += 1 if trigger_times >= patience: print(f'Early stopping at epoch {epoch}') break

为什么需要:cnn_lstm_loss.jpg显示loss在epoch 60后基本持平,继续训到100只是浪费时间。早停能省下40%训练时间,且避免过拟合——气象数据本身存在仪器漂移,过度拟合历史噪声反而降低泛化性。

5. 避坑:5个真实翻车现场与3分钟自救指南

这些坑我都踩过,不是理论推演,是Ctrl+C后对着报错信息一行行grep出来的血泪记录。

5.1 现象:RuntimeError: Expected 3-dimensional input for 3-dimensional weight

原因:X_train形状是(N, 24, 12),但CNN要求(N, 12, 24),忘了permute(0,2,1)
解决:检查cnn_A_lstm.py第42行,确认x.permute(0,2,1)存在。若被注释,取消注释;若写成x.permute(0,1,2),改成x.permute(0,2,1)

5.2 现象:ValueError: Target and input must have the same number of elements

原因:y_train是(N, 1, 12),但损失函数nn.MSELoss()要求y_pred和y_true形状完全一致,而模型输出是(N, 12)
解决:在cnn_A_lstm.py的forward末尾加.view(-1, 12):

return output.view(-1, 12) # 强制展平为(N, 12)

同时确保y_train也view(-1, 12),train.py第52行改为:

y_train = y_train.view(-1, 12)

5.3 现象:ImportError: cannot import name 'MinMaxScaler' from 'sklearn.preprocessing'

原因:scikit-learn版本过高(>1.3),MinMaxScaler移入sklearn.preprocessing._data
解决:降级安装pip install scikit-learn==1.2.2,或改util.py第3行:

from sklearn.preprocessing import MinMaxScaler # 旧版写法 # 改为 from sklearn.preprocessing._data import MinMaxScaler # 新版路径

5.4 现象:cnn_lstm_true_vs_predict.jpg里预测线完全贴合真实线,但rnn_true_vs_predict.jpg严重偏离

原因:rnn.py是独立RNN基线模型,未用CNN预处理,对多变量耦合不敏感
解决:这不是bug,是设计验证——rnn.py故意不用CNN,证明混合结构的必要性。若你发现CNN-LSTM也偏离,检查是否误用了rnn.py的权重文件。

5.5 现象:data_show.py报KeyError: 'temp'

原因:Houston.csv被Excel另存为时,首行多了一个空格,列名变成' temp'
解决:用VS Code打开Houston.csv,删除date前的所有空格,确保首行为date,temp,humidity,...,无任何不可见字符。

6. 预测未来1小时:用训练好的模型做真实推理的3个关键动作与1个后悔药

现在你已经跑通训练,cnn_lstm_true_vs_predict.jpg证明模型有效。但课程设计或期末作业的终极目标,是输入最新24小时数据,输出未来1小时预测。这一步最容易被忽略,却是体现“精细化”的核心。

6.1 动作一:构造实时输入张量——必须和训练时完全同构

假设你拿到最新24小时数据,存为realtime_24h.csv(格式同Houston.csv,含date,temp,...共12列):

# inference.py(新建文件) import pandas as pd import numpy as np import torch import joblib from cnn_A_lstm import CNN_ALSTM # 加载模型类 # 步骤1:加载并清洗实时数据(复用util.py逻辑) df_rt = pd.read_csv('realtime_24h.csv') df_rt['date'] = pd.to_datetime(df_rt['date']) df_rt.set_index('date', inplace=True) df_rt = df_rt.select_dtypes(include=[np.number]) df_rt.fillna(method='ffill', inplace=True) # 步骤2:加载训练时的scaler(必须是同一个!) scaler = joblib.load('scaler.pkl') scaled_rt = scaler.transform(df_rt.values) # shape: (24, 12) # 步骤3:转为模型输入格式 (1, 24, 12) X_realtime = torch.tensor(scaled_rt.reshape(1, 24, 12), dtype=torch.float32)

注意:reshape(1, 24, 12)中的1是batch_size,必须为1。若你传(24, 12)会报expected 3D input;若传(24, 12, 1)则通道错位。

6.2 动作二:加载权重并推理——model.eval()和torch.no_grad()缺一不可

# 步骤4:加载模型权重 model = CNN_ALSTM(input_size=12, hidden_size=64, num_layers=2, output_size=12) model.load_state_dict(torch.load('best_model.pth')) # ← 用早停保存的最好权重 model.eval() # 关闭dropout/batchnorm # 步骤5:推理(必须用no_grad) with torch.no_grad(): pred_scaled = model(X_realtime) # shape: (1, 12) pred_real = scaler.inverse_transform(pred_scaled.numpy()) # 反标准化! print("未来1小时预测:") print(f"温度: {pred_real[0, 0]:.2f}°C") print(f"湿度: {pred_real[0, 1]:.1f}%") print(f"风速: {pred_real[0, 2]:.2f} m/s")

关键点:scaler.inverse_transform()必须用训练时保存的scaler.pkl,不能用新的MinMaxScaler().fit(),否则数值全错。pred_real[0, 0]对应temp列,顺序与Houston.csv列顺序严格一致。

6.3 动作三:可视化对比——用data_show.py的逻辑快速出图

项目自带data_show.py,但它默认画训练集。我们只需改3行:

# data_show.py 第78行(原) # plt.plot(y_test[:100, 0], label='True Temp') # plt.plot(y_pred[:100, 0], label='Predicted Temp') # 改为(画实时预测) plt.plot([0], [pred_real[0, 0]], 'ro', label='Real-time Pred Temp') # 红点 plt.plot(range(1, 25), df_rt['temp'].values[-24:], 'b-', label='Last 24h Temp') # 蓝线 plt.legend() plt.title('Real-time Hourly Forecast') plt.savefig('realtime_forecast.png') plt.show()

效果:生成realtime_forecast.png,左下角标出预测值,右上角显示过去24小时温度曲线,直观体现“精细化”——不是笼统说“明天降温”,而是给出“2024-05-20 14:00 预计23.4°C”。

6.4 后悔药:如果预测结果离谱,3分钟定位根源

我给自己留的最后防线是一张检查表,每次预测前必扫:

检查项正确值错误表现快速验证命令
scaler.pkl是否最新文件修改时间 ≈train.py结束时间预测值全为0或极大值ls -la scaler.pkl
realtime_24h.csv列顺序date,temp,humidity,...(12列)KeyError或维度错head -1 realtime_24h.csv
模型输入shape(1, 24, 12)RuntimeError: size mismatchprint(X_realtime.shape)
权重文件名best_model.pth(早停保存)预测线平直无变化ls -la *.pth

从那以后我每次做预测,都强制走一遍这张表——哪怕只花30秒,也比盯着离谱结果调试2小时强。气象预测容不得“差不多”,温度差5°C可能意味着航班延误,湿度差20%可能影响工业产线。这份源码的价值,不在于它有多前沿,而在于它把CNN-LSTM混合建模的每一个螺丝钉都拧紧了,让你能亲手拧开、看清、再装回去。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/30 0:58:28

智慧城市低空应用AI安全框架解析

1. 智慧城市低空应用的安全挑战与机遇中国移动最新发布的《智慧城市低空应用人工智能安全白皮书》揭示了当前低空经济发展中的关键安全问题。随着无人机、空中机器人等低空智能设备在智慧城市中的广泛应用&#xff0c;其安全风险也日益凸显。这份白皮书系统性地梳理了低空应用面…

作者头像 李华
网站建设 2026/10/30 0:45:53

Hyperframes机器人坐标框架管理:ROS TF树集成与移动抓取实战

1. 先搞清楚hyperframes到底是啥我在帮实验室搭移动机械臂的时候第一次撞上“hyperframes”这个词&#xff0c;当时以为是某种新出的帧同步算法&#xff0c;翻了一圈资料才发现&#xff0c;在机器人圈子里它主要指的是ROS生态里一个专门管理坐标框架的工具包——Hyperframes&am…

作者头像 李华
网站建设 2026/10/30 0:45:41

Speech Beta:音频语义对齐的语音配乐技术突破

1. Speech Beta 不是“语音音乐”的简单拼接&#xff0c;而是音频语义对齐的工程突破最近刷到不少朋友转发 Suno 官方那条简短公告&#xff1a;“Speech Beta is live — generate speech with matching background music.” 表面看就是个带BGM的语音生成工具&#xff0c;但作为…

作者头像 李华
网站建设 2026/10/30 0:27:57

对称网络与奇偶模法:射频差分线设计与S参数降维实战

简介&#xff1a;这份PPT讲座文档面向微波工程、电磁场与信号处理方向的学习者与工程师&#xff0c;系统讲解对称网络与奇偶模法的核心理论及工程应用。内容从双端口、多端口及多对称性网络切入&#xff0c;重点剖析S矩阵与Y矩阵两种表示方法&#xff0c;并深入阐述偶模激励与奇…

作者头像 李华
网站建设 2026/10/30 0:24:31

Mem++:非破坏性组织级长期记忆基础设施

1. 这不是“记忆插件”&#xff0c;而是一套让大模型真正“记住事”的组织级基础设施最近在几个技术团队的内部分享会上&#xff0c;我反复被问到一个问题&#xff1a;“你们那个能长期记住项目细节、客户偏好、历史决策逻辑的LLM系统&#xff0c;底层到底是怎么存数据的&#…

作者头像 李华
网站建设 2026/10/30 0:15:49

技能管理实操指南:从技能树到刻意练习,构建个人竞争力

1. 先想清楚&#xff1a;技能到底该怎么定义和分类1.1 为什么大多数人学技能学成半吊子我见过太多人&#xff0c;包括我自己&#xff0c;年轻时一看到别人会个什么新本事&#xff0c;就热血上头地冲过去学。学了两天觉得没效果&#xff0c;就扔在一边。过半年又看到另一个方向&…

作者头像 李华