简介这份资源面向具备Python与深度学习基础、希望上手时间序列预测的开发者与学习者提供一套用循环神经网络预测天气的完整Python源码帮助理解RNN、LSTM或GRU在气象数据建模中的实际落地方式。压缩包内共1个文件为单个py脚本体积约2KB代码结构紧凑便于直接阅读与二次修改。内容围绕温度、湿度、风速、气压等历史气象序列展开涉及数据清洗、缺失值处理、归一化、序列构造以及损失函数、优化器选择与均方根误差等评估环节可帮助读者梳理从数据预处理到模型训练验证的完整链路。目前已有373人学习下载适合作为入门序列预测的练手案例也可在此基础上尝试超参数调优与模型融合提升对RNN类模型泛化能力的理解。1. 循环神经网络预测天气从时序数据到可运行 Python 源码的完整路径用循环神经网络预测天气这件事真正动手做过的人都知道难点从来不是把 LSTM 那几行代码敲出来而是搞清楚「拿什么数据喂进去、预测的是哪一个物理量、预测步长是多少」。标题里说的循环神经网络预测天气代码本质上是一个时间序列回归任务用过去若干小时或若干天的温度、湿度、气压、风速等观测值去预测未来某个时刻的温度或降雨概率。它适合已经会写基础 Python、想找一个完整时序项目练手的人也适合手上有气象观测数据、想快速验证 RNN 类模型到底能不能用的工程师。这一章先把任务边界划清楚后面几章再一步步落到数据、模型、训练和排错上。2. 天气时序任务拆解预测目标、输入窗口与数据来源2.1 先确定预测什么再决定网络输出几个节点很多人一上来就写model.add(LSTM(50))结果训练完发现预测出来的曲线永远滞后真实值一截。问题往往出在预测目标没定义清楚。天气预测常见的目标有三类单变量单步用过去 24 小时温度预测下一小时温度、单变量多步预测未来 6 小时温度序列、多变量单步用温度、湿度、气压一起预测下一小时温度。目标不同输出层节点数和损失函数都不一样。单变量单步是最容易跑通的起点输出层一个节点损失用 MSE。多变量输入时输入张量形状从(样本数, 时间步, 1)变成(样本数, 时间步, 特征数)LSTM 层不用改只是input_shape的最后一维要跟着变。多步预测则要把输出层改成Dense(预测步数)或者用 Seq2Seq 结构。我一般建议第一次做先锁定单变量单步把整条链路跑通再往上加复杂度否则调参时根本分不清是数据问题还是模型问题。数据来源上公开气象数据集常见的有历史地面观测小时数据字段一般包含时间戳、气温、相对湿度、气压、风速、降水量。如果只是练手用一份几千到几万行、时间连续的 CSV 就够了。关键是时间列必须能解析成标准时间格式且相邻记录的时间间隔一致否则构造滑动窗口时会错位。2.2 滑动窗口构造把一维时间序列变成监督学习样本RNN 吃的是三维张量而原始 CSV 是一维时间序列中间必须做窗口切分。核心逻辑是用第 t 到 tn 个时刻的值作为输入第 tn1 个时刻的值作为标签然后窗口整体后移一步。import numpy as np import pandas as pd # 读取数据parse_dates 保证时间列是 datetime 类型 df pd.read_csv(weather.csv, parse_dates[datetime]) df df.sort_values(datetime).reset_index(dropTrue) # 只取温度列缺失值先线性插值 series df[temperature].interpolate(methodlinear).values.astype(float32) def make_windows(data, window_size): X, y [], [] for i in range(len(data) - window_size): X.append(data[i:i window_size]) # 输入窗口 y.append(data[i window_size]) # 预测下一个点 X np.array(X).reshape(-1, window_size, 1) # 变成 (样本, 时间步, 特征) y np.array(y) return X, y WINDOW 24 # 用过去 24 小时 X, y make_windows(series, WINDOW) print(X.shape, y.shape) # 例如 (8760, 24, 1) (8760,)这段代码里window_size是最关键的超参数。取 24 表示用过去一天的数据预测下一小时适合有明显日周期的温度。如果预测的是降雨窗口可能要拉到 72 甚至更长因为天气系统的移动尺度更大。reshape(-1, window_size, 1)里的 1 是特征维度多变量时改成特征数量即可。注意窗口切分前一定要排序并处理缺失值否则插值顺序错乱会直接污染训练集。2.3 归一化与训练集划分时间序列不能随机打乱时间序列和图像分类最大的区别是样本之间有先后依赖绝对不能train_test_split(shuffleTrue)。正确做法是按时间顺序切分比如前 80% 做训练后 20% 做测试。归一化也要只用训练集的均值和方差再应用到测试集否则会引入未来信息。# 按时间顺序切分禁止 shuffle split int(len(X) * 0.8) X_train, X_test X[:split], X[split:] y_train, y_test y[:split], y[split:] # 用训练集统计量做标准化 mean, std X_train.mean(), X_train.std() X_train (X_train - mean) / std X_test (X_test - mean) / std y_train (y_train - mean) / std y_test (y_test - mean) / std这里mean和std必须从X_train算不能从全量数据算。预测完成后还要用同样的mean和std反归一化回真实温度否则画出来的曲线数值对不上。这一步是很多新手翻车的地方模型训练 loss 很低但预测值量纲完全不对就是因为忘了反归一化或者用了全量统计量。3. 用 Keras 搭一个能跑通的 RNN 预测模型3.1 模型结构选择SimpleRNN、LSTM 还是 GRU标题说的是循环神经网络实际落地时很少有人用最原始的 SimpleRNN因为它在超过 10 个时间步后梯度消失非常严重预测温度这种有日周期的序列基本学不到长依赖。常见做法是用 LSTM 或 GRU。LSTM 参数多、表达能力强GRU 参数少、训练快在几千到几万样本量级上两者差距不大。我一般先用 GRU 快速验证如果效果不够再换 LSTM。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.optimizers import Adam model Sequential([ LSTM(64, input_shape(WINDOW, 1), return_sequencesFalse), Dropout(0.2), Dense(32, activationrelu), Dense(1) # 回归任务输出一个温度值 ]) model.compile(optimizerAdam(learning_rate0.001), lossmse, metrics[mae]) model.summary()LSTM(64)里的 64 是隐藏单元数样本量小的时候 32 到 64 足够再大容易过拟合。return_sequencesFalse表示只取最后一个时间步的输出传给全连接层如果后面还要接一层 LSTM就要设成 True。Dropout(0.2)是正则化天气数据噪声大加一点 dropout 能明显缓解过拟合。输出层Dense(1)不带激活函数因为回归任务输出范围是任意实数。3.2 训练参数设置与早停策略训练时 batch size 和 epoch 不是拍脑袋定的。时间序列样本通常几千到几万batch size 取 32 或 64 比较稳。epoch 不要固定死用 EarlyStopping 监控验证集 loss连续若干轮不下降就停。from tensorflow.keras.callbacks import EarlyStopping early_stop EarlyStopping( monitorval_loss, patience10, # 连续 10 轮不改善就停 restore_best_weightsTrue ) history model.fit( X_train, y_train, validation_split0.1, # 从训练集尾部再切 10% 做验证 epochs100, batch_size32, callbacks[early_stop], verbose1 )validation_split0.1是从训练集末尾切不是随机切这一点 Keras 对时序数据是友好的。patience10表示给模型 10 轮机会如果验证 loss 一直不降就回滚到最优权重。训练完成后用model.evaluate(X_test, y_test)看测试集 MAE再反归一化换回摄氏度才能判断模型到底能不能用。3.3 预测与可视化把归一化结果还原成真实温度训练完不画图等于没做。把测试集预测结果反归一化和真实值叠在一起看才能发现模型是滞后、平滑过度还是完全没学到。import matplotlib.pyplot as plt pred model.predict(X_test).flatten() pred_real pred * std mean # 反归一化 true_real y_test * std mean plt.figure(figsize(12, 4)) plt.plot(true_real[:200], label真实温度) plt.plot(pred_real[:200], label预测温度) plt.legend() plt.xlabel(时间步) plt.ylabel(温度) plt.show()如果预测曲线整体滞后真实曲线一到两个时间步说明窗口太短或模型容量不够如果预测曲线几乎是一条直线说明模型只学到了均值可能是学习率太大或者归一化有问题。这两种现象在天气预测里非常典型看到图基本就能定位方向。4. 天气预测 RNN 的避坑与排查清单4.1 现象loss 降到很低但预测曲线完全不对原因通常是归一化和反归一化不匹配。训练时用了训练集 mean/std预测后却忘了乘回去或者测试集用了自己的统计量。解决方法是把 mean 和 std 保存下来预测后统一用同一组参数还原并在代码里加一行断言检查数值范围。4.2 现象验证 loss 震荡剧烈时好时坏原因多半是 batch 内样本时间跨度太大或者学习率偏高。时间序列不能 shuffle如果 batch size 太小每个 batch 覆盖的时间片段太窄梯度方向就会来回跳。把 batch size 提到 64 或 128学习率降到 0.0005通常能稳住。4.3 现象模型预测值永远接近历史均值这是典型的欠拟合。可能是窗口太短、LSTM 单元太少或者训练轮数不够就被 EarlyStopping 停了。先把 patience 调大再逐步增加 LSTM 单元数同时观察训练 loss 是否还在下降。如果训练 loss 也降不下去说明模型容量确实不够。4.4 现象测试集表现远差于训练集过拟合。天气数据本身噪声大如果模型在训练集上 MAE 很低但测试集很高优先加 Dropout、减小隐藏单元数、增加训练数据量。另一个容易被忽略的点是数据泄漏如果用全量数据做了归一化测试集信息就提前泄露了必须改成只用训练集统计量。4.5 现象多变量输入时维度报错Keras 对输入形状很敏感。多变量时input_shape要写成(window_size, 特征数)构造窗口时reshape(-1, window_size, 特征数)。如果报ValueError: Input 0 is incompatible先打印X_train.shape确认三个维度分别是样本数、时间步、特征数顺序不能错。5. 把单点预测升级成多步预测的一个实用技巧单变量单步跑通之后最自然的进阶是预测未来多个时刻。直接让Dense(1)改成Dense(6)是一种做法但训练时标签要变成未来 6 个点的向量窗口构造也要跟着改。更省事的技巧是递归预测先用模型预测下一时刻把预测值追加到输入窗口末尾再预测下下时刻循环若干次。def recursive_forecast(model, last_window, steps, mean, std): window last_window.copy() # 形状 (1, WINDOW, 1)已归一化 preds [] for _ in range(steps): p model.predict(window, verbose0)[0, 0] preds.append(p * std mean) # 记录真实尺度 p_norm (p - 0) / 1 # 已是归一化尺度 window np.append(window[:, 1:, :], [[[p_norm]]], axis1) return preds这个方法的优点是复用已训练好的单步模型不用重新设计网络缺点是误差会逐步累积预测步长越长越不准。我一般用它做未来 3 到 6 小时的短期预测再长就改用直接多输出模型。递归预测时每一步都要保持窗口形状不变np.append那行是去掉最老的点、补上最新预测点顺序不能反。验证多步预测效果时不要只看最后一步的误差要把整条预测序列和真实序列对齐画出来观察误差是均匀增长还是某一步突然发散。如果第三步就开始发散说明单步模型本身不够稳得先回去把单步预测的 MAE 压下来。这套流程我反复用过很多次最深的教训就是天气预测里数据质量和窗口设计的重要性远大于换更复杂的网络结构先把归一化、切分、窗口这三件事做对再谈调参。希望帮到你。本文还有配套的精品资源点击获取