news 2026/10/1 17:35:11

基于LSTM的气温预测实战:从数据预处理到可视化大屏

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于LSTM的气温预测实战:从数据预处理到可视化大屏

简介:这是一份面向Python开发者、深度学习初学者及气象数据爱好者的实战项目资源,围绕LSTM长短期记忆网络实现气温预测与可视化,可作为毕业设计、课程设计或算法练手参考。压缩包共16个文件,约721KB,以8个py源码文件为核心,辅以4个pyc编译文件、2个xlsx数据集和2份md说明文档,涵盖数据爬取、预处理、模型构建、训练评估到结果可视化的完整链路。项目通过bs4从中国天气网抓取北京、上海、广州、郑州四城多年气温数据,将过去5天特征输入LSTM预测次日温度,并借助Keras调参优化,最终以图表呈现预测效果。已有281人学习,配套文档与注释较为丰富,便于新手理解时间序列建模思路、循环神经网络输入维度设计及可视化分析方法,适合希望快速上手LSTM气温预测的读者参考。

1. 从一份气温预测源码说起:LSTM 到底能解决什么

打开这份「基于 LSTM 的气温预测及可视化」的 Python 源码包,你最先看到的不是模型,而是一堆按日期排列的温度记录。这正是时间序列预测最真实的样子:没有花哨的特征工程,只有一条随时间波动的曲线,而任务就是让模型学会这条曲线的脾气,然后往后推几天。LSTM 之所以常被拿来干这件事,是因为它天生带记忆门控,能记住「上周降温后这周往往回暖」这类跨天依赖,而普通全连接网络只能看到当前这一帧。对做气象数据分析、农业估产、能耗调度的从业者来说,这套东西的价值在于:它把「看历史猜未来」变成了可复现的代码流程,而不是靠经验拍脑袋。新手能顺着它跑通第一个时序模型,熟手则能借它验证自己的特征和调参思路。这一篇就按源码包的实际结构,把数据、模型、训练、可视化四段拆开讲透,顺带把 LSTM 时间序列预测 Python 里最容易翻车的地方标出来。

2. 数据准备与序列构造:气温预测的第一道坎

2.1 气温数据长什么样,先做三件事

拿到气温数据,别急着往模型里灌。我一般先做三件事:看时间跨度、查缺失值、画一眼原始曲线。气温序列通常按天或按小时记录,字段可能叫date、temp、temperature,不同来源命名不统一,得先统一成ds(时间)和y(温度)两列。缺失值在气象数据里很常见,传感器掉线、整点漏采都会留下空洞。直接dropna()会打断时间连续性,LSTM 对等间隔序列更友好,所以常见做法是用线性插值补上,再检查补出来的值有没有超出物理合理范围。

import pandas as pd import numpy as np # 读取原始气温数据,假设字段为 date 和 temp df = pd.read_csv("temperature.csv", parse_dates=["date"]) df = df.rename(columns={"date": "ds", "temp": "y"}).sort_values("ds") # 统一为按天频率,缺失处先标记再插值 df = df.set_index("ds").asfreq("D") print("缺失数量:", df["y"].isna().sum()) # 线性插值补缺,前后各留一个边界值兜底 df["y"] = df["y"].interpolate(method="linear", limit_direction="both") # 简单异常值处理:超出 -60~60 摄氏度的视为记录错误 df.loc[(df["y"] < -60) | (df["y"] > 60), "y"] = np.nan df["y"] = df["y"].interpolate(method="linear", limit_direction="both")

这段代码的逻辑是:先把时间设成索引并强制按天对齐,asfreq("D")会把缺失日期显式暴露出来,而不是悄悄跳过。插值用线性方法,是因为气温短缺口内近似线性变化,比均值填充更贴近真实。异常值阈值设成 -60 到 60,是给极端记录留余地,同时挡掉明显的手误。参数上,limit_direction="both"保证序列开头和结尾的缺口也能补上,否则训练时会出现 NaN 导致报错。

