
简介这份资源面向高校学生与Python初学者提供一套基于LSTM的气温预测及可视化完整实现方案可用于期末大作业、课程设计或入门深度学习实战。项目以Python语言编写涵盖数据采集、模型构建、训练与预测全流程并配有可视化展示代码注释详尽新手也能看懂下载后简单部署即可运行。压缩包共16个文件约723KB包含8个py源码文件、4个pyc编译文件、2个xlsx数据表与2个md说明文档源码按爬虫、数据集、模型、训练、预测等模块拆分结构清晰便于按需查阅与二次修改。目前已有179人学习下载具备一定参考热度。读者可获得一套可直接运行的完整项目代码、配套文档说明与数据文件既能作为高分大作业提交也能借此理解LSTM时序预测的建模思路与可视化实现方法具有较高的实际应用与学习价值。1. 从一份气温预测源码包说起LSTM 到底能不能跑通去年帮一个学弟看他的期末大作业题目是「基于 LSTM 的气温预测及可视化」他跑出来的预测曲线几乎是一条直线RMSE 高得离谱答辩前三天还在改。我拿到源码包一看问题不在 LSTM 本身而在数据反归一化和时间窗切分上——这两个地方一错模型再深也是白搭。这份基于 LSTM 的气温预测及可视化 Python 源码加文档说明就是围绕这类真实痛点组织的它把数据爬取、序列建模、训练、预测、可视化串成一条完整链路适合正在做 Python 期末大作业、课程设计的学生也适合想快速搭一个时序预测 Demo 的开发者。下面我按「资源结构 → 数据与模型 → 训练与可视化 → 避坑 → 进阶」的顺序把这份包拆开讲清楚让你拿到手就能判断能不能用、怎么改。2. 源码包结构拆解每个文件在链路里干什么2.1 目录清单与模块职责拿到压缩包解压后根目录下能看到这些文件weather_spider.py、data.py、dataset.py、model.py、train.py、predict.py、demo.py、config.py、README.md以及一个data目录和__pycache__缓存目录。这个结构是典型的「数据采集 → 数据处理 → 模型定义 → 训练 → 推理 → 演示」分层不是把所有逻辑塞进一个脚本里对期末大作业来说这种分层本身就是加分项因为答辩时老师能看出你理解模块边界。文件职责关键依赖weather_spider.py抓取历史气温数据落盘到data/requests、BeautifulSoupdata.py读取原始数据、清洗、缺失值处理pandas、numpydataset.py构造时间窗样本划分训练/测试集torch.utils.datamodel.py定义 LSTM 网络结构torch.nntrain.py训练循环、损失计算、模型保存torchpredict.py加载模型、单步/多步预测torchdemo.py可视化预测结果与真实值对比matplotlibconfig.py超参数、路径、窗口大小集中管理无config.py单独存在这件事很关键。很多同学的作业把学习率、窗口大小、批次大小散落在各个文件里改一个参数要翻五个地方最后自己都记不清哪个生效。这份包把配置抽出来你调参时只动一个文件复现实验记录也方便。2.2 环境依赖与首次运行README 里一般会写依赖但为了确保能跑我建议先建虚拟环境再装包。Python 版本用 3.8 到 3.10 之间比较稳太新的版本有时和旧版 PyTorch 有兼容问题。# 创建虚拟环境避免污染全局包 python -m venv venv # Windows 激活 venv\Scripts\activate # macOS / Linux 激活 source venv/bin/activate # 安装核心依赖版本按 README 为准这里给常见组合 pip install torch numpy pandas matplotlib requests beautifulsoup4 scikit-learn装完后先跑weather_spider.py抓数据或者直接用data/里已有的 CSV。注意如果爬虫目标站点结构变了抓取会失败这时候不要死磕爬虫直接用包内自带数据先把训练跑通确认模型链路没问题再回头修爬虫。这是排错的基本顺序——先隔离变量再定位问题。提示__pycache__目录是 Python 自动生成的字节码缓存删掉不影响运行提交作业时建议删掉保持包干净。3. 数据管道与 LSTM 建模窗口切分和归一化是命门3.1 时间序列窗口构造气温预测本质是拿过去 N 天的温度预测未来 M 天的温度。dataset.py里通常用滑动窗口把一维序列切成(样本数, 窗口长度, 特征数)的三维张量。窗口长度就是config.py里的seq_len比如设为 7表示用过去 7 天预测第 8 天。# dataset.py 核心逻辑示意 import numpy as np import torch from torch.utils.data import Dataset class TempDataset(Dataset): def __init__(self, series, seq_len7, pred_len1): self.series series # 归一化后的一维数组 self.seq_len seq_len # 输入窗口长度 self.pred_len pred_len # 预测步长 def __len__(self): # 可切出的样本总数 return len(self.series) - self.seq_len - self.pred_len 1 def __getitem__(self, idx): # 输入窗口 x self.series[idx : idx self.seq_len] # 预测目标 y self.series[idx self.seq_len : idx self.seq_len self.pred_len] # LSTM 要求输入是 (seq_len, feature)这里 feature1 return torch.FloatTensor(x).unsqueeze(-1), torch.FloatTensor(y)这段代码里seq_len和pred_len直接决定样本数量和预测难度。seq_len太小模型看不到足够历史预测会滞后太大样本数骤减训练容易过拟合。我一般先用 7 或 14 试看验证集损失再调。unsqueeze(-1)是把一维序列变成(seq_len, 1)因为nn.LSTM要求输入维度是(batch, seq, feature)少这一维会直接报维度错误这是新手最常见的翻车点之一。3.2 归一化与反归一化气温数值范围不大但归一化仍然必要因为 LSTM 对输入尺度敏感。常见做法是 Min-Max 归一化到[0,1]或者用训练集的均值和标准差做标准化。关键原则归一化参数只能用训练集算然后应用到测试集否则就是数据泄露答辩时被问到会很难解释。# data.py 中的归一化处理 from sklearn.preprocessing import MinMaxScaler scaler MinMaxScaler(feature_range(0, 1)) train_scaled scaler.fit_transform(train_values.reshape(-1, 1)) # 注意测试集用 transform不是 fit_transform test_scaled scaler.transform(test_values.reshape(-1, 1)) # 预测完成后必须反归一化否则画出来的曲线是 0 到 1 之间的 pred_real scaler.inverse_transform(pred_scaled)反归一化这一步如果漏掉可视化出来的预测值全在 0 到 1 之间和真实气温比如 15 到 35 度完全对不上曲线看起来「很平」这就是我开头说的那个学弟踩的坑。predict.py和demo.py里一定要确认有inverse_transform调用。3.3 LSTM 网络结构定义model.py里的网络通常不复杂一层或两层 LSTM接一个全连接层输出预测值。层数不是越多越好气温这种单变量序列两层 LSTM 已经够用再深容易过拟合且训练慢。# model.py 网络定义 import torch.nn as nn class LSTMModel(nn.Module): def __init__(self, input_size1, hidden_size64, num_layers2, output_size1): super().__init__() self.hidden_size hidden_size self.num_layers num_layers # batch_firstTrue 让输入格式为 (batch, seq, feature) self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue) self.fc nn.Linear(hidden_size, output_size) def forward(self, x): # h0、c0 初始化为全零 h0 torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device) c0 torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device) out, _ self.lstm(x, (h0, c0)) # 只取最后一个时间步的输出做预测 out self.fc(out[:, -1, :]) return outhidden_size控制记忆容量64 或 128 是常见起点num_layers是堆叠层数batch_firstTrue这个参数必须和dataset.py的输出维度对齐否则训练时维度报错。out[:, -1, :]表示只拿序列最后一步的隐藏状态去预测因为 LSTM 已经把历史信息压缩到了最后一步。4. 训练、预测与可视化把曲线画对才算完4.1 训练循环与损失监控train.py的核心是标准 PyTorch 训练循环前向传播、算损失、反向传播、更新参数。损失函数用 MSELoss优化器用 Adam学习率从config.py读。# train.py 训练主循环 import torch import torch.nn as nn from model import LSTMModel from dataset import TempDataset from torch.utils.data import DataLoader model LSTMModel() criterion nn.MSELoss() optimizer torch.optim.Adam(model.parameters(), lr0.001) loader DataLoader(TempDataset(train_scaled), batch_size32, shuffleTrue) for epoch in range(100): model.train() total_loss 0 for x, y in loader: optimizer.zero_grad() pred model(x) loss criterion(pred.squeeze(), y.squeeze()) loss.backward() optimizer.step() total_loss loss.item() # 每 10 轮打印一次观察是否收敛 if (epoch 1) % 10 0: print(fEpoch {epoch1}, Loss: {total_loss/len(loader):.6f})lr0.001是 Adam 的常用默认值如果损失震荡不降先降到 0.0005 试如果下降太慢可以升到 0.005但别超过 0.01否则容易发散。shuffleTrue对时间序列要谨慎——如果样本之间有强时序依赖打乱可能破坏顺序信息不过这里每个样本已经是独立窗口打乱通常没问题。训练时一定要打印损失不打印你就不知道模型是没学还是过拟合这是黑匣子调试的基本习惯。4.2 预测与反归一化predict.py负责加载保存的模型权重对测试集做预测并把结果反归一化回真实温度。# predict.py 预测流程 model.load_state_dict(torch.load(best_model.pth)) model.eval() with torch.no_grad(): preds model(test_tensor) # 输出是归一化值 preds preds.squeeze().numpy() preds_real scaler.inverse_transform(preds.reshape(-1, 1))model.eval()会关闭 Dropout 和 BatchNorm 的训练行为预测时必须加。torch.no_grad()关闭梯度计算省显存也提速。反归一化用训练时保存的同一个scaler不能重新 fit否则尺度对不上。4.3 可视化对比与指标计算demo.py用 matplotlib 把真实值和预测值画在同一张图上同时算 RMSE 和 MAE。图能直观看出模型是滞后、偏高还是偏低。# demo.py 可视化 import matplotlib.pyplot as plt from sklearn.metrics import mean_squared_error, mean_absolute_error import numpy as np rmse np.sqrt(mean_squared_error(true_real, preds_real)) mae mean_absolute_error(true_real, preds_real) plt.figure(figsize(12, 5)) plt.plot(true_real, label真实气温, colorblue) plt.plot(preds_real, label预测气温, colorred, linestyle--) plt.xlabel(时间步) plt.ylabel(气温) plt.title(fLSTM 气温预测对比 RMSE{rmse:.2f} MAE{mae:.2f}) plt.legend() plt.savefig(result.png, dpi150) plt.show()RMSE 对异常值敏感MAE 更稳健两个都报出来更专业。如果预测曲线整体滞后真实曲线一个时间步说明seq_len偏小或模型没学到趋势可以试着加长窗口或增加 LSTM 层数。如果曲线几乎重合但测试集 RMSE 很低、换一段数据就崩那多半是过拟合需要加 Dropout 或减少参数量。5. 避坑与排查这几处翻车率最高5.1 预测曲线是一条直线现象可视化出来预测值几乎不变像一条水平线。原因漏了反归一化或者模型输出被squeeze成了标量后广播。解决检查predict.py里有没有inverse_transform确认preds形状是(N,1)而不是(1,N)必要时用reshape(-1,1)修正。5.2 训练损失不下降或变成 NaN现象Loss 一直停在某个值或者几个 epoch 后变成nan。原因学习率太大、输入没归一化、或者序列里有缺失值没处理。解决先把学习率降到 0.0001 试确认data.py里做了缺失值填充前向填充或均值填充检查归一化是否在切窗口之前完成。5.3 维度不匹配报错现象RuntimeError: input must have 3 dimensions或expected hidden size。原因dataset.py返回的x没有unsqueeze(-1)或者batch_first没设成True。解决统一输入形状为(batch, seq_len, 1)nn.LSTM初始化时加batch_firstTrueh0、c0的维度用x.size(0)而不是硬编码。5.4 爬虫抓不到数据现象weather_spider.py运行后data/目录为空或报连接错误。原因目标页面结构变化、请求头被拦截、或者网络环境限制。解决先用包内自带 CSV 跑通训练修爬虫时加User-Agent请求头检查解析用的标签是否还对得上不要在没有数据的情况下怀疑模型。5.5 测试集指标好得离谱现象RMSE 小于 0.1曲线完美重合。原因归一化时对全量数据做了fit_transform测试集信息泄露到训练过程。解决严格按时间顺序切分训练/测试scaler只在训练集上fit测试集只transform。这是时序预测里最隐蔽的坑答辩时被问到「你怎么保证没有未来信息泄露」要能答上来。6. 进阶技巧多步预测与超参数搜索怎么落地单步预测跑通后真正拉开差距的是多步预测。把pred_len从 1 改成 3 或 7模型要一次输出未来多天温度有两种常见做法一种是直接让全连接层输出pred_len个值另一种是自回归——把预测出的值再喂回输入窗口。前者训练稳定但误差会累积后者实现简单但容易漂移。我一般先用直接多输出因为代码改动小model.py里把self.fc nn.Linear(hidden_size, pred_len)即可dataset.py的__getitem__里y取连续pred_len个点。超参数方面seq_len、hidden_size、lr、batch_size这四个最值得搜。不要上重型框架写个三层循环就够# 简易网格搜索记录每组参数的验证集 RMSE import itertools seq_lens [7, 14, 21] hidden_sizes [32, 64, 128] lrs [0.001, 0.0005] results [] for seq_len, hidden, lr in itertools.product(seq_lens, hidden_sizes, lrs): # 用当前参数重建 dataset、model训练若干轮 # 在验证集上算 RMSE追加到 results results.append((seq_len, hidden, lr, val_rmse)) # 按 RMSE 升序取前几组 results.sort(keylambda x: x[3]) for r in results[:5]: print(fseq_len{r[0]}, hidden{r[1]}, lr{r[2]}, RMSE{r[3]:.3f})这个搜索很粗糙但比拍脑袋调参强。注意每次换seq_len都要重新切数据集不能复用旧的 DataLoader。验证集要从训练集里再切一段出来不能直接用测试集选参数否则测试集就失去了评估意义。还有一个容易被忽略的点保存模型时把config.py里的超参数一起存下来或者写进 README。我见过太多人过两周回来跑自己的代码忘了当时seq_len设的多少结果加载权重后维度对不上只能重训。从那以后我每次保存模型都强制把参数字典一起 dump 成 json加载时先读参数再建网络这个习惯帮我省了无数次后悔药。希望这份拆解能帮到你拿到包先跑通链路再按自己的数据改窗口和归一化最后把可视化做扎实期末大作业的分数不会低。本文还有配套的精品资源点击获取