ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

LSTM时间序列预测实战:从期末大作业到可复现Python源码

LSTM时间序列预测实战:从期末大作业到可复现Python源码 简介这份资源面向高校学生与Python初学者提供一套可直接运行的LSTM时间序列预测完整项目适用于期末大作业、课程设计或入门深度学习实践。项目以空气质量等真实序列数据为样本覆盖数据读取、预处理、模型搭建、训练与预测全流程代码注释详尽新手也能看懂并快速部署。压缩包共129个文件约5.42MB其中78个py脚本承担模型定义与训练逻辑26个csv与若干txt提供原始数据及说明另含checkpoint、h5权重与TensorFlow数据分片便于直接复现结果。目前已有169人学习下载。整体目录结构清晰读者可据此掌握LSTM做时序预测的完整思路并在此基础上替换数据集完成自己的课题具备较高的参考与复用价值。1. 从一份期末大作业说起LSTM 时间序列预测到底能跑出什么结果如果你手头正拿着一份「基于 LSTM 进行时间序列数据预测」的期末大作业大概率会遇到两个尴尬代码能跑但预测曲线像一条滞后三拍的影子数据能读但换个数据集就报形状不匹配。LSTM 时间序列预测这件事真正卡人的从来不是model.fit()那一行而是窗口怎么切、归一化在哪一步做、反归一化有没有对齐、评估指标是不是在自欺欺人。这篇笔记面向三类人正在赶期末大作业、需要一份能直接复现的 Python 源码结构的学生想把 LSTM 用到设备寿命预测、销量预测、传感器趋势预测上的工程师以及刚配好 PyCharm 或 VS Code Python 环境、想找一个完整项目练手的新手。我会按「数据长什么样 → 窗口怎么造 → 模型怎么搭 → 训练怎么盯 → 坑怎么躲」的顺序把一份可运行的 LSTM 预测方案拆开讲清楚参数给到能改的程度失败时该看哪里也一并说明。读完你应该能自己判断这份源码值不值得改以及怎么改成能写进简历的版本。2. 数据准备与窗口构造LSTM 吃进去的到底是什么2.1 时间序列预测的任务定义与数据格式先把问题说清楚。时间序列预测本质是用过去一段时间的观测值去推断未来一个或多个时间点的值。单变量场景下输入是一列按时间排序的数值比如每天销量、每小时温度、每周期设备振动幅值多变量场景下输入是多列数值外加时间戳对齐。LSTM 不会直接吃原始序列它吃的是「样本」。一个样本由两部分组成一段长度为look_back的历史窗口和一个长度为horizon的预测目标。假设原始序列是[10, 12, 11, 13, 14, 15]look_back3、horizon1那么第一个样本是输入[10,12,11]、标签13第二个样本是输入[12,11,13]、标签14。这一步叫滑动窗口构造是 LSTM 时间序列预测里最容易写错、也最容易被忽略的地方。常见的数据格式有三种CSV 单列时间序列、CSV 多列带时间戳、以及数据库导出的宽表。期末大作业里最常见的是第一种只有一列数值没有时间列。这种数据反而好处理因为不需要考虑时间间隔不规律的问题。但如果你拿到的是带时间戳的数据第一件事是检查时间列是否等间隔不等间隔要先重采样。提示不要一上来就pd.read_csv然后直接丢进模型。先df.head()、df.info()、df.describe()三连确认缺失值、异常值、量纲范围。LSTM 对量纲敏感没归一化的数据会让训练直接翻车。2.2 用 Pandas 和 NumPy 构造滑动窗口下面这段代码是我一般会用的窗口构造方式单变量多变量都能兼容。核心思路是先把数据归一化到[0,1]再切窗口最后 reshape 成 LSTM 需要的三维张量(样本数, 时间步, 特征数)。import numpy as np import pandas as pd from sklearn.preprocessing import MinMaxScaler # 读取数据假设只有一列数值列名为 value df pd.read_csv(data.csv) values df[value].values.reshape(-1, 1) # 归一化只在训练集上 fit避免数据泄漏 scaler MinMaxScaler(feature_range(0, 1)) scaled scaler.fit_transform(values) def create_dataset(data, look_back24, horizon1): data: 归一化后的二维数组 (n, 1) look_back: 历史窗口长度 horizon: 预测未来第几个点 返回: X (样本数, look_back, 1), y (样本数,) X, y [], [] for i in range(len(data) - look_back - horizon 1): X.append(data[i:i look_back, 0]) y.append(data[i look_back horizon - 1, 0]) X np.array(X) y np.array(y) # reshape 成 LSTM 输入格式 X X.reshape(X.shape[0], X.shape[1], 1) return X, y look_back 24 # 用过去 24 个点预测 horizon 1 # 预测未来第 1 个点 X, y create_dataset(scaled, look_back, horizon) print(X.shape, y.shape) # 例如 (1000, 24, 1) (1000,)这段代码有三个关键参数。look_back决定模型能看到多长的历史太小会欠拟合太大会引入噪声且训练变慢horizon决定预测步长设为 1 是单步预测设为 7 就是预测未来第 7 个点feature_range默认(0,1)如果数据里有明显异常值可以改成(-1,1)配合tanh激活。逻辑说明create_dataset里循环的终止条件是len(data) - look_back - horizon 1这个1很容易漏漏了会少一个样本。X.reshape那一步是必须的因为 Keras/TensorFlow 的 LSTM 层要求输入是三维的PyTorch 的nn.LSTM默认也要求(batch, seq_len, input_size)除非你设了batch_firstFalse。参数说明look_back的经验值是取一个完整周期比如日数据取 7 或 30小时数据取 24 或 168。horizon根据业务定期末大作业一般取 1 就够了。归一化器一定要保存预测完要用它反归一化否则你得到的是一堆 0 到 1 之间的小数没法解释。2.3 训练集、验证集、测试集的切分顺序时间序列不能随机打乱切分这是血泪经验。随机切分会让未来信息泄漏到训练集模型在测试集上表现虚高实际部署就崩。正确做法是按时间顺序切前 70% 训练中间 15% 验证最后 15% 测试。如果数据量小可以 80/20 两段切。n len(X) train_end int(n * 0.7) val_end int(n * 0.85) X_train, y_train X[:train_end], y[:train_end] X_val, y_val X[train_end:val_end], y[train_end:val_end] X_test, y_test X[val_end:], y[val_end:] print(X_train.shape, X_val.shape, X_test.shape)切完之后检查一下三个集合的均值和方差如果差异很大说明数据分布不稳定可能需要做差分或者滚动归一化。这一步很多人跳过结果训练 loss 降得很漂亮测试集一塌糊涂回头找原因找半天。3. 模型搭建与训练Keras 和 PyTorch 两条路怎么选3.1 用 Keras 搭一个最小可用的 LSTM 预测模型Keras 适合期末大作业和快速验证代码短报错友好。下面是一个两层 LSTM 加全连接输出的结构输入形状是(look_back, 1)。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.optimizers import Adam model Sequential() # 第一层 LSTMreturn_sequencesTrue 才能接第二层 LSTM model.add(LSTM(64, return_sequencesTrue, input_shape(look_back, 1))) model.add(Dropout(0.2)) # 第二层 LSTM只返回最后一个时间步的输出 model.add(LSTM(32, return_sequencesFalse)) model.add(Dropout(0.2)) # 输出层单值预测 model.add(Dense(1)) model.compile(optimizerAdam(learning_rate0.001), lossmse, metrics[mae]) model.summary() history model.fit( X_train, y_train, epochs50, batch_size32, validation_data(X_val, y_val), verbose1 )参数说明第一层LSTM(64)的 64 是隐藏单元数数据量大可以加到 128数据量小就降到 32。return_sequencesTrue是接第二层 LSTM 的必要条件忘了会报形状错误。Dropout(0.2)是防过拟合的如果训练 loss 和验证 loss 差距大可以加到 0.3 或 0.4。learning_rate0.001是 Adam 的常用起点loss 震荡就降到 0.0005收敛太慢就升到 0.002。逻辑说明model.fit返回的history里存了每轮的 loss 和 mae后面画学习曲线要用。batch_size32是默认值数据量小于 1000 可以改成 16 或 8梯度更新更频繁收敛更稳。epochs50不是越多越好要看验证 loss 什么时候不再下降配合EarlyStopping更省事。3.2 PyTorch 版本的 LSTM 模型与训练循环如果你用 PyTorch结构要自己写但控制力更强。下面是一个等价的单层 LSTM 模型。import torch import torch.nn as nn from torch.utils.data import DataLoader, TensorDataset class LSTMPredictor(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 self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue) self.fc nn.Linear(hidden_size, output_size) def forward(self, x): # x: (batch, seq_len, input_size) h0 torch.zeros(self.num_layers, x.size(0), self.hidden_size) c0 torch.zeros(self.num_layers, x.size(0), self.hidden_size) out, _ self.lstm(x, (h0, c0)) # 取最后一个时间步 out self.fc(out[:, -1, :]) return out # 转成 Tensor X_train_t torch.tensor(X_train, dtypetorch.float32) y_train_t torch.tensor(y_train, dtypetorch.float32).unsqueeze(1) train_loader DataLoader(TensorDataset(X_train_t, y_train_t), batch_size32, shuffleFalse) model LSTMPredictor() criterion nn.MSELoss() optimizer torch.optim.Adam(model.parameters(), lr0.001) 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() print(fEpoch {epoch1}, Loss: {total_loss/len(train_loader):.6f})参数说明hidden_size64和 Keras 版对应num_layers2表示两层 LSTM。batch_firstTrue让输入格式变成(batch, seq_len, feature)和 Keras 一致不设的话要自己转置。shuffleFalse在时间序列训练里通常要设 False因为 DataLoader 的 shuffle 会打乱样本顺序虽然对 LSTM 的梯度传播影响不大但保持时间顺序更符合直觉。逻辑说明h0和c0是 LSTM 的初始隐藏状态和细胞状态全零初始化是常见做法。out[:, -1, :]取最后一个时间步的输出因为我们要用整个窗口的信息预测下一个点。训练循环里optimizer.zero_grad()不能漏漏了梯度会累加loss 直接爆炸。3.3 训练过程要盯哪几个信号训练不是设完参数就等结果。我一般会盯三个信号训练 loss 和验证 loss 的差距、loss 下降的平滑度、以及验证 loss 的最低点出现在第几轮。如果训练 loss 持续下降但验证 loss 先降后升说明过拟合加 Dropout 或减层数。如果两个 loss 都震荡说明学习率太大或 batch_size 太小。如果两个 loss 都不降先检查数据归一化和窗口构造再检查模型输入形状。import matplotlib.pyplot as plt plt.plot(history.history[loss], labeltrain_loss) plt.plot(history.history[val_loss], labelval_loss) plt.xlabel(Epoch) plt.ylabel(MSE Loss) plt.legend() plt.show()这张图是判断模型状态最直接的工具。验证 loss 最低点对应的轮数就是你应该停下来的轮数。Keras 里可以用EarlyStopping(patience10)自动停PyTorch 里要自己写判断逻辑。4. 预测、反归一化与评估别让曲线骗了你4.1 反归一化与预测结果对齐模型输出的是归一化后的值必须用同一个 scaler 反变换回原始量纲。这一步看起来简单但对齐容易错。# 预测 y_pred_scaled model.predict(X_test) # 反归一化 y_pred scaler.inverse_transform(y_pred_scaled) y_true scaler.inverse_transform(y_test.reshape(-1, 1)) # 对齐时间轴 test_start look_back val_end time_index df.index[test_start:test_start len(y_pred)] result pd.DataFrame({ true: y_true.flatten(), pred: y_pred.flatten() }, indextime_index) print(result.head())逻辑说明scaler.inverse_transform要求输入是二维数组所以y_test要先reshape(-1,1)。test_start的计算要跟窗口构造时的偏移对齐否则时间轴会错位。这个错位在图上表现为预测曲线整体平移很多人以为是模型滞后其实是索引算错了。参数说明如果归一化时用的是多列数据inverse_transform要传入相同列数的数组单变量预测时只传预测列即可。如果训练时用了差分反变换还要把差分加回去。4.2 评估指标MAE、RMSE、MAPE 各自说明什么单看一条曲线不够要有数值指标。常用的三个MAE 反映平均绝对误差RMSE 对大误差更敏感MAPE 是百分比误差适合跨量纲比较。from sklearn.metrics import mean_absolute_error, mean_squared_error mae mean_absolute_error(y_true, y_pred) rmse np.sqrt(mean_squared_error(y_true, y_pred)) mape np.mean(np.abs((y_true - y_pred) / y_true)) * 100 print(fMAE: {mae:.4f}) print(fRMSE: {rmse:.4f}) print(fMAPE: {mape:.2f}%)参数说明MAPE 在y_true接近 0 时会爆炸如果数据里有零值改用 SMAPE 或直接看 MAE。RMSE 和 MAE 的比值能反映误差分布比值接近 1 说明误差均匀比值大说明存在个别大误差点。注意不要只报一个指标。期末大作业里常见只写 MSE但 MSE 的量纲是原始值的平方不好解释。至少报 MAE 和 MAPE答辩时能说清楚。4.3 画一张能放进报告的预测对比图图要能看出三件事真实值的趋势、预测值的跟随程度、误差最大的区间。plt.figure(figsize(12, 5)) plt.plot(result.index, result[true], labelTrue, linewidth1.5) plt.plot(result.index, result[pred], labelPred, linewidth1.5, linestyle--) plt.fill_between(result.index, result[true], result[pred], alpha0.2, colorgray) plt.xlabel(Time) plt.ylabel(Value) plt.legend() plt.title(LSTM Prediction vs True) plt.tight_layout() plt.show()fill_between把误差区域填灰一眼能看出哪里预测得差。如果误差集中在峰值附近说明模型对突变捕捉不够可以加特征或改用注意力机制。如果误差整体平移检查反归一化和索引对齐。5. 避坑与排查LSTM 时间序列预测最常见的 5 个翻车现场5.1 现象训练 loss 正常但预测是一条直线原因模型学到了均值没有学到序列的动态。常见于look_back太小、归一化范围不对、或者输出层激活函数用错。解决把look_back增大到一个完整周期以上检查归一化是不是在训练集上 fit 的输出层不要加sigmoid或tanh回归任务用线性输出。5.2 现象验证 loss 比训练 loss 低很多原因验证集太小或分布和训练集差异大也可能是 Dropout 在验证时没关闭。解决检查切分比例验证集至少占总样本 10%Keras 的 Dropout 在evaluate和predict时自动关闭PyTorch 要手动model.eval()。5.3 现象预测结果整体滞后一个窗口原因窗口构造时标签偏移算错或者反归一化时索引没对齐。解决重新检查create_dataset里的i look_back horizon - 1确认标签取的是窗口之后第horizon个点检查test_start的计算是否和窗口偏移一致。5.4 现象换数据集后报形状错误原因新数据集的列数或时间步长和旧的不一致input_shape没改。解决把input_shape(look_back, n_features)里的n_features改成实际特征数多变量时确认create_dataset返回的X是三维的。5.5 现象GPU 显存不够或训练速度极慢原因batch_size太大、look_back太长、或者模型层数太多。解决先把batch_size降到 16 或 8look_back超过 200 时考虑用卷积层先降维层数从 2 层开始加不要一上来就堆 4 层。6. 把这份源码改成能写进简历的项目三个进阶技巧6.1 用多变量输入提升预测精度单变量预测只看历史值多变量可以加入时间特征、周期特征、外部变量。比如销量预测可以加入星期几、是否节假日、促销标记。做法是把这些列一起归一化然后create_dataset返回的X形状变成(样本数, look_back, 特征数)。# 假设 df 有多列value, dayofweek, is_holiday features df[[value, dayofweek, is_holiday]].values scaler MinMaxScaler() scaled scaler.fit_transform(features) def create_multivariate_dataset(data, look_back24, horizon1, target_col0): X, y [], [] for i in range(len(data) - look_back - horizon 1): X.append(data[i:i look_back, :]) y.append(data[i look_back horizon - 1, target_col]) return np.array(X), np.array(y) X, y create_multivariate_dataset(scaled, look_back24, horizon1, target_col0) print(X.shape) # (样本数, 24, 3)target_col0表示预测第一列其他列作为辅助特征。模型结构不用大改只把input_shape改成(look_back, 3)。6.2 用早停和模型保存避免白跑训练轮数设大一点配合早停验证 loss 不降就停同时保存最佳模型。from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint callbacks [ EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue), ModelCheckpoint(best_lstm.h5, monitorval_loss, save_best_onlyTrue) ] history model.fit( X_train, y_train, epochs200, batch_size32, validation_data(X_val, y_val), callbackscallbacks, verbose1 )patience10表示验证 loss 连续 10 轮不降就停restore_best_weightsTrue把权重恢复到最佳轮次。ModelCheckpoint保存最佳模型后面可以直接加载做预测不用重新训练。6.3 一个我常用的验证习惯每次改完窗口长度或模型结构我不会直接跑全量数据。先取前 500 个点跑一遍看 loss 曲线和预测图确认没有形状错误和明显滞后再上全量。这个习惯帮我省了很多次白等一小时的训练。另外我会把每次实验的look_back、hidden_size、learning_rate、MAE记在一个表格里跑过五六组之后就能看出哪组参数最稳。这个表格也是期末大作业报告里「参数调优」部分最好的素材。希望帮到你。本文还有配套的精品资源点击获取
返回列表