2.2 用滑动窗口把序列切成监督学习样本

LSTM 吃的是「过去 N 天 → 未来 M 天」这样的样本对。窗口长度look_back决定模型能看多远,预测长度look_forward决定一次输出几天。气温有强日周期和年周期,look_back太短学不到趋势,太长则训练慢且容易过拟合。我一般从 7 天或 14 天起步,先跑通再调。

def make_sequences(values, look_back=14, look_forward=1): X, y = [], [] for i in range(len(values) - look_back - look_forward + 1): X.append(values[i : i + look_back]) y.append(values[i + look_back : i + look_back + look_forward]) return np.array(X), np.array(y) look_back, look_forward = 14, 1 X, y = make_sequences(df["y"].values, look_back, look_forward) print("样本形状:", X.shape, y.shape) # 例如 (n, 14) 和 (n, 1)

这里X的每一行是连续 14 天的温度,y是紧接着的第 15 天。look_forward=1表示单步预测,改成 3 就是一次预测未来三天。注意循环上界要减去look_back + look_forward再减一,否则最后几个样本会越界。切完之后必须按时间顺序划分训练集和测试集,不能随机打乱,否则等于用未来数据预测过去,指标会好看得离谱,上线就崩。

2.3 归一化与反归一化:别让量纲毁了训练

温度数值本身不大,但不同地区可能一个在 -20 到 40,一个在 10 到 35。归一化到 [0,1] 能让梯度更稳。关键是反归一化要一一对应,训练时用训练集的最大最小值,预测后要用同一组参数还原,否则可视化出来的曲线会整体偏移。

from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler(feature_range=(0, 1)) scaled = scaler.fit_transform(df["y"].values.reshape(-1, 1)).flatten() # 划分训练/测试,保持时间顺序 split = int(len(scaled) * 0.8) train, test = scaled[:split], scaled[split:] # 反归一化示例 def inverse(values): return scaler.inverse_transform(np.array(values).reshape(-1, 1)).flatten()

fit_transform只在训练集上做,测试集用transform,这是铁律。很多人图省事对全量数据fit,结果测试集信息泄漏进归一化参数,评估偏乐观。反归一化函数单独抽出来,是为了可视化时直接调用,避免每处都手写 reshape 出错。

3. 搭一个能跑通的 LSTM 模型:层数、维度与训练参数

3.1 用 PyTorch 定义 LSTM 回归网络

这份源码大概率用 PyTorch 或 Keras,两者思路一致。下面用 PyTorch 写一个最小可用版本,输入维度 1,隐藏层 64,输出 1。LSTM 层负责提取时序依赖,最后接一个全连接把隐藏状态映射成温度值。

import torch import torch.nn as nn class TempLSTM(nn.Module): def __init__(self, input_size=1, hidden_size=64, num_layers=2, output_size=1): super().__init__() self.lstm = nn.LSTM( input_size=input_size, hidden_size=hidden_size, num_layers=num_layers, batch_first=True, dropout=0.2, ) self.fc = nn.Linear(hidden_size, output_size) def forward(self, x): # x: (batch, seq_len, input_size) out, _ = self.lstm(x) # 取最后一个时间步的隐藏状态做回归 return self.fc(out[:, -1, :])

batch_first=True让输入形状是(batch, seq_len, feature),符合直觉。num_layers=2加dropout=0.2是常见组合,层数再多在气温这种低维序列上收益有限,反而容易过拟合。out[:, -1, :]取最后一步,是因为 LSTM 已经把整段序列的信息压缩到最终隐藏状态里。如果你的任务是预测未来多天,这里要改成对多个时间步输出,或者用 Seq2Seq 结构。

3.2 训练循环里必须盯住的四个量

训练不是把数据丢进去等 loss 下降就完事。气温预测里我重点看四个量:训练 loss、验证 loss、学习率、以及预测曲线和真实曲线的相位差。前两个判断是否过拟合,学习率决定收敛快慢,相位差则暴露模型是不是只会「滞后复制」。

