
简介这份资源面向具备一定Python与深度学习基础的开发者、气象数据分析学习者提供一套基于CNN-LSTM混合网络的精细化小时级天气预测完整源码可用于短期气温、湿度等时序气象要素的建模实验与课程设计参考。压缩包共26个文件约3.58MB其中8个py源码文件分别实现CNN-LSTM、CNN-A-LSTM、Bi-LSTM、LSTM、GRU、RNN等对比模型及数据处理与可视化脚本另含1个CSV数据集、11张jpg与4张png训练损失及预测对比图、readme说明文档便于直观比较各网络结构的预测效果。目前已有1259人学习下载。读者可据此掌握时序数据预处理、混合网络搭建、多模型对照实验与结果可视化的完整流程并借助现成图像与脚本快速复现、调参和排错适合作为天气预测入门到进阶的实践素材。1. 小时级天气预测为什么难做从 Houston.csv 到 CNN-LSTM 混合网络小时级天气预测的难点不在模型深不深而在数据里同时存在两种截然不同的模式短时突变比如午后对流云团快速生成、风速骤升和长程依赖比如连续十几个小时的温度缓降、气压趋势。纯 CNN 擅长抓局部突变但记不住长序列纯 LSTM 能记住长依赖却容易忽略短时局部特征。这个源码包做的事就是把两者串起来——先用卷积层从气象序列里抽局部模式再把特征序列喂给 LSTM 做时序建模最后输出小时级预测值。包里的 Houston.csv 是真实城市气象数据code 目录下有 cnn_lstm.py、Bi_lstm.py、gru.py、rnn.py、lstm.py、cnn_A_lstm.py、util.py、data_show.py 共 8 个 Python 文件另外还有 11 张 jpg 和 4 张 png 训练曲线图能直接看到各模型 loss 和 true_vs_predict 的对比。适合想上手时序预测、又不想从零搭数据管道的 Python 开发者也适合拿它当课程设计或对比实验的基线。下面按「数据怎么进 → 模型怎么搭 → 坑在哪 → 怎么验证」的顺序拆开讲。2. 数据管道与特征工程Houston.csv 怎么喂进网络2.1 先看清 CSV 的列结构和时间粒度Houston.csv 是整条流水线的起点读之前先确认三件事时间列是不是标准 datetime、目标列是哪一列、有没有缺失值。常见做法是用 pandas 先做一次体检别急着往模型里灌。import pandas as pd import numpy as np # 读取原始气象数据 df pd.read_csv(Houston.csv) print(df.shape) print(df.dtypes) print(df.isnull().sum()) # 时间列解析常见列名是 date / datetime / time df[date] pd.to_datetime(df[date]) df df.sort_values(date).reset_index(dropTrue) # 缺失值处理小时级数据一般用前向填充 线性插值组合 df df.fillna(methodffill).interpolate()逻辑说明pd.to_datetime把字符串时间转成可排序对象sort_values保证序列顺序正确——时序模型对顺序极度敏感乱序输入等于喂噪声。ffill处理短缺口interpolate补连续缺失。参数上如果缺失比例超过 5%建议直接丢弃该时间段而不是硬填否则会引入虚假趋势。2.2 归一化与滑动窗口构造气象各列量纲差异大温度在 0~40气压在 1000 上下风速在 0~20。不归一化的话气压列会主导梯度。我一般用 MinMaxScaler 把每列压到 [0,1]再构造滑动窗口。from sklearn.preprocessing import MinMaxScaler feature_cols [temp, pressure, humidity, wind_speed] # 按实际列名替换 target_col temp scaler MinMaxScaler() data_scaled scaler.fit_transform(df[feature_cols [target_col]]) def make_windows(data, lookback24, horizon1): X, y [], [] for i in range(len(data) - lookback - horizon 1): X.append(data[i:ilookback, :-1]) # 特征窗口 y.append(data[ilookback:ilookbackhorizon, -1]) # 目标 return np.array(X), np.array(y) X, y make_windows(data_scaled, lookback24, horizon1) print(X.shape, y.shape) # (样本数, 24, 特征数), (样本数, 1)逻辑说明lookback24表示用过去 24 小时预测下一小时这是小时级天气的常用窗口。horizon1是单步预测想预测未来 6 小时就把 horizon 改成 6。注意data[i:ilookback, :-1]里:-1是把目标列从特征里剔除避免标签泄漏——这是新手最容易翻车的地方目标列混进特征会让 loss 低得离谱但实际预测全错。2.3 训练集/测试集切分不能随机打乱时序数据的切分必须按时间先后不能train_test_split(shuffleTrue)。常见做法是前 80% 做训练、后 20% 做测试。split int(len(X) * 0.8) X_train, X_test X[:split], X[split:] y_train, y_test y[:split], y[split:] print(X_train.shape, X_test.shape)逻辑说明按索引切分保证测试集全在训练集之后模拟真实预测场景。如果打乱模型会「偷看」未来数据评估结果虚高。这一步没有可调参数但顺序错了后面全白搭。3. CNN-LSTM 模型搭建卷积抽特征、LSTM 记趋势3.1 为什么是 CNN 接 LSTM 而不是并联cnn_lstm.py 里的结构是串联Conv1D → MaxPooling → LSTM → Dense。串联的逻辑是让卷积先在一维时间轴上做局部特征提取把原始序列压缩成更抽象的特征序列再交给 LSTM 建模长程依赖。并联结构CNN 和 LSTM 各走一路再拼接适合多模态输入但单变量气象序列用串联更省参数、收敛更稳。from tensorflow.keras import layers, models def build_cnn_lstm(lookback, n_features): model models.Sequential([ layers.Conv1D(filters64, kernel_size3, activationrelu, paddingsame, input_shape(lookback, n_features)), layers.MaxPooling1D(pool_size2), layers.Conv1D(filters32, kernel_size3, activationrelu, paddingsame), layers.LSTM(64, return_sequencesFalse), layers.Dropout(0.2), layers.Dense(1) ]) model.compile(optimizeradam, lossmse, metrics[mae]) return model model build_cnn_lstm(lookback24, n_featuresX_train.shape[2]) model.summary()逻辑说明第一层Conv1D用 64 个 3 宽卷积核在时间轴上滑动paddingsame保持序列长度MaxPooling1D(pool_size2)把序列长度减半降低 LSTM 的计算量。第二层卷积进一步抽象。LSTM(64)是核心return_sequencesFalse表示只取最后一个时间步的输出。Dropout(0.2)防过拟合。参数上filters 从 64 降到 32 是常见递减设计kernel_size3 适合小时级数据的局部相关性再大容易平滑掉突变。3.2 训练配置与早停from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau callbacks [ EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue), ReduceLROnPlateau(monitorval_loss, factor0.5, patience5, min_lr1e-6) ] history model.fit( X_train, y_train, validation_split0.1, epochs100, batch_size32, callbackscallbacks, verbose1 )逻辑说明EarlyStopping的patience10表示验证 loss 连续 10 轮不降就停restore_best_weightsTrue回滚到最优权重——这是后悔药防止最后几轮过拟合。ReduceLROnPlateau在 loss 停滞时把学习率减半帮助跳出局部极小。batch_size32是时序任务的稳妥起点显存够可以上 64。validation_split0.1从训练集尾部切 10% 做验证注意这里也是按顺序切不是随机。3.3 对比模型Bi_lstm、GRU、RNN 怎么选包里同时给了 Bi_lstm.py、gru.py、rnn.py、lstm.py这是让你做消融对比的。Bi_lstm 双向 LSTM 能同时看过去和未来但预测任务里「未来」不可得所以它更适合做特征提取而非在线预测。GRU 参数比 LSTM 少、训练快在数据量不大时表现接近。RNN 是基线梯度消失严重长序列上基本会被 LSTM 碾压。我一般先用 LSTM 跑通再换 GRU 看能否用更少参数达到同等精度最后用 CNN-LSTM 冲精度。模型参数量长依赖能力适用场景RNN最少弱基线对比LSTM中强通用时序GRU中偏少较强数据量小、求快Bi-LSTM中偏多强离线特征提取CNN-LSTM最多强 局部特征精度优先4. 训练过程避坑loss 不降、预测滞后、显存爆4.1 现象loss 降到某个值就卡住不动原因通常是学习率太大导致在极小值附近震荡或者输入没归一化导致梯度尺度失衡。解决先确认data_scaled是否真的在 [0,1]再检查ReduceLROnPlateau是否生效。如果两者都正常把batch_size调小到 16 试试小批量带来的梯度噪声有时反而能跳出平台。4.2 现象true_vs_predict 图里预测曲线整体滞后一格这是时序预测的经典问题模型倾向于输出「上一时刻的值」而不是真正的下一时刻。原因多半是 lookback 窗口太长、目标列和特征列高度自相关。解决把 lookback 从 24 降到 12或者在 loss 里加一阶差分惩罚。包里 cnn_lstm_true_vs_predict.jpg 如果看到滞后先查这个。4.3 现象训练到一半显存爆掉CNN-LSTM 的参数量比纯 LSTM 大filters64加LSTM(64)在长序列上很吃显存。解决把MaxPooling1D的 pool_size 从 2 调到 4序列长度直接砍到四分之一或者把 LSTM 单元数从 64 降到 32。别一上来就上大模型小时级数据几千条样本撑不起百万参数。4.4 现象验证 loss 比训练 loss 低正常情况验证 loss 应该略高于训练 loss。如果反过来八成是validation_split切到了训练集里已经见过的模式或者数据泄漏——目标列混进了特征。回头查make_windows里的:-1有没有写对。4.5 现象换 GRU 后结果波动很大GRU 对初始化敏感kernel_initializer默认 glorot_uniform 在数据量小时方差大。解决固定随机种子tf.random.set_seed(42)和np.random.seed(42)多跑几次取平均。包里 gru_loss.jpg 和 gru_true_vs_predict.jpg 可以对照看波动幅度。5. 结果验证与进阶技巧从曲线图读出模型真实水平5.1 别只看 loss要看反归一化后的真实误差训练时的 loss 是在 [0,1] 尺度上算的没有物理意义。必须用 scaler 反变换回原始量纲再算 MAE 和 RMSE。# 反归一化只对目标列做逆变换 y_pred model.predict(X_test) # 构造与 scaler 同宽度的占位矩阵 dummy np.zeros((len(y_pred), data_scaled.shape[1])) dummy[:, -1] y_pred.flatten() y_pred_inv scaler.inverse_transform(dummy)[:, -1] dummy_true np.zeros((len(y_test), data_scaled.shape[1])) dummy_true[:, -1] y_test.flatten() y_true_inv scaler.inverse_transform(dummy_true)[:, -1] mae np.mean(np.abs(y_true_inv - y_pred_inv)) rmse np.sqrt(np.mean((y_true_inv - y_pred_inv) ** 2)) print(fMAE: {mae:.2f}, RMSE: {rmse:.2f})逻辑说明inverse_transform要求输入维度和 fit 时一致所以用dummy占位只把目标列填进去。这一步不做的话你报的 MAE 是 0.02 这种没意义的数。参数上MAE 反映平均偏差RMSE 对大误差更敏感两个一起看才能判断模型是整体偏还是偶尔崩。5.2 用 data_show.py 快速出对比图包里 data_show.py 是可视化脚本常见用法是把y_true_inv和y_pred_inv画在同一张图上再叠加 loss 曲线。我一般会改三处图标题加上模型名和 lookback 值、y 轴标注原始单位、保存成 png 而不是 show。这样批量跑对比实验时不用手动截图。import matplotlib.pyplot as plt plt.figure(figsize(12, 4)) plt.plot(y_true_inv, labelTrue, alpha0.8) plt.plot(y_pred_inv, labelCNN-LSTM, alpha0.8) plt.xlabel(Time Step (hour)) plt.ylabel(Temperature) plt.legend() plt.title(CNN-LSTM lookback24 horizon1) plt.savefig(cnn_lstm_compare.png, dpi150)逻辑说明alpha0.8让两条线重叠时也能看清。dpi150保证论文或报告里够清晰。保存而不是 show是为了在服务器上跑实验时不留一堆弹窗。5.3 一个具体技巧用残差图定位模型盲区把y_true_inv - y_pred_inv按时间画出来如果残差在某个时段系统性偏正或偏负说明模型对该时段的气象模式没学好。常见的是夜间温度预测偏高的因为 CNN 的卷积核在低值区间响应弱。解决办法是在特征里加一列「小时 sin/cos 编码」让模型知道当前是几点。df[hour] df[date].dt.hour df[hour_sin] np.sin(2 * np.pi * df[hour] / 24) df[hour_cos] np.cos(2 * np.pi * df[hour] / 24)逻辑说明sin/cos 编码把 0~23 的循环时间转成连续值避免模型把 23 点和 0 点当成相距很远。这一列加进去后夜间残差通常能收窄 20% 左右。从那以后我每次做小时级预测都强制先加时间编码再谈模型结构——这个习惯帮我省了至少三轮调参。希望帮到你。本文还有配套的精品资源点击获取