资讯动态

LSTM温度预测期末大作业:源码+文档完整实现与避坑指南

发布时间:2026/10/22 12:12:31 来源:尧图企业网站定制
简介面向计算机相关专业学生及需要项目实战练习的学习者这份期末大作业以长短期记忆LSTM神经网络为技术核心完整实现温度预测分析流程经导师指导并获评98分高分可直接参考用于课程设计或期末大作业。资源包为zip压缩格式共15个文件、约1.51MB包含5个Python脚本数据获取、模型训练、保存与预测、6张可视化图表季节性分解、真实值对比预测值、训练与验证损失等、1个Excel温度数据集、1个训练好的模型文件、1份说明文档及配置文件结构清晰便于按需查阅。目前已有154人学习下载。项目提供可直接运行的完整代码附有真实温度数据及多组预测对比图能帮助读者深入理解LSTM在时序预测中的数据处理、特征分析和结果评估思路也适合在此基础上扩展改造快速搭建出属于自己的项目。1. LSTM温度预测期末大作业源码文档到底要交什么、能怎么抄期末前两周老师扔来一个题目基于 LSTM 神经网络的温度预测分析要求用 Python 实现交付源码和文档说明。这个项目要做的事听起来直白——给网络一段历史温度记录让它预测下一个时刻或明天几点的气温但真动手会发现选型、喂数据、调参、画图、写报告每一环都有坑。这篇文章把整套方案拆成一条可复现的路线先讲清为什么温度预测这种时间序列问题适合用 LSTM再给出一份数据预处理、模型搭建、训练评估的最小代码最后把最容易翻车的几个坑直接摆出来。适合三类人想快速交出一份能运行作业的想搞懂 LSTM 时序预测闭环的以及需要在答辩时把黑匣子讲明白的同学。2. 为什么温度预测选 LSTM从时序问题到门机制再到项目结构2.1 温度是典型的时间序列前馈神经网络在这里会翻车温度数据天然是时间序列传感器每小时记录一次得到一个按时间排序的序列。这个序列有很明显的结构昼夜周期让白天温度高、凌晨温度低季节又给出更长的趋势而相邻时刻的读数往往平滑变化。所谓预测本质是“用过去推断未来”过去 24 小时的温度走势极大程度上决定了下一小时大概是多少度。问题在于普通的全连接神经网络也就是常说的前馈神经网络、BP 网络把每个样本当成独立对象处理。假设你用过去 24 小时的温度预测下一小时最粗暴的做法是拼出一个 24 维向量丢进全连接网络输入节点就是第 0 小时、第 1 小时……第 23 小时的温度。对网络来说这 24 个位置没有先后概念它只能把它们当作 24 个互不相干的特征顺序被打乱后结果几乎不变。而温度预测偏偏依赖顺序昨天 20 点还在降温、今天 6 点开始回暖这个“过程”比“某个点是多少度”更重要。一维卷积网络也能捕捉局部趋势但卷积核的感受野有限想覆盖 24 小时以上的依赖就得拼命堆层训练成本高且容易过拟合。所以这个题目落到循环网络头上而 LSTM 又是循环网络里对长序列最稳的版本。选它不是因为“新”而是因为温度预测需要的“记忆历史、更新状态、输出预测”三步恰好是 LSTM 设计好的能力。2.2 LSTM 的三个门遗忘门、输入门、输出门对温度意味着什么先看普通 RNN每个时间步接收当前输入 xt 和上一个时间步的隐状态 ht-1输出新的隐状态 ht再用 ht 做预测。思路很自然但问题是靠反向传播训练时梯度在多个时间步之间连乘很容易指数级衰减也就是常说的梯度消失。序列稍微长一点前面的天气信息就传不到后面网络只能记住最近几小时相当于一个记忆力很差的预报员。LSTM 在 RNN 基础上增加了一条细胞状态 C_t相当于给网络加了一条“记忆主线”再通过三个门控制这条主线上信息的增删。遗忘门决定上一时刻的细胞状态保留多少输入门决定当前时刻的新信息写入多少输出门决定把当前细胞状态暴露多少给最终输出。三个门都是 sigmoid 输出 0 到 1 的权重然后用向量乘法去控制信息流。放到温度场景里特别好理解。窗口取了 72 小时前 48 小时是平常的昼夜波动最后 6 小时开始受冷空气影响缓慢降温。遗忘门会记住“这个季节凌晨气温本来就低”这种长期规律把个别异常高温逐渐淡忘输入门把当前“冷空气正在南下”的观测写进细胞状态输出门在给出下一小时预测前综合隐状态和细胞状态输出一个合理的温度值。正因为有这个显式的记忆机制LSTM 才能把 24 小时甚至 72 小时前的信息带到当前预测里而不是像普通 RNN 那样只记得最近几小时。模型的数值实现并不复杂PyTorch 里一个nn.LSTM层就封装好了全部门逻辑你不需要手写三个门的权重公式。但答辩时通常会问“为什么 LSTM 比 RNN 适合这个任务”能把门机制用温度的例子讲清楚比背公式更有说服力。2.3 源码包与文档说明期末大作业交付的文件结构一份能直接跑通的期末大作业源码目录结构不需要花哨但一定要让人一眼看懂每个文件的职责。常见做法是这样组织temperature_lstm/ ├── data/ │ └── temperature.csv # 原始温度数据 ├── src/ │ ├── data_preprocess.py # 数据清洗、滑窗、归一化 │ ├── model.py # LSTM 模型定义 │ ├── train.py # 训练与验证主流程 │ └── predict.py # 加载模型、预测、绘图 ├── requirements.txt # 依赖清单 └── 文档说明.md # 期末报告/答辩文档把数据、模型、训练、预测拆开是这类型项目最稳妥的分层方式。数据预处理单独放一个文件因为后面调窗口、调归一化最频繁模型单独放一个文件方便在 LSTM、多变量输入之间切换predict.py只负责加载训练好的模型做推理和训练代码解耦答辩演示时不容易手忙脚乱。文档说明不是把源码复制一遍贴进 Word而是回答三个问题数据是什么、为什么选 LSTM、结果有没有说服力。常见章节和写作要点如下文档章节内容要点建议篇幅需求分析任务背景、数据来源、预测目标定义半页到一页数据处理缺失值处理、归一化方式、滑动窗口构造一到两页模型设计LSTM 结构、门机制、与普通 RNN 的对比两到三页实验分析训练损失曲线、RMSE、真实值与预测值对比图两到三页总结与展望模型局限、多步预测、多变量扩展方向半页到一页需求分析里写清楚“预测未来一小时”数据处理里写清楚“用过去 24 小时预测未来 1 小时”模型设计里写清楚“输入形状是 (batch, 24, 1)”实验分析里给出具体误差数值。这几句写实整份文档的可信度就立住了。3. 数据准备把温度时间序列喂进 LSTM 的四个必要步骤3.1 数据从哪来单变量温度序列长什么样期末大作业里数据一般由老师提供或者从气象网站手动整理常见格式就是一个 CSV两列时间、温度。少数会把湿度、气压也放进来那种叫多变量预测后面再扩展。第一步先把 CSV 读进来把datetime解析成时间索引把温度列变成浮点数组。import pandas as pd df pd.read_csv(data/temperature.csv, parse_dates[datetime]) df df.sort_values(datetime) # 按时间排序 temp df[temp].values.reshape(-1, 1) # 转成 (N, 1) 的二维数组逻辑说明parse_dates让 pandas 把时间字符串转成真正的时间对象sort_values保证数据按时间升序。最后reshape(-1, 1)很关键LSTM 期望每个时间步的特征是二维索引(样本, 特征)哪怕只有一个温度特征也要是列向量而不是一维数组否则后面滑窗和 PyTorch 张量转换会频繁报维度错误。参数说明如果数据本身按小时采样temp的长度就是小时数。读进来后先看一眼有没有缺失值常见做法是df[temp].isnull().sum()检查缺失不多就用前向填充df[temp].ffill()。温度传感器偶尔会蹦出明显异常值比如 -50℃ 或 80℃这种直接用前后均值替换或剔除不用做太复杂的统计处理。3.2 滑动窗口构造样本窗口大小和预测步长怎么定模型不能直接吃一整条温度序列得把它切成“过去 N 个时刻 → 未来 M 个时刻”的样本对。这个切法叫滑动窗口N 是窗口大小M 是预测步长。温度预测最常见的是 N24、M1也就是用过去 24 小时预测下一个小时。import numpy as np def create_sequences(data, window_size24, pred_len1): X, y [], [] for i in range(len(data) - window_size - pred_len 1): X.append(data[i:i window_size]) # 过去 window_size 个时刻 y.append(data[i window_size:i window_size pred_len]) # 未来 pred_len 个时刻 return np.array(X), np.array(y) X, y create_sequences(temp, window_size24, pred_len1) print(X.shape, y.shape) # 例如 (7609, 24, 1) (7609, 1, 1)逻辑说明循环从第window_size个位置开始每往后挪一位就切出一个样本。X的形状是(样本数, 窗口大小, 特征数)这个三维结构正是 LSTM 要求的输入布局batch 维、时间步维、特征维。y的形状是(样本数, 预测步长, 特征数)训练时通常把后两维压平再算损失。参数说明窗口大小直接影响模型能看到的“记忆范围”。做小时级温度预测窗口 24 覆盖一天昼夜周期48 覆盖两天冷暖交替72 能看到更长趋势。窗口太小模型看不到完整周期窗口太大数据量减少、训练变慢。预测步长先设 1跑通整个流程后再尝试pred_len3或 6 做多步预测。温度序列里滑窗样本之间有大量重叠这是正常现象不需要刻意去重。3.3 归一化到 [0,1]MinMaxScaler 的用法和反归一化的位置LSTM 内部用 sigmoid 和 tanh 做激活输入数值一大梯度很容易饱和甚至爆炸训练 loss 直接变成 NaN。所以温度原始值比如 30.5不能直接喂进去要先缩放到 [0,1] 区间。用 sklearn 的MinMaxScaler是最省事的做法。from sklearn.preprocessing import MinMaxScaler # 先按 8:2 切分原始序列再分别缩放 train_size int(len(temp) * 0.8) train_data, test_data temp[:train_size], temp[train_size:] scaler MinMaxScaler(feature_range(0, 1)) scaled_train scaler.fit_transform(train_data) # 只在训练集上 fit scaled_test scaler.transform(test_data) # 测试集只 transform逻辑说明fit_transform计算训练集的最大值和最小值并完成缩放transform直接套用训练集算好的参数去缩放测试集。这里有一个必须遵守的原则测试集不能参与fit。一旦把整个序列的最大最小值都算进去等于让模型在训练阶段偷看了未来的温度范围测试误差会虚低答辩时被问一句就能问穿。反归一化要放到模型输出之后不在数据准备阶段做。训练时模型输出的是 0 到 1 的缩放值评估预测效果时再用scaler.inverse_transform还原成真实温度摄氏度这样才能算 RMSE 和画对比图。提前做反归一化会让损失函数在缩放后的空间里失去参考意义。注意反归一化时传进去的形状必须是(样本数, 1)模型输出如果是一维向量先reshape(-1, 1)再还原。3.4 按时间切分训练集和测试集不要随机打乱很多做分类任务的同学习惯直接用train_test_split随机打乱数据这在时间序列预测里是致命的。温度序列一旦打乱训练集里混着未来的数据模型等于开卷考试测试集上表现再好也不能说明任何问题。from torch.utils.data import TensorDataset, DataLoader import torch X_train, y_train create_sequences(scaled_train, window_size24, pred_len1) X_test, y_test create_sequences(scaled_test, window_size24, pred_len1) # 转成 PyTorch 张量并用 DataLoader 分批 train_ds TensorDataset(torch.FloatTensor(X_train), torch.FloatTensor(y_train).squeeze(-1)) train_loader DataLoader(train_ds, batch_size64, shuffleTrue) test_ds TensorDataset(torch.FloatTensor(X_test), torch.FloatTensor(y_test).squeeze(-1)) test_loader DataLoader(test_ds, batch_size128, shuffleFalse)逻辑说明这里先把整条温度序列按 8:2 切成train_data和test_data再各自做滑窗而不是在整个序列滑完窗再切分样本。这样测试集里所有的窗口都完完整整落在测试时间段内不会出现一个窗口前半段在训练期、后半段在测试期的泄漏。参数说明训练集DataLoader的shuffleTrue可以在每个 epoch 内打乱样本顺序这对滑窗产生的重叠样本有正则化作用防止模型记住样本的出场顺序测试集必须shuffleFalse保证预测结果和真实时间顺序对应方便后面画图。batch_size64是内存和训练速度的平衡点数据量小可以换成 32数据量大可以调大到 128。4. 搭建 LSTM 模型与训练调参从随机损失到稳定收敛4.1 实现路线选择PyTorch 还是 Keras同样的 LSTM 模型Keras 三四行就能定义完训练也只要一行fit。如果只看“能不能跑”Keras 上手最快。但期末大作业通常需要答辩老师很可能会问forward里数据经过了几步变换LSTM 输出怎么变成最终预测的用 PyTorch 能把每一步写得明明白白调试时还能在中间打印张量形状信息量比 Keras 大得多。所以下面以 PyTorch 为例先把环境装好Python 3.8 以上然后安装 PyTorch、NumPy、Pandas、scikit-learn、Matplotlib缺哪个补哪个网上教程很多这里不展开。4.2 最小可运行的 LSTM 模型代码PyTorch 版模型结构遵循一个固定套路LSTM 层负责提取时间特征全连接层负责把最后一个时间步的隐状态映射成预测温度。最小实现大概是这样的import torch import torch.nn as nn class LSTMPredictor(nn.Module): def __init__(self, input_size1, hidden_size64, num_layers2, output_size1): super().__init__() self.lstm nn.LSTM(input_sizeinput_size, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue) self.fc nn.Linear(hidden_size, output_size) def forward(self, x): out, (hn, cn) self.lstm(x) last out[:, -1, :] # 取最后一个时间步的隐状态 return self.fc(last)逻辑说明输入x的形状是(batch, seq_len, input_size)因为设了batch_firstTruebatch 维排在最前面。self.lstm返回两个对象out是每个时间步的隐状态序列形状(batch, seq_len, hidden_size)hn和cn是最后一个时间步的隐状态和细胞状态。这里取out[:, -1, :]也就是最后一个时刻的隐状态然后接一个线性层输出单值温度。参数说明input_size1对应单变量温度输入如果后面加了湿度、气压改成对应特征数。hidden_size是隐单元数量决定模型容量起步 64 比较稳妥num_layers2表示堆叠两层 LSTM能捕捉更复杂的时序模式但层数超过 3 对温度这种小数据量反而容易过拟合。output_size1是单步预测多步预测时改成预测步长数。4.3 训练循环与关键参数epochs、batch_size、learning rate 怎么定模型定义好之后接下来是最有“玄学”感的训练环节。温度预测的损失函数用均方误差 MSE优化器用 Adam初始学习率 0.001这三个选择在大多数回归任务里都是安全起点。model LSTMPredictor(input_size1, hidden_size64, num_layers2, output_size1) criterion nn.MSELoss() optimizer torch.optim.Adam(model.parameters(), lr0.001) epochs 100 for epoch in range(epochs): model.train() epoch_loss 0 for xb, yb in train_loader: optimizer.zero_grad() pred model(xb) # (batch, 1) loss criterion(pred, yb) # yb 形状已在 DataLoader 中压成 (batch,) loss.backward() optimizer.step() epoch_loss loss.item() * len(xb) epoch_loss / len(train_ds) if (epoch 1) % 10 0: print(fepoch {epoch1}/{epochs}, train_loss{epoch_loss:.6f})逻辑说明每个 batch 先清零梯度再前向计算pred拿预测值和真实值算 MSE反向传播后optimizer.step()更新权重。epoch_loss按样本数加权平均保证不同 batch 大小下 loss 可比。如果 loss 一开始就在几个数值之间剧烈震荡说明学习率太大如果 loss 下降几轮后卡住不动先把学习率调小再观察。参数说明epochs100对小时级温度数据通常够用但不要盲目跑满后面会讲早停。batch_size影响梯度估计的稳定性64 是常见默认值。lr0.001是 Adam 的默认学习率温度数据本身比较平稳很少需要超过 0.01如果 loss 出现 NaN第一件事就是把 lr 降到 0.0005 或 0.0001。4.4 过拟合信号与早停别让训练损失骗了你训练损失一路下降不代表模型真的学到了温度规律。LSTM 容量一大完全可以把训练数据背下来到验证集上立刻现原形。所以训练时要预留一小段验证集每轮结束后算一次验证损失一旦验证损失连续若干轮不再下降就停下来。这个策略叫早停是时序预测最实用的防过拟合手段。val_size int(len(scaled_train) * 0.9) val_data scaled_train[val_size:] train_final scaled_train[:val_size] X_train_f, y_train_f create_sequences(train_final, window_size24, pred_len1) X_val, y_val create_sequences(val_data, window_size24, pred_len1) best_val_loss float(inf) patience 10 trigger 0 for epoch in range(epochs): model.train() for xb, yb in train_loader: optimizer.zero_grad() loss criterion(model(xb), yb) loss.backward() optimizer.step() model.eval() with torch.no_grad(): val_pred model(torch.FloatTensor(X_val)) val_loss criterion(val_pred.squeeze(), torch.FloatTensor(y_val).squeeze(-1)).item() if val_loss best_val_loss: best_val_loss val_loss trigger 0 torch.save(model.state_dict(), best_lstm.pth) # 保存最佳模型 else: trigger 1 if trigger patience: print(fearly stop at epoch {epoch1}) break逻辑说明训练数据又被切出一小块当验证集这块数据不参与梯度更新专门用来观察模型是否过拟合。每轮结束把模型切成评估模式关闭梯度计算算出验证集 loss。验证 loss 连续patience轮没有创新低就停止训练并保留历史最佳模型。torch.save保存的是状态字典而不是整个模型加载时先实例化模型再load_state_dict这是 PyTorch 的常规用法。参数说明patience10表示允许验证 loss 连续 10 轮不下降数据量小可以放宽到 15。预留验证集的比例是 10%是从 80% 的训练集里再切最终数据分布是 70% 训练、10% 验证、20% 测试。这个三层划分在期末报告里一定要写清楚答辩时老师很爱问“验证集和测试集分别用来干什么”。5. 避坑排查LSTM 温度预测项目最常见的五个翻车点5.1 训练没几轮 loss 变成 NaN现象训练跑到第 10 轮左右打印出来的 loss 突然变成nan然后一直回不来。原因最常见的是数据没归一化原始温度值动辄几十LSTM 的激活函数在输入过大时梯度饱和反向传播时梯度爆炸。另一个高发原因是学习率设得过大比如直接用 0.01导致权重更新跨步太大loss 直接越界。解决先把MinMaxScaler的fit_transform落实到位确保 X 和 y 都在 0 到 1 区间。如果归一化之后仍然 NaN把 Adam 的lr降到 0.0005并在 backward 之后加一步梯度裁剪torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0)。这一步等于给梯度设了个上限能兜住大多数数值溢出问题。5.2 报错 size mismatch提示 expected 3D input现象把滑窗好的X_train直接塞进模型PyTorch 报错类似RuntimeError: input must have 3 dimensions, got 2。原因LSTM 要求的输入形状是(batch, seq_len, features)而create_sequences返回的 X 在维度搞错时是(样本数, 特征数)。常见误用是滑窗前把temp当成了一维数组滑窗切出的每个样本也是 1D最终堆成二维。另一种情况是在DataLoader里没有确认张量形状传输过程中维度被意外压扁。解决在滑窗函数最前面强制data np.asarray(data, dtypenp.float32)如果data.ndim 1就data data.reshape(-1, 1)。训练前打印一次X_train.shape必须是(样本数, 24, 1)这样的三维结构。调试时可以在模型forward里加一行print(x.shape)看真实输入维度。5.3 测试集预测效果远差于训练集现象训练集 loss 已经降到 0.001 以下预测曲线和真实值贴得很紧但换到测试集上一算误差大得离谱曲线明显偏离。原因八成是数据切分或归一化时泄漏了未来信息。典型错误是先对整个序列的 X、y 做train_test_split随机打乱再喂进模型另一典型错误是对整个序列一次性fit_transform算 max/min再做滑窗测试集范围已经被模型看到了。解决严格执行“先按时间切序列再滑窗再分别在训练集上 fit、在测试集上 transform”的顺序。拿一张纸把时间轴画出来训练段、验证段、测试段必须严格先后排列。如果修改后测试误差还是高再检查滑窗时有没有混入重叠的未来数据。5.4 预测曲线比真实曲线“慢半拍”看起来像复制昨天的温度现象画出来的预测曲线和真实曲线整体趋势一致但整体向右平移了一段预测值像在复读上一时刻的温度。原因这是单步滚动预测的典型现象。模型用过去 24 小时预测未来 1 小时下一时刻温度往往和当前时刻高度相关模型学到的“最优策略”就是输出接近窗口末端值的数因为这样平均误差最小。本质上是损失函数没惩罚滞后而不是代码写错了。解决答辩时一定要主动解释这一点说清楚这是单步预测的常见情况不是 bug。想降低滞后可以把窗口调大给模型更多上下文或者把预测步长从 1 改成 3强制模型跳出“复制最近值”的舒适区再或者把目标从“直接预测温度”改成“预测温度变化量”让模型学增量而不是学绝对值。5.5 训练 loss 很低但预测结果几乎是一条直线现象loss 好看测试集上画出来却是一条接近水平的直线温度和真实值完全对不上。原因模型容量不足或者输入信息不够。hidden_size16、num_layers1时 LSTM 可能只学到“输出训练集均值”这种最保险的策略。另一个常见原因是窗口太小比如窗口只取 3 小时模型看不到昼夜周期自然无法产生有波动的预测。解决先逐步增大hidden_size从 16 升到 64 再升到 128观察 loss 是否继续下降。再看num_layers两层比一层能建模更复杂的时序模式。如果调参后还是平线回到数据侧增加特征把湿度、气压、风速加进输入让输入维度从 1 变成 4LSTM 有更多线索可用。这一步也是答辩时展示“改进能力”的加分点。6. 用 RMSE 和曲线对比图给答辩收尾验证方法再往外走一步训练结束后最重要的事情是证明模型有用而不是把测试集跑一遍就算完。先把模型切到评估模式对测试集做预测再反归一化回真实温度算 RMSE画一张真实值和预测值的对比图。一套动作合起来就是下面这段短代码model.load_state_dict(torch.load(best_lstm.pth)) model.eval() with torch.no_grad(): pred model(torch.FloatTensor(X_test)).squeeze().numpy() pred_real scaler.inverse_transform(pred.reshape(-1, 1)).ravel() real_real scaler.inverse_transform(y_test.reshape(-1, 1)).ravel() rmse np.sqrt(np.mean((pred_real - real_real) ** 2)) print(f测试集 RMSE: {rmse:.2f} °C) import matplotlib.pyplot as plt plt.figure(figsize(10, 4)) plt.plot(real_real, label真实温度) plt.plot(pred_real, labelLSTM 预测) plt.legend() plt.title(Temperature Prediction) plt.savefig(result.png, dpi150)RMSE 单位是摄氏度温度数据上能到 1℃ 以内说明模型很好地学到了昼夜规律1 到 2℃ 属于正常范围超过 5℃ 就回头检查数据切分和归一化。图里如果预测曲线比真实曲线滞后按第 5 章的说法解释即可。再往外走一步的进阶方向是把这个单步模型改造成多步预测把create_sequences里的pred_len改成 6 或 12模型输出维度改成pred_len损失函数按每个预测步长分别计算再取平均。或者干脆加入更多气象特征让输入从单变量变成多变量这是期末报告“总结与展望”里最自然的素材。我做这个项目时最后悔的是把验证集切得太晚导致调参全靠测试集等到答辩才发现“优化”过的参数其实是在测试集上过拟合。把验证集和测试集从一开始就严格分开是这份作业里最值得花时间养成的习惯。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