from torch.utils.data import DataLoader, TensorDataset # 构造张量数据集 X_tensor = torch.tensor(X, dtype=torch.float32).unsqueeze(-1) y_tensor = torch.tensor(y, dtype=torch.float32) train_ds = TensorDataset(X_tensor[:split], y_tensor[:split]) train_loader = DataLoader(train_ds, batch_size=32, shuffle=True) model = TempLSTM() criterion = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) for epoch in range(50): model.train() total_loss = 0 for xb, yb in train_loader: optimizer.zero_grad() pred = model(xb) loss = criterion(pred, yb) loss.backward() optimizer.step() total_loss += loss.item() if (epoch + 1) % 10 == 0: print(f"Epoch {epoch+1}, Loss {total_loss/len(train_loader):.4f}")

batch_size=32在几千条样本上比较稳,太小梯度噪声大,太大收敛慢。lr=1e-3是 Adam 的常用起点,如果 loss 震荡就降到 1e-4。注意shuffle=True只在训练集内部打乱样本顺序,不破坏时间划分。每 10 轮打印一次,方便观察是否早停。如果验证 loss 连续上升而训练 loss 还在降,就该加 dropout 或减层数了。

3.3 评估指标别只看 MSE

MSE 对异常值敏感,气温里偶尔的极端高温会把指标拉偏。我一般同时看 MAE 和 MAPE,MAE 直观反映平均误差多少度,MAPE 看相对误差。更重要的是画图对比,因为指标好不代表曲线形状对。

model.eval() with torch.no_grad(): pred_test = model(X_tensor[split:]).squeeze().numpy() pred_real = inverse(pred_test) true_real = inverse(y_tensor[split:].squeeze().numpy()) mae = np.mean(np.abs(pred_real - true_real)) print(f"测试集 MAE:{mae:.2f} 摄氏度")

model.eval()会关闭 dropout,保证预测稳定。torch.no_grad()省显存。反归一化后算 MAE,单位才是摄氏度,直接拿归一化值算没有物理意义。如果 MAE 在 2 度以内,对日尺度气温预测就算可用;超过 5 度,先回去检查窗口长度和数据泄漏。

4. 可视化:把预测曲线和误差讲清楚

4.1 用 Matplotlib 画预测对比图

可视化不是装饰,是排查工具。一张好的对比图能立刻看出模型是滞后、平滑过度还是完全跑偏。基础画法是把真实值和预测值叠在同一张时间轴上,再补一条误差带。

import matplotlib.pyplot as plt plt.figure(figsize=(12, 5)) plt.plot(true_real, label="真实气温", color="#2c7fb8", linewidth=1.5) plt.plot(pred_real, label="LSTM 预测", color="#e6550d", linewidth=1.5, linestyle="--") plt.fill_between( range(len(true_real)), true_real, pred_real, color="gray", alpha=0.2, label="误差带", ) plt.xlabel("测试集天数") plt.ylabel("气温(摄氏度)") plt.legend() plt.title("LSTM 气温预测对比") plt.tight_layout() plt.savefig("prediction.png", dpi=150)

fill_between把真实和预测之间的差值涂成灰色,误差大的地方一眼可见。dpi=150保证保存的图在文档里不糊。如果曲线整体滞后一天,说明look_back不够或模型没学到突变;如果预测线过于平滑,可能是 dropout 太大或隐藏层太窄。

4.2 误差分布与滚动指标

单看一条曲线不够,还要看误差随时间的分布。把绝对误差按天画出来,能发现模型在换季时段是不是特别差。

errors = np.abs(true_real - pred_real) plt.figure(figsize=(12, 4)) plt.bar(range(len(errors)), errors, color="#31a354", alpha=0.7) plt.axhline(errors.mean(), color="red", linestyle="--", label=f"平均误差 {errors.mean():.2f}") plt.xlabel("测试集天数") plt.ylabel("绝对误差(摄氏度)") plt.legend() plt.tight_layout() plt.savefig("error_bar.png", dpi=150)

