简介:时间序列预测是数据科学与机器学习中常见的回归任务,本资源以长短期记忆神经网络为核心,提供了一套完整的可运行代码,适合期末大作业、课程设计以及希望快速完成预测项目的学生参考。压缩包共三十一个文件,包含三个数据文件、三个代码脚本、七个配置信息、十四张演示截图以及说明文档,整体大小约二十八点四八兆字节,目录划分为数据、代码、配置和文档四个部分。目前已有1026人学习下载,说明该资源在同类作业中具有较高的参考价值。代码从数据切分、归一化处理到模型构建、训练与评估均有清晰注释,附带的训练结果截图和说明文档方便对照验收;同时包含模型状态文件,支持加载已有权重继续训练或直接预测,节省大量调参时间。因此这份资源兼具完整性与实操性,稍加调整即可用于其他序列数据。
1. 期末大作业里的 LSTM 时间序列预测源码,评分到底看什么
期末大作业选题里,「时间序列预测(LSTM模型)python代码实现源码」是出现频率最高的组合之一。直线趋势用线性回归,周期性强用 ARIMA,为什么轮到 LSTM 神经网络?因为它能把过去若干时间步的信息压缩进隐状态,再输出下一个值,对非线性、带漂移的序列更稳。这类作业的评分点其实很固定:数据预处理规不规范、模型代码能不能跑通、预测曲线和真实曲线的对比图可不可信、答辩时能不能解释清楚参数。看懂这篇笔记,你就能从源码里抽出主线,自己动手复现一遍,而不是对着报错干瞪眼。
2. 数据预处理:从一列原始数值到能喂进 LSTM 的样本集
不管压缩包里那份代码排版多乱,能过评审的版本都绕不开三件事:把原始序列切成 LSTM 能吃的样本、把数值压到合适的范围、把训练和测试严格按时间切开。这三件事做完,后面的模型代码只是把同一个套路重复三遍。
2.1 滑动窗口构造样本:look_back 是整份代码的灵魂参数
常见做法是用一个长度为 look_back 的窗口,把前 look_back 个时刻的值作为特征 X,第 look_back+1 个时刻的值作为标签 y,然后整体向后滑动。这个窗口长度决定了模型「能看到多远的过去」,直接左右预测曲线的平滑程度和滞后程度。这也是源码里最值得你反复改的一个数,改一次跑一遍,比改任何网络结构都直观。
import numpy as np import pandas as pd from sklearn.preprocessing import MinMaxScaler # 读入一列原始序列,比如某商品的日销量 df = pd.read_csv("sales_data.csv", parse_dates=["date"]) series = df["value"].values.reshape(-1, 1) # 归一化:只对训练段 fit,防止信息泄漏 split = int(len(series) * 0.8) scaler = MinMaxScaler(feature_range=(0, 1)) scaler.fit(series[:split]) scaled = scaler.transform(series) def make_samples(data, look_back=10): X, y = [], [] for i in range(look_back, len(data)): X.append(data[i - look_back:i, 0]) y.append(data[i, 0]) return np.array(X), np.array(y) X, y = make_samples(scaled, look_back=10) # 切分时保持时间顺序,绝不随机打乱 X_train, y_train = X[:split - look_back], y[:split - look_back] X_test, y_test = X[split - look_back:], y[split - look_back:] # LSTM 输入必须是 (样本数, time_steps, features) X_train = X_train.reshape(X_train.shape[0], X_train.shape[1], 1) X_test = X_test.reshape(X_test.shape[0], X_test.shape[1], 1) print(X_train.shape, X_test.shape) # 例:(790, 10, 1) (190, 10, 1)逻辑说明:循环里第 i 个样本取data[i - look_back:i]这段连续历史作为输入,data[i]作为要预测的目标。切片完成后统一 reshape 成三维,最后一维是 1,代表单特征;以后要加星期几、天气这类外生变量,把这一维从 1 改成特征总数即可。这个函数只依赖 numpy,放到哪个环境都能直接跑。
参数说明:split = 0.8是时间序列训练测试比的常见默认值;不要用图像分类那套随机切分或 K 折,相邻样本高度相关,随机一抽就把未来混进训练集。look_back初始值按数据周期取,日销量按周波动就设 7 或 14,金融日线常设 20~30,课程演示数据里 10~30 足够显效果。窗口越大,预测越平缓,但样本总数会少掉 look_back 个,注意别为追求大窗口把训练数据砍没了。
2.2 归一化:为什么默认选 MinMaxScaler 而不是 StandardScaler
LSTM 内部激活函数是 tanh 和 sigmoid,输入数值范围太大或太小,梯度计算都会不稳定。MinMaxScaler 把数据压到 0~1,正好匹配激活函数输出区间;StandardScaler 适合特征分布近似高斯的场景,做单序列预测时选 MinMax 更省心。归一化的关键不在选哪个,而在 fit 的时机:只能 fit 训练段,再用同一套参数 transform 测试段。很多翻车现场就是先对全序列 fit 再切分,测试集的统计信息提前「偷看」了训练数据。
这一步没有太多玄学,但有一个高频误用:写成先scaler.fit(scaled_all)再切分。测试段的最大最小值已经参与归一化参数估计,训练出来的模型在测试集上被高估。平时自测看不出问题,答辩时老师问一句「归一化参数来自哪段数据」,一下就能戳穿。正确顺序永远是:切分 → fit 训练段 → transform 全量。
2.3 时间序列的切片顺序:shuffle 在这里是作弊
图像分类里 shuffle 是标准动作,但时间序列预测里样本之间存在时间依赖,随机打乱等于把未来信息倒灌进训练集,指标虚高得离谱。训练时想增加随机性,只对每个 batch 内部做轻微扰动,不要动样本的全局顺序。测试集必须严格接在训练集之后,保持时间连续性,模型看到的最后一段历史和评测的第一段历史在时间上无缝衔接。
提示:拿到压缩包先搜索
shuffle。如果发现训练环节里有shuffle=True,建议直接删掉,把验证方式改成按时间顺序切分。这是评审老师最容易挑出来的硬伤,也是数据部分最值得单独写一节讲的内容。
3. 搭出能跑的 LSTM 模型:Keras 代码逐行拆解与参数选型
3.1 网络结构:LSTM 层 + Dropout + 全连接层的最短配置
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping look_back = 10 model = Sequential([ LSTM(units=64, input_shape=(look_back, 1), return_sequences=True), Dropout(0.2), LSTM(units=32, return_sequences=False), Dropout(0.2), Dense(1) ]) model.compile(optimizer="adam", loss="mse", metrics=["mae"]) model.summary()逻辑说明:第一层 LSTM 的return_sequences=True表示把每个时间步的隐状态都传给下一层;第二层设为 False,只保留最后一个时间步的输出,再接 Dense 层压成单一预测值。Dense(1)是回归任务的标配,激活函数默认 linear,千万别在这里套 sigmoid——那是分类的思维,套上会让输出被钳制在 0~1。Dropout 放在每层 LSTM 之后,训练时随机丢弃部分神经元,抑制过拟合;预测阶段它自动关闭,不用担心。
参数说明:input_shape=(look_back, 1)第二个数字对应特征数,和上一章预处理 reshape 的维度一致。units控制隐状态维度,手动网格搜索的合理区间是 16~128。层数方面:一层 LSTM 在大多数期末数据集上已经够用,两层能学更复杂的依赖但训练明显变慢;三层以上对小数据量基本是过拟合重灾区,别为了「显得高级」硬叠。优化器默认 adam、学习率 0.001,多数场景不用动;用 SGD 还得自己调学习率和动量,不建议在一份大作业里折腾自己。
3.2 三个必调参数:hidden_size、num_layers、dropout 的合理取值
这里按踩坑经验给一张参数速查表,优先级从上到下:
| 参数 | 常见取值 | 调参依据 | 调偏的后果 |
|---|---|---|---|
| look_back | 10~30 | 数据周期长度的 1~2 倍 | 太小:预测滞后明显;太大:样本数骤减、训练慢 |
| units | 32~128 | 样本量千级用 32/64,万级再上 128 | 太小:欠拟合;太大:小数据直接过拟合 |
| num_layers | 1~2 | 先 1 层跑通再考虑 2 层 | 3 层以上小数据几乎必过拟合 |
| dropout | 0.1~0.3 | 训练 loss 低但验证 loss 高时加大 | 太大:欠拟合,loss 居高不下 |
| batch_size | 16~64 | 样本不足 1000 时用 32 以下 | 太大:收敛慢;太小:训练震荡 |
| epochs | 30~100 | 配 EarlyStopping 看验证 loss 早停 | 太少:没收敛;太多:过拟合 |
参数之间是联动的:units 翻倍,dropout 通常要适当抬一点;look_back 加大,样本数变少,batch_size 就调小。别指望一套参数打天下,同一份代码换个数据集,units 可以不变,dropout 往往要重调。答辩被问「这些数怎么来的」,照实说「先按经验表初始化,再看 val_loss 曲线调」就是完整答案。
3.3 训练循环与 EarlyStopping:跑得慢比跑得差更要命
early_stop = EarlyStopping( monitor="val_loss", patience=10, restore_best_weights=True ) history = model.fit( X_train, y_train, validation_data=(X_test, y_test), epochs=80, batch_size=32, callbacks=[early_stop], verbose=1 )逻辑说明:EarlyStopping监控验证集 loss,连续 patience 个 epoch 不下降就停,并恢复验证 loss 最低那次的权重。restore_best_weights=True是后悔药,防止最后几轮已经过拟合还保留坏权重;设为 False 的话,停的时候拿到的往往是过拟合状态。训练结束后history.history里存着每个 epoch 的 loss 和 val_loss,把两条曲线画出来贴到报告里,是答辩时的加分材料。
参数说明:patience=10意味着容忍 10 个 epoch 的波动,噪声大的序列可以放宽到 15;monitor必须盯验证集指标,别盯训练 loss——那只会告诉你模型把训练数据背得有多熟。如果你拿到的压缩包里是手写训练循环,没有 callbacks,就在循环里记录 val_loss,每轮结束比较一下,手动保留最优权重,效果一样。
3.4 如果压缩包里是 PyTorch:结构对照与最小改动
不少课程现在要求 PyTorch,压缩包里可能同时给 keras 和 pytorch 两个版本。PyTorch 的核心代码区别只在网络定义和训练循环,数据预处理部分完全复用第二章。
import torch.nn as nn class LSTMPredictor(nn.Module): def __init__(self, input_size=1, hidden_size=64, num_layers=2, dropout=0.2): super().__init__() self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True, dropout=dropout) self.fc = nn.Linear(hidden_size, 1) def forward(self, x): out, _ = self.lstm(x) return self.fc(out[:, -1, :])逻辑说明:batch_first=True让输入形状与 Keras 保持一致,直接喂 (样本数, look_back, 特征数)。out[:, -1, :]取最后一个时间步的隐状态,等价于 Keras 里第二层 LSTM 的return_sequences=False。num_layers对应层数,dropout只在层数大于 1 时生效。两个框架写出来的模型在相同参数下预测结果非常接近,期末作业只需要二选一,不用两套都维护。
4. 评估与画图:LSTM 预测结果要拿得出的三张图和三个数
跑完模型只是完成一半,期末大作业里评估与可视化通常占三成以上的分值。评委没耐心看你几十个 epoch 的训练日志,他们要的是三张图和三个数:训练集拟合曲线、测试集对比曲线、残差图,以及 RMSE、MAE、R²。
4.1 评价指标:RMSE、MAE、R² 分别说明什么
训练 loss 只能证明模型收敛,交作业要有「预测准不准」的数字证据。RMSE 对大误差更敏感,因为差值先平方再求和,个别离谱预测立刻被放大;MAE 更直观,量纲和原始数据一致。R² 接近 1 说明模型解释了大部分方差,但时间序列带强趋势时 R² 容易虚高,别只报这一个。
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score train_pred = model.predict(X_train).reshape(-1, 1) test_pred = model.predict(X_test).reshape(-1, 1) # 反归一化还原回原始量纲 train_pred_raw = scaler.inverse_transform(train_pred) test_pred_raw = scaler.inverse_transform(test_pred) y_train_raw = scaler.inverse_transform(y_train.reshape(-1, 1)) y_test_raw = scaler.inverse_transform(y_test.reshape(-1, 1)) rmse = np.sqrt(mean_squared_error(y_test_raw, test_pred_raw)) mae = mean_absolute_error(y_test_raw, test_pred_raw) r2 = r2_score(y_test_raw, test_pred_raw) print(f"RMSE={rmse:.4f}, MAE={mae:.4f}, R2={r2:.4f}")逻辑说明:预测值必须经过inverse_transform还原成原始量纲再算指标,否则 RMSE 小到小数点后七八位,看着漂亮但没法跟业务数据对照。还原 y 时要把y_test重新 reshape 成列向量,画图和算指标都要求形状一致。打印的这三个数就是答辩 PPT 上的核心数字,建议顺带算一个朴素基准的 RMSE——比如「预测值 = 昨天真实值」,LSTM 必须比这个基准好才算有效,这是检验模型有没有白训的最快方法。
4.2 画预测对比图:横坐标日期太密集怎么处理
import matplotlib.pyplot as plt plt.figure(figsize=(12, 4)) plt.plot(y_test_raw, label="真实值") plt.plot(test_pred_raw, label="预测值") plt.legend() plt.xticks(rotation=45) plt.tight_layout() plt.savefig("result.png", dpi=150)逻辑说明:两条曲线叠在一起,滞后和偏差一眼就能看出来。rotation=45处理的就是横坐标日期太密集导致标签叠成黑块的问题;如果测试集超过 500 个点,只画最后 100 个点,否则曲线挤成一条粗线。dpi=150保证图片插进 Word 后放大不糊。建议再补两张图:一张训练集上的拟合曲线,证明模型没有只靠测试集「事后找补」;一张残差图,真实值减预测值围绕 0 波动,说明没有系统性偏差,比单看 loss 曲线有说服力得多。
4.3 多步预测:递归滚动输出与前向多步输出怎么选
期末大作业常要求预测未来 N 天,比如预测下个月 30 天的销量。最简单的做法是递归预测:把上一步预测值拼回输入窗口,再预测下一步,循环 N 次。代价是误差累积,第一步偏一点,后面越滚越偏,这是多步预测翻车的主因。另一个做法是改模型输出头,把Dense(1)换成Dense(N),一次输出未来 N 个值,让模型同时学这些目标之间的关系,速度更快但实现复杂一点。
# 递归预测 30 步 def recursive_predict(model, last_window, n_steps, scaler): window = last_window.reshape(1, -1, 1) preds = [] for _ in range(n_steps): next_val = model.predict(window, verbose=0)[0, 0] preds.append(next_val) # 窗口左移一格,把刚预测的值拼到末尾 window = np.concatenate( [window[0, 1:, :], [[next_val]]], axis=0 ).reshape(1, -1, 1) return scaler.inverse_transform(np.array(preds).reshape(-1, 1))逻辑说明:循环里每次只预测一个点,然后用拼接方式滚动更新窗口。np.concatenate做的事就是丢掉窗口里最旧的值、把新预测值补进来,窗口长度始终是 look_back。递归预测的优点是模型不用改,缺点是误差累积;预测步数越多,越要降低对绝对数值的期待,看趋势比看具体数字靠谱。交报告时分别报第 1、5、10 步的 RMSE,让误差增长曲线显性化,说明你理解误差累积问题,比塞一张漂亮的 30 步预测图更经得起追问。
5. 避坑手册:五个让 LSTM 期末作业翻车的典型问题
下面五条来自课程答疑和作业批改里反复出现的问题,按出现频率排序。每条都是「现象 → 原因 → 解决」的结构,基本覆盖了这套代码 90% 的报错现场。
5.1 预测曲线比真实曲线滞后一条线
现象:画出来的预测曲线形状跟真实曲线几乎一样,但整体右移了一格,像延迟的回声。原因:look_back 太小,模型学到的最优策略是「复制上一个值」。强自相关的平滑序列里,前一刻的真实值本来就是下一刻最好的预测,于是模型走了捷径。解决:把 look_back 放大到数据周期的 1.5~2 倍;或者先做一阶差分消掉自相关再预测,最后用累计求和还原;再不行就改成前向多步输出,强制模型看更长的上下文。验证方法:算一下预测值与「真实值前移一格」的相关系数,如果高得离谱,基本就是滞后问题。
5.2 loss 不降反升,甚至直接变 NaN
现象:训练到第 2、3 个 epoch,loss 变成 NaN,或者一直在高位震荡不下降。原因按顺序排查:输入数据里有 NaN 或 inf;归一化没做,原始数值在几十万级别直接喂进去;学习率太大;标签 y 的形状写错,模型一直在拟合噪声。解决:先跑np.isnan(series).sum()检查数据,再确认归一化只 fit 训练段,最后把学习率从 0.001 降到 0.0001 试一次。loss 长期不降的隐蔽原因是 y 维度没对齐,比如预测多步时标签少了一列,模型拿一个空数组当目标,怎么训都不会好。
5.3 测试集指标虚高:训练和测试样本重叠
现象:验证集 RMSE 低得离谱,R² 接近 0.99,但换一段真实未来数据一测就崩。原因:切分边界算错了。常见 bug 是先用整个序列构造样本,再按样本数组的长度切分,而样本数组比原始序列少了 look_back 个元素,split 下标一错位,训练样本里就混进了横跨 split 时刻的窗口,等价于偷看了未来。解决:先切分原始序列、再分别构造样本,或者切分后严格检查边界——X_test[0]的最后一个时刻,应该正好是X_train[-1]最后一个时刻加 1。差一格就是泄漏,差对了就是干净边界。
5.4 环境混乱:keras 与 tensorflow 版本冲突、numpy 报错
现象:from keras和from tensorflow.keras混用,模型能定义但 fit 时报AttributeError;或者装好新环境后np.float报错,因为 numpy 1.24 之后移除了这个别名。原因:机器上同时存在两套 keras,调用时路由混乱;课程镜像装的是老版本,自己重装后新老混在一起。解决:统一从tensorflow.keras导入,卸载独立的 keras 包;numpy 固定在 1.23.x,别追新;GPU 版本跑不起来就先切 CPU,这个数据量用 CPU 跑几十个 epoch 也就几分钟。环境问题占作业失败原因的一半,别在报错堆里硬扛,直接新建虚拟环境重来更省时间。
5.5 多步预测越滚越偏,远端误差爆炸
现象:递归预测第 1 步误差 0.5,到第 20 步误差翻倍,曲线发散或逐渐变成一条水平线。原因:递归预测把模型自己上一轮的输出当输入,误差逐级累积;模型在预测时看不到真实值,滚动几步后输入已经被污染。解决:把递归步数控制在训练时见过的范围内;优先用前向多步输出;如果数据集支持,每预测 5 步用真实值校准一次再继续。答辩时主动说「远端误差大是递归预测的固有缺陷」,比被老师问住以后支支吾吾好得多。
6. 期末大作业加分项:从「能跑」到「能接住老师追问」
代码能跑只是及格线,想拿高分,建议在这个方向上加四个东西。
第一,加外生特征。把星期几、是否节假日、上一天销量拼进特征维度,输入从 (look_back, 1) 变 (look_back, 3)。很多销售数据里周末效应非常明显,纯序列模型学不到日历信息,加一列df["date"].dt.weekday往往能让 RMSE 掉一截。量化交易场景同理,可以拼交易量、涨跌幅,这就是热门的 LSTM + 特征工程路线。
第二,做差分平稳化。对明显带趋势的非平稳序列,先diff(1)再预测,效果通常比直接硬训稳定得多,模型也不用浪费容量去拟合一条上升趋势线。预测结果记得用np.cumsum还原回去,报告里写清楚「原始序列 → 差分 → LSTM → 还原」这一串流程,显得你真的理解数据生成过程。
第三,配一个朴素基准。在报告里加一行「基准模型:预测值等于昨天真实值」,然后展示 LSTM 的 RMSE 比它低多少。这能直接回答「你凭什么说 LSTM 有效」,比贴十张训练曲线都管用。如果 LSTM 打不过这个基准,先别急着调参,大概率是预处理或窗口设置有问题。
第四,把五个答辩问题提前写在报告备注里:为什么用 LSTM 不用普通 RNN 或 GRU;look_back 怎么定出来的;模型怎么防止过拟合;训练集和测试集怎么切;多步预测误差为什么变大。这五个问题几乎每次答辩必问,提前把答案写清楚,现场就不慌。
我自己做这类作业的习惯是:先跑通最小版本,再动 look_back 和 dropout,每次只改一个参数并记录 RMSE 变化,最后把对比结果放一页 PPT。参数调整过程本身就是最好的实验报告素材。希望这些经验能帮到你,祝这份期末大作业一次过,答辩顺利。
本文还有配套的精品资源,点击获取