恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
LSTM气温预测实战:从数据预处理到模型可视化
首页
资讯中心
/
LSTM气温预测实战:从数据预处理到模型可视化
LSTM气温预测实战:从数据预处理到模型可视化
发布时间:2026/9/23 5:15:52
简介面向Python课程设计与期末大作业的LSTM气温预测及可视化项目提供完整源码与配套说明文档解决气温序列建模与预测可视化全流程问题。代码注释细致结构清晰即使初学者也能逐步理解时间序列预测的实现思路。压缩包内包含16个文件8个Python脚本覆盖天气数据爬取、数据清洗、数据集构建、模型定义、训练与预测可视化等完整环节2个Excel文件存放气温样本数据另有Markdown格式的说明文档便于阅读环境配置与运行方法。整个资源仅723KB部署轻量下载后简单配置即可运行。目前已有179人学习下载是期末大作业、课程设计或数据挖掘入门实践的优质参考。通过该项目读者可以掌握LSTM在气象数据上的建模流程学习如何编写数据加载器、设计训练循环、保存与调用模型并利用演示脚本快速查看预测效果同时可基于配置文件灵活调整超参数进行二次研究与扩展满足期末答辩与功能演示需求。1. 为什么拿 LSTM 做气温预测值得你抄这份作业每年期末都能看到一大批气温预测的大作业但绝大多数人栽在同一件事上模型跑出来了图也画了老师一问“滞后一拍是怎么回事”就哑火答辩直接翻车。实际上 LSTM 做气温预测是时间序列里最经典、也最适合拿来当课程设计收尾的题目——数据好找、效果直观、可视化能做得很漂亮而且整套流程从数据处理到模型训练再到画图每一步都有清晰的技术标准。这个标题所指向的项目本质是一个最小闭环把某城市过去几年甚至几十年的逐日气温数据喂进 LSTM训练一个模型让它预测未来 7 天乃至 30 天的气温走向再把真实值和预测值叠在一张图上对比展示。你不需要分布式训练、不需要 GPU 集群一台普通笔记本、一个 Python 环境、几百行代码就能跑通。适合的人群很明确正在准备 Python 或机器学习课程期末大作业的学生以及想快速掌握 LSTM 时间序列预测套路、但不想啃长篇论文的初学者。我见过太多人把精力全砸在调神经网络结构上结果忽略了数据预处理和可视化这两个真正的得分点。这篇就是按“数据清洗→滑窗构造→模型训练→可视化→答辩兜底”的顺序把每一步的做法、参数依据和踩过的坑全部摊开讲。2. 数据准备与预处理决定你模型上限的 80% 工作量2.1 数据从哪来、长什么样才算干净常见做法是去中国气象数据网或公开数据集站下载某个城市过去 3~10 年的逐日气温数据保存成 CSV一般至少要有两列日期和当日平均气温。有些数据集还附带最高温、最低温、湿度、风速等字段你可以先用平均气温做单变量预测后续再扩展成多变量输入这在答辩时会变成加分项。拿到数据后第一件事不是建模而是先做三件脏活检查缺失值、检查重复行、确认时间序列的频率是连续的比如有没有跳天。气温站数据经常会有“某几天没记录”或者“传感器故障出现极端值”的情况这些都会直接污染 LSTM 的训练过程。import pandas as pd # 读入原始数据 df pd.read_csv(temperature.csv, parse_dates[date]) # 按日期排序防止原始文件时间顺序错乱 df df.sort_values(date).reset_index(dropTrue) # 检查缺失 print(df.isnull().sum()) # 检查是否有重复日期 print(df[date].duplicated().sum()) # 简单粗暴但有效的填充方式用前后两天的均值补缺失 df[temp] df[temp].interpolate(methodlinear, limit_directionboth)这段代码里parse_dates[date]是把日期列直接解析成 pandas 的 datetime 类型后续做时间切片和画图时非常方便。interpolate(methodlinear)是线性插值对气温这种连续变化的变量来说用前后两天均值补缺失通常足够但如果你发现缺失值连续超过 5 天建议直接考虑删掉这一段否则补出来的数据是假的模型学到的也是假的规律。2.2 归一化为什么 LSTM 对量纲这么敏感LSTM 内部使用 sigmoid 和 tanh 作为激活函数这两个函数的输出区间分别是 (0,1) 和 (-1,1)。如果输入数据的量纲是“摄氏度”这种 0 到 40 的量级或者更夸张的“气温×1000”特征值直接落在激活函数的饱和区梯度会变得非常小训练基本走不动。所以归一化不是可选项而是 LSTM 的硬性前置条件。from sklearn.preprocessing import MinMaxScaler import numpy as np # 提取气温列并转为 numpy 数组 temp_values df[temp].values.reshape(-1, 1) # 实例化归一化器把数据缩放到 [0, 1] 区间 scaler MinMaxScaler(feature_range(0, 1)) scaled_data scaler.fit_transform(temp_values)注意一个高频翻车点fit_transform只能用在训练数据集上验证集和测试集必须用同一个已经 fit 好的 scaler 做transform绝对不能重新 fit。因为一旦重新 fit测试集的信息就“泄漏”进了训练流程你在答辩时如果被问到这个问题会非常难看。2.3 滑窗构造样本time_step 到底怎么定LSTM 不能直接吃一整个序列你需要把时间序列切成“过去 N 天预测未来 M 天”的样本对。这个 N 就是 time_step也就是滑窗长度。选多少合适取决于数据的周期性气温数据有强烈的年周期365 天一个轮回理论上 time_step 越大模型能看到的上下文越完整但训练成本也会暴涨——你的样本数量几乎不变但每个样本的时间维度变长了LSTM 的展开步数也随之增加训练时间成倍上涨。def create_sequences(data, time_step30, predict_step7): X, y [], [] for i in range(len(data) - time_step - predict_step 1): X.append(data[i : i time_step, 0]) # 过去 time_step 天 y.append(data[i time_step : i time_step predict_step, 0]) # 未来 predict_step 天 return np.array(X), np.array(y) time_step 30 # 用过去 30 天的气温 predict_step 7 # 预测未来 7 天 X, y create_sequences(scaled_data, time_step, predict_step) # 按 8:2 切分训练集和测试集注意是时序数据不能随机打乱 split_idx int(len(X) * 0.8) X_train, X_test X[:split_idx], X[split_idx:] y_train, y_test y[:split_idx], y[split_idx:]time_step30 是开学作业里最常见的取值因为它对应“过去一个月”的天气符合直觉但如果你想让结果更好看一点我建议试一下 90 或 120让模型至少看到跨季节的边缘信息然后画一张不同 time_step 的误差对比表这页 PPT 放出去老师基本不会再挑刺。3. 构建 LSTM 模型网络结构设计与训练参数详解3.1 用 Keras 搭一个能跑的模型最少需要几层有了样本之后模型本身反而是最简单的部分。Keras 的 Sequential 接口几行就能搭出一个可用的 LSTM 模型。但“能用”和“拿高分”之间隔着一个 Dropout 和合理的层数选择。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout # 搭建模型 model Sequential() model.add(LSTM(units64, return_sequencesTrue, input_shape(time_step, 1))) model.add(Dropout(0.2)) model.add(LSTM(units32, return_sequencesFalse)) model.add(Dropout(0.2)) model.add(Dense(unitspredict_step)) model.compile(optimizeradam, lossmse, metrics[mae]) print(model.summary())这里用了两层 LSTM第一层return_sequencesTrue表示把每个时间步的隐藏状态都输出给下一层这样第二层 LSTM 可以继续在时间维度上做特征提取第二层return_sequencesFalse只保留最后一个时间步的输出然后接一个 Dense 层直接输出未来 7 天的气温预测值。如果只做未来 1 天的预测第二层 Dense 的输出单元就是 1现在是预测 7 天所以输出单元是 7。units 是 LSTM 隐藏单元的个数你可以理解为模型的“记忆宽度”。64/32 这种组合在气温预测这种单变量、规律较强的任务上通常足够再往上加比如 128/64训练时间会明显变长但精度提升非常有限性价比不高。3.2 训练参数epochs、batch_size、learning_rate 怎么配合接下来是训练环节。很多初学者一上来就把 epochs 拉到 200跑完发现 loss 曲线像心电图一样乱跳然后就开始怀疑模型出了问题。其实问题多半出在 learning_rate 和 batch_size 的配合上。from tensorflow.keras.callbacks import EarlyStopping # 早停当验证集 loss 连续 10 轮不下降就停止训练防止过拟合 early_stop EarlyStopping( monitorval_loss, patience10, restore_best_weightsTrue ) # 训练模型 history model.fit( X_train, y_train, validation_data(X_test, y_test), epochs100, batch_size32, callbacks[early_stop], verbose1 )epochs 设 100但因为加了 EarlyStopping实际一般跑 30~50 轮就会停。patience10的意思是连续 10 轮验证集 loss 没有下降就提前终止并且restore_best_weightsTrue会把模型权重回滚到验证集 loss 最低的那一轮——这是大作业里最实用的“后悔药”。batch_size 在 16~64 之间通常比较稳定数值越大训练越快但收敛越不稳定数值越小收敛越慢但更平滑。在 LSTM 的默认配置里Adam 优化器自带自适应学习率你基本不用手动调整 learning_rate。但如果发现训练 loss 前几轮下降特别快、后面震荡很大可以去model.compile里显式写一个learning_rate0.001Adam 的默认值把学习率降到 0.0005 再试。3.3 训练过程诊断loss 曲线告诉你的三件事模型训练完不要直接拿去画预测图先看 loss 曲线。model.fit返回的 history 对象里保存了每一轮的 loss 和 val_loss画成曲线之后你会看到三种典型情况第一种训练 loss 和验证 loss 同时下降并趋于平缓这是最理想的状态模型收敛了。第二种训练 loss 持续下降但验证 loss 先降后升说明过拟合了需要加大 Dropout 比例、减小模型层数或者增加训练数据。第三种两条曲线同时震荡不收敛说明学习率太高、time_step 太短或者数据没有做好归一化。这里有一个容易被忽略的细节验证集不是随便切的。因为这是时间序列你必须保证验证集在时间上位于训练集之后也就是未来不能随机打乱。上面的代码里split_idx int(len(X) * 0.8)就是按时间顺序切分前 80% 训练后 20% 验证。这一点在答辩时几乎是必问题注意措辞。4. 可视化与结果展示把预测曲线画到让老师眼前一亮4.1 核心图表一真实值 vs 预测值曲线对比可视化是这个题目里最能拉开分差的部分——很多人的模型其实没差多少但图的质量差了一整个档次。最核心的图是把测试集上的真实气温和 LSTM 预测气温叠在同一张坐标轴里一眼看出拟合效果。import matplotlib.pyplot as plt # 用训练好的模型对测试集做预测 predicted model.predict(X_test) # 把预测结果反归一化回真实气温量纲 predicted_temp scaler.inverse_transform(predicted) y_test_temp scaler.inverse_transform(y_test) # 取测试集中最后 180 天做展示太长了反而看不清 show_days 180 plt.figure(figsize(14, 6)) plt.plot(y_test_temp[-show_days:, 0], label真实气温, linewidth1.5) plt.plot(predicted_temp[-show_days:, 0], labelLSTM 预测气温, linewidth1.5, linestyle--) plt.legend() plt.title(真实气温 vs LSTM 预测气温测试集后 {} 天.format(show_days)) plt.xlabel(天数) plt.ylabel(气温℃) plt.grid(alpha0.3) plt.tight_layout() plt.savefig(temperature_prediction.png, dpi200) plt.show()这里要注意scaler.inverse_transform这一步——模型输出的数值是 [0,1] 区间的归一化值不反归一化的话画出来的图纵轴是 0 到 1老师看到会觉得你连单位都没搞清楚。预测结果的第一列对应 predict_step 中的第 1 天这段代码展示的是“每个样本的第一个预测值”和“真实值第 1 天”的对比目的是让你直观看到模型的整体拟合能力。4.2 核心图表二训练过程的 loss 曲线这张图虽然简单但能证明你的模型是“训练收敛”而不是“结果撞运气”。把训练集和验证集的 loss 画在一起老师在答辩时一眼就能判断你是否真的理解训练过程。# 从 history 中提取 loss 数据 train_loss history.history[loss] val_loss history.history[val_loss] plt.figure(figsize(10, 5)) plt.plot(train_loss, label训练集 Loss, linewidth1.8) plt.plot(val_loss, label验证集 Loss, linewidth1.8, linestyle--) plt.legend() plt.title(LSTM 模型训练过程 Loss 变化) plt.xlabel(Epoch) plt.ylabel(MSE Loss) plt.grid(alpha0.3) plt.tight_layout() plt.savefig(loss_curve.png, dpi200) plt.show()如果你加了 EarlyStopping实际训练的 epoch 数会小于你设置的 100但图表里横坐标会自动适配——因为 history 只记录实际跑过的轮次。一个值得在答辩时主动说出来的细节是“验证集 loss 在前 10 轮快速下降之后进入平稳期说明模型在 60 轮左右已经收敛EarlyStopping 帮我节省了约 40% 的训练时间。”这句话本身就是一个知识面展示。4.3 加分图表未来 7 天预测外推展示上面的对比图衡量的是“模型拟合历史数据”的能力但大作业题目说的是“预测”所以你还得展示模型确实能预测未来。常见做法是取测试集最后 time_step 天数据作为输入让模型预测下一个 7 天的气温然后把预测结果接在历史曲线后面画出来。# 取数据集最后 time_step 天作为模型输入 last_sequence scaled_data[-time_step:].reshape(1, time_step, 1) # 预测未来 7 天 future_pred model.predict(last_sequence) future_temp scaler.inverse_transform(future_pred)[0] # 把历史真实气温的末尾 30 天和未来 7 天预测值接在一起画 history_temp temp_values[-30:].flatten() forecast_range np.arange(len(history_temp), len(history_temp) len(future_temp)) plt.figure(figsize(12, 5)) plt.plot(range(len(history_temp)), history_temp, label历史气温, linewidth1.8) plt.plot(forecast_range, future_temp, label未来 7 天预测, linewidth1.8, linestyle--, markero) plt.axvline(xlen(history_temp) - 1, colorgray, linestyle:, alpha0.7) plt.legend() plt.title(未来 7 天气温预测外推) plt.xlabel(天数) plt.ylabel(气温℃) plt.grid(alpha0.3) plt.tight_layout() plt.savefig(future_forecast.png, dpi200) plt.show()画外推图时要注意一个问题模型是拿“过去 30 天”预测“未来 7 天”但预测完 7 天之后如果还想预测更远的未来就需要把预测值当作已知数据重新拼接进输入序列再预测下一个 7 天——这叫递归预测误差会逐日累积预测的置信度也会越来越低。如果答辩时被问到“你能预测多久”回答“短期 7 天可信超过 30 天误差会显著增大”是最诚实的说法。4.4 画图避坑中文乱码与图像清晰度Matplotlib 默认字体不包含中文字符不加处理直接写plt.title(真实气温)出来的图全是方框。两种解决方案一是设置中文字体二是把图表里的所有文字改成英文。对大作业来说直接改成英文是最省事的做法但如果你想展示中文图表更贴近老师阅卷习惯可以用下面的代码指定系统里已有的中文字体。plt.rcParams[font.sans-serif] [SimHei] # Windows 黑体 plt.rcParams[axes.unicode_minus] False # 解决负号显示为方块的问题另外所有plt.savefig()建议都加上dpi200或者更高。很多同学提交的报告里图片模糊到完全看不清曲线趋势就是因为用了默认 dpi100 甚至更低的画质。这属于典型的不费脑但拉好感的小细节一份 30 页的报告里十几张清晰的大图光看视觉效果就已经赢了一半。5. 踩坑记录LSTM 气温预测的 5 个高频翻车现场5.1 预测曲线总比真实曲线“慢半拍”看起来像平移了一样现象把预测值和真实值画在一张图里发现预测曲线的每个波峰波谷都比真实曲线晚了一两天整体走势完全错位。原因这是 LSTM 做时间序列预测最经典的“滞后现象”。本质是模型的 loss 函数在优化时倾向于“复制上一个时间步的值”因为气温日与日之间存在惯性直接复制昨天的值已经能获得很低的 loss。time_step 太短、模型结构太简单、数据噪声大都会加重这种滞后。解决先增加 time_step从 30 提到 90同时加入 Dropout 让模型不能过度依赖近邻时间步如果滞后仍然明显可以把训练 loss 改成 MAE 而不是 MSEMAE 对异常值更鲁棒能稍微缓解这种惯性。5.2 训练到一半 loss 变成 NaN模型直接废了现象前几个 epoch 正常某一步开始 loss 直接变成 nan然后一路 nan 到底。原因最常见的是数据里有 NaN 没处理或者 learning_rate 太大导致梯度爆炸。另一个容易被忽视的原因是归一化时数据里有极端异常值比如某一天气温记录为 999传感器故障MinMaxScaler 会把正常数据压缩到极窄的区间梯度计算随之失控。解决训练之前打印df.isnull().sum()和df.describe()检查异常值如果确认没有数据问题则把model.compile的 learning_rate 从 0.001 降到 0.0001 再试。5.3 归一化之后预测结果反归一化失败或者图形乱掉现象scaler.inverse_transform(predicted)报 shape 错误或者不报错但画出来的预测值范围完全不对比如气温变成 0~1 的小数。原因MinMaxScaler 在 fit 的时候接收的是(N, 1)形状的数据但在inverse_transform时传入的predicted形状是(N, 7)——因为你的 Dense 层输出 7 个值scaler 是在单变量上 fit 的它不知道如何把一个样本的 7 个预测值映射回原始量纲。解决这是最常见的模型设计问题——用单变量 scaler 去反归一化多步预测结果。一个可行方案是训练时把y也做逐列的 scaler 处理但更简单的做法是改用一个支持多输出形状的 scaler或者将 predicted 的每一列分别做 inverse_transform。5.4 模型在训练集上 loss 很低测试集上一塌糊涂现象训练集的拟合曲线几乎完美贴合真实值但在测试集未来数据上预测曲线要么是一条水平的直线要么波动幅度远比实际小。原因这是典型的过拟合并叠加了分布漂移。如果你的数据跨了多个年份模型可能把“今年上半年”的表象特征当成了固定规律而气温本身存在年际差异测试集年份的平均气温可能整体比训练集偏高或偏低。解决检查训练集和测试集的时间分布是否均衡尽量让训练集包含至少 2~3 个完整年份覆盖完整的春夏秋冬如果数据不够可以改用前 90% 做训练、后 10% 做测试而不是 8:2。5.5 每次跑出来的结果都不一样是不是代码写错了现象同一个模型、同一份数据连续跑两次预测曲线不完全一样甚至 loss 曲线也有差异。原因不是代码写错了。LSTM 的权重初始化是随机的训练时 dropout 也会引入随机性模型本身是“非确定性”的。这在学术上完全正常但在大作业里会出现一个尴尬场景——老师在你的笔记本上看你当面运行一次结果和你报告里贴的图不一样。解决在代码开头加np.random.seed(42)和tf.random.set_seed(42)固定随机种子。这是答辩演示必备的一行代码它保证了你的结果可复现报告里的图和现场跑出来的图完全一致。提示固定随机种子只对单线程运行有效。如果你在训练时开了 GPU 并启用了 TF32 精度仍然可能存在微小误差。对课程大作业来说固定 CPU 随机种子已经足够不必深究。6. 从“能跑”到“高分”三个进阶技巧与答辩亮点设计如果你的项目现在只停留在“模型能跑、图能画”的阶段那它只能拿到及格分。想把分数往上提一个档次不需要重新写代码只需要做三件低成本的事。第一个技巧是加一个多变量输入对比实验。原始数据里通常不止气温一列还有湿度、风速、气压。你可以做一个“只用气温”和“气温湿度风速”两个模型的对比实验把两者的测试集 loss 画在一张柱状图里。因为多变量输入给了 LSTM 更多判别信息后者的预测误差通常明显更小这一页 PPT 就是你整份报告里最有说服力的亮点。第二个技巧是做 time_step 的敏感性分析。分别用 time_step7、30、90、180 训练四个模型画出各自的 loss 曲线或预测误差对比表。你会发现 time_step7 时模型几乎学不到跨季节规律time_step180 时训练时间变长但精度趋于平缓——这个“边际收益递减”的结论放在任何答辩现场都是加分项。第三个技巧是误差分析的可视化。不要只画“真实 vs 预测”的曲线再补一张预测误差残差的分布直方图或者按月份分组统计每个月的平均绝对误差MAE。通常夏冬两季的误差明显大于春秋两季原因是夏季极端高温和冬季寒潮的波动幅度大模型难以捕捉。这句话只要你有图有数据地讲出来就证明你不是在背代码而是真的在思考模型的行为。按我这些年做课程设计和带新人的习惯最后一步一定会做“全流程重跑验证”新建一个干净的 Python 环境只装 requirements.txt 里的依赖从python train.py开始确认每一步都无报错跑通。很多人的代码在自己的环境里能跑换个机器就各种缺包、路径错误期末周老师让你现场演示时翻车基本都栽在这种地方。这个习惯救过我很多次希望帮到你。本文还有配套的精品资源点击获取