红色虚线是平均误差,柱子明显高出的区段就是模型的薄弱环节。常见做法是回到那段数据看有没有寒潮或热浪,如果有,考虑加入天气类型作为额外特征,或者对极端值单独建模。这一步是很多源码包省略的,但恰恰是能不能落地的分水岭。

4.3 可视化大屏场景下的数据接口

如果这套预测要接到可视化大屏上,别直接把 Matplotlib 图片塞进去。大屏通常用 ECharts 或类似前端图表库,需要的是 JSON 格式的时间序列。常见做法是训练完把预测结果导出成[{date, real, pred}, ...],前端按需渲染。

import json records = [] for i in range(len(true_real)): records.append({ "date": str(df.index[split + i].date()), "real": round(float(true_real[i]), 2), "pred": round(float(pred_real[i]), 2), }) with open("prediction.json", "w", encoding="utf-8") as f: json.dump(records, f, ensure_ascii=False, indent=2)

导出时保留日期字段,前端才能对齐时间轴。round到两位小数足够展示,也减小文件体积。如果大屏要实时刷新,就把推理部分封装成函数,定时读最新数据、滑窗预测、推送 JSON,而不是每次重新训练。

5. 避坑与排查:气温预测里最容易翻车的五件事

5.1 现象:测试集指标极好,上线后完全不准

原因:划分数据时随机打乱,或者归一化用了全量数据,导致未来信息泄漏。解决:严格按时间顺序切分,fit只在训练集上做,测试集只transform。检查方法很简单,把测试集整体后移一天再预测,如果指标暴跌,说明之前有泄漏。

5.2 现象:预测曲线整体滞后真实曲线一天

原因:look_back太短,模型只能靠最近值外推;或者损失函数对相位不敏感。解决:把窗口从 7 天加到 14 天甚至 30 天,并在输入里加入日期特征(月份、星期)帮助模型识别周期。滞后严重时,也可以改用差分序列建模,让模型学变化量而不是绝对值。

5.3 现象:训练 loss 一直不降,或者降到某个值就卡住

原因:学习率太大导致震荡,或者输入没有归一化。解决:先把学习率降到 1e-4 试,再确认归一化范围。如果还不行,检查序列构造有没有把 NaN 带进去,NaN 会让 loss 直接变 NaN 而不报错。用np.isnan(X).sum()快速排查。

5.4 现象:换一个地区的数据,模型效果断崖式下降

原因:不同地区气温分布差异大,用 A 地训练的归一化参数直接套 B 地,输入范围对不上。解决:每个地区单独训练归一化器,或者改用对量纲不敏感的标准化(减均值除标准差)。跨地区迁移时,至少要在目标地区数据上微调几轮。

5.5 现象:可视化图里预测线比真实线平滑很多

原因:MSE 损失倾向于输出条件均值,天然会抹平波动;dropout 过大也会让输出保守。解决:换用 MAE 或 Huber 损失,降低 dropout 到 0.1,或者增加隐藏层宽度。如果业务更关心峰值,可以在损失里对高温和低温样本加权。

6. 把单点预测升级成多步滚动预测的一个技巧

单步预测跑通后,真正有用的是未来多天预测。最直接的做法是递归:用预测出的第 15 天拼进输入,再预测第 16 天。但这样误差会累积,几步之后曲线就塌了。我的习惯是改用「直接多输出」:让模型一次吐出未来 3 到 7 天,损失函数对所有步一起算。这样每个预测都直接依赖历史窗口,不依赖上一步的预测值,误差不会滚雪球。

# 直接多输出:look_forward=3 X_multi, y_multi = make_sequences(df["y"].values, look_back=14, look_forward=3) print("多步样本:", X_multi.shape, y_multi.shape) # (n, 14) 和 (n, 3) class MultiStepLSTM(nn.Module): def __init__(self, input_size=1, hidden_size=64, num_layers=2, output_size=3): super().__init__() self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True, dropout=0.2) self.fc = nn.Linear(hidden_size, output_size) def forward(self, x): out, _ = self.lstm(x) return self.fc(out[:, -1, :]) # 一次输出未来 3 天

output_size改成 3,标签y也变成三维,训练代码几乎不用动。评估时分别算第 1、2、3 天的 MAE,通常第 3 天误差会明显大于第 1 天,这是正常的。如果第 3 天误差爆炸,说明窗口信息不够,加长look_back或加入气压、湿度等外生变量。验证多步效果时,我习惯把预测的第 1 天和真实第 1 天对齐画图,再单独看第 3 天的散点,避免被平均指标骗过去。

这套流程我在几个不同地区的气温数据上跑过,最深的教训是:别在归一化和数据划分上偷懒,这两个地方出的问题,模型结构再花哨也救不回来。另一个习惯是每次改完参数先跑 10 轮看 loss 曲线,确认方向对了再跑满,省得等半天发现是数据错了。希望帮到你。

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

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

消息队列选型与重复消费实战:从原理到落地避坑指南

消息队列这四个字&#xff0c;在很多团队眼里就是个“发件箱”。订单创建成功了&#xff0c;往队列里丢一条消息&#xff0c;库存、积分、短信各取所需&#xff0c;谁有空谁来消费。这个理解大方向没错&#xff0c;但如果你真把它当成一个普通发件箱来用&#xff0c;生产环境迟…

作者头像 李华
网站建设 2026/10/1 17:33:48

小米MiMo-V2.6大模型部署实战:Pro与Flash选型、性能调优与避坑指南

1. 从一次模型选型聊起&#xff1a;为什么MiMo-V2.6值得单独写一篇上个月帮一个做智能硬件的团队做技术选型&#xff0c;他们的场景很具体&#xff1a;在本地服务器上跑一个能理解设备日志、能回答运维问题、还能做简单代码补全的模型&#xff0c;预算有限&#xff0c;不想按AP…

作者头像 李华
网站建设 2026/10/1 17:33:36

MATLAB实战:SVM-KNN组合分类器在信用风险评分卡中的应用与调优

简介&#xff1a;这份资源面向机器学习初学者与需要完成分类实验的开发者&#xff0c;围绕支持向量机&#xff08;SVM&#xff09;与K近邻&#xff08;KNN&#xff09;两种经典算法&#xff0c;重点给出二者组合模型SVM-KNN的MATLAB实现思路。SVM擅长小样本与非线性分类&#x…

作者头像 李华
网站建设 2026/10/1 17:30:38

Java+SSM+Django学费管理系统实战:从设计到部署

校务缴费那块儿&#xff0c;我建议你可以先试试这个思路——用Java、SSM、Django这一套技术组合去搭一个学费管理系统。这东西不是新鲜概念&#xff0c;但真正做扎实、能在实际场景里跑起来&#xff0c;还挺考验细节的。我是做Java后端开发的&#xff0c;这两年帮朋友和几个小型…

作者头像 李华
网站建设 2026/10/1 17:30:37

CPU到底是什么?从原理到天梯图,一文读懂核心参数与性能排查

刷到这篇文章的朋友&#xff0c;多半被CPU这几个词反复折腾过——天梯图看得眼花&#xff0c;参数表读不明白&#xff0c;电脑一卡就觉得是CPU不行。我在装机、排查问题、研究调度逻辑这些年里发现&#xff0c;九成困惑都源于同一个问题&#xff1a;对CPU这个“大脑”本身不够了…

作者头像 李华
网站建设 2026/10/1 17:30:20

文玩电商系统:规格爆炸与密码加盐的Java实战方案

简介&#xff1a;这是一套面向计算机专业本科生的毕业设计级Java Web实战项目&#xff0c;聚焦文玩电商场景&#xff0c;完整覆盖需求分析、系统开发到成果展示全流程。资源包含可直接运行的JSPServletMySQL源码、结构清晰的毕业论文、答辩用PPT、开题报告、任务书、数据库SQL文…

作者头像 李华