news 2026/10/1 4:08:52

LSTM蔬菜价格预测毕设项目:原理、源码与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LSTM蔬菜价格预测毕设项目:原理、源码与避坑指南

简介:基于深度学习LSTM的蔬菜价格预测完整毕业设计项目,包含Python源码、项目说明与配套数据集。项目针对蔬菜价格时间序列数据,利用长短期记忆网络建模预测,适合计算机相关专业正在准备毕设的学生,以及需要通过真实项目完成课程设计或期末大作业的实战学习者。压缩包内共181个文件,约1.86MB;其中142个csv为本地菜心、西红柿、青皮冬瓜等常见蔬菜的价格历史数据,25个py为模型构建、训练与预测的Python源码,10个pyc为编译缓存,3个docx为项目背景与使用说明文档,1个md为简要说明,整体结构清晰。项目在站内已有271人学习,且为导师指导并认可的高分毕业设计,评审分98分。通过该项目可掌握LSTM时序预测的完整流程,包括数据清洗与划分、模型搭建、训练调参及结果评估,并可直接对照源码与文档进行二次开发,适合作为毕业设计或课程实践参考。

1. 蔬菜价格预测,为什么值得用 LSTM 做毕业设计

黄瓜三天前还卖两块五一斤,一场雨后直接翻到五块;菠菜上周还在一块钱档,这周就冲上三块。蔬菜价格天然带着滞后和波动,线性回归和传统 ARIMA 见到这类序列往往直接投降,不是算不准,而是它连“前天的高价会在今天产生什么影响”这种非线性关系都表达不出来。基于深度学习 LSTM 做蔬菜价格预测,正是把这类时间序列问题交给循环网络去学:模型不只看当天的价格,还会在内部记住一段历史,再从历史里找规律。这个标题里的 python 源码、项目说明和数据集,打包的是一个可以从数据处理一路做到预测画图的完整毕设项目。适合正在做机器学习或数据科学方向选题、想避开“烂大街图像分类”的同学,也适合想快速把 LSTM 用于真实业务数据练手的从业者。

2. 读懂这套基于 LSTM 的蔬菜价格预测项目:原理与代码结构

2.1 LSTM 为什么能记住“上周的菜价”,普通神经网络记不住

在做时间序列预测时,最简单的做法是“滑窗”:把过去七天的价格当作七个特征,丢给全连接网络,让它预测第八天。这种做法本身没有错,错在窗口长度固定后,模型永远只能用最近这七天,再早的影响就丢了。比如端午节前十天蒜薹开始涨价,只给七天窗口,模型根本看不到节前备货的信号。

LSTM 属于循环神经网络家族,它允许信息沿着时间步一路向后传递。每个时间步都接收当前输入和上一步传下来的隐藏状态,然后通过三个称为“门”的结构决定三件事:丢掉哪些旧记忆、写入哪些新信息、输出哪些内容。对应到菜价里就是:上周五的暴雨导致菠菜大涨,这个记忆会被保留几天;等到价格回落、市场恢复平稳,模型又会把这条旧记忆逐步“忘掉”。这种机制让 LSTM 在输入只有“当天价格”这种单变量时,也能自己从数据里学出滞后和趋势,不需要手工构造一堆移动平均特征。

这也是它长期被用于股票、销量、气象和价格预测的根本原因。相比 ARIMA 这类统计模型,LSTM 对非线性特征更宽容;相比普通前馈网络,它又能把远近不同时间跨度的历史一起纳入考虑。理解到这一层,打开代码时就不会被num_layers、hidden_size这些参数吓住,它们其实是在调节 LSTM 的记忆容量和时间建模深度。

2.2 拿到源码包先看什么:目录结构与运行流程

这种“源码+项目说明+数据集”的毕设打包,解压后通常会有相对固定的结构。我拿到手的第一步,从来不是直接跑训练,而是先花十分钟理清目录,避免缺失依赖和数据路径写到一半才报错。

常见文件/目录典型职责一开始看这里的目的
data/存放原始数据和清洗后的数据确认数据格式是 csv 还是 xlsx,是否已经按日期排好
models/保存训练好的模型权重看有没有提供现成 checkpoint,能不能跳过训练直接预测
train.py训练入口确认命令行参数、默认值、模型保存位置
predict.py预测入口确认输入输出是单条样本还是整个文件
data_preprocess.py或utils.py清洗、归一化、构造滑窗样本看数据预处理和训练是否耦合在一起
requirements.txtPython 依赖列表确认 torch 版本、是否依赖 pandas 和 sklearn
README.md或项目说明.docx项目说明优先读它,里面通常会写明数据集来源和运行步骤

读项目说明时重点看三件事:数据集是采集的真实农贸市场价格,还是模拟生成的;价格字段单位是元/斤还是元/公斤;模型是每个蔬菜品种单独建模,还是所有品种合在一个模型里。这三件事直接决定后面所有步骤正确与否。

整个项目的运行链路一般是:读取原始 csv → 清洗缺失值和异常值 → 归一化 → 构造滑窗样本 → 划分训练集和验证集 → 训练 LSTM → 反归一化 → 输出并绘制预测结果。这个链路中任何一环出问题,都会在生产预测阶段暴露出来,而不是在训练阶段,这点做到后面会有更深的体会。

2.3 最小跑通:从构建环境到完成第一次训练

先把项目跑起来,再回头理解细节,这是成本最低的启动方式。假设压缩包已经解压到本地目录,打开终端执行下面的命令。

cd /path/to/解压后的项目目录 # 创建虚拟环境,避免 torch 和其他项目依赖互相污染 python -m venv .venv # Windows 用 .venv\Scripts\activate,Linux/macOS 用下面这行 source .venv/bin/activate # 安装依赖:torch、pandas、numpy、matplotlib、scikit-learn pip install -r requirements.txt

如果项目没有提供requirements.txt,装一套最小依赖也够用:torch、pandas、numpy、matplotlib、scikit-learn。安装完成后,看一眼入口脚本名称,多数项目是train.py,也可能叫main.py。训练命令通常长这样:

python train.py --seq_len 7 --epochs 50 --batch_size 32 --lr 0.001

这里seq_len=7代表用最近 7 天价格预测下一天,epochs=50是把全部训练数据重复学 50 轮,batch_size=32是每批喂入的样本数量,lr是 Adam 优化器的学习率。第一次跑通,不需要追求精度,只要能看到 loss 逐轮下降,说明环境、数据、模型三者的衔接没有问题。

跑通后打开项目里的数据文件看一眼结构,我习惯用下面这段代码做快速预览:

import pandas as pd # 用 pandas 直接读 csv,打印前五行和字段类型 df = pd.read_csv('data/raw/vegetable_price.csv', parse_dates=['date']) print(df.head()) print(df.info())

打印结果里最重要的信息是字段名和日期范围。字段名要和你后面预处理代码里写的列名完全一致,否则KeyError会在训练中途爆出来。日期范围决定了你做训练集和验证集划分时能留多少历史数据,一般少于 500 条记录的蔬菜序列,LSTM 能学到的规律非常有限。

3. 把菜价历史数据变成 LSTM 能学的东西:数据集与特征工程

3.1 一份合格的蔬菜价格数据集长什么样

很多同学从网上下载到的城市菜价数据,字段命名五花八门:“品种”“品名”“avg_price”“prices”等等。不管叫什么,最后落到训练脚本里至少要包含三个字段:日期、品种、价格。下面是一个我见过比较规整的格式。

datevarietypriceunit
2023-01-01黄瓜2.80元/斤
2023-01-02黄瓜2.50元/斤
2023-01-03黄瓜3.20元/斤

拿到原始数据后,第一步不是建模,而是检查数据质量。常见的问题有三个:同一日期出现多条重复记录,需要按均值或最后一次记录去重;某个日期连续缺失多天,需要决定是前向填充还是直接剔除;价格列里混入“暂无报价”“--”之类的非数字字符串,要用pd.to_numeric配合errors='coerce'把它们转成 NaN 再处理。

有一个很容易忽略的细节是价格单位。有的批发市场数据按“元/公斤”记录,有的零售数据按“元/斤”记录。标题里说“蔬菜价格预测”,但价格单位不统一会让模型输出变得不可解释。统一单位是预处理里最优先做的事,不然预测结果即便 loss 很低,实际换算后也可能差一倍。

在处理缺失值这件事上,蔬菜数据比股票数据更难办。股票可以停牌不交易,菜价没有“停盘”一说,缺失日期的价格只能靠相邻日期插值。对连续缺失超过 3 天的区间,我通常直接放弃这一段,而不是强行填充,因为连续多天的价格真空会让 LSTM 学到一段假的平滑过渡。短于 3 天的缺失,用前一天的数值前向填充就足够了。

3.2 归一化与滑窗:顺序做反了,精度直接掉一半

LSTM 默认使用 tanh 作为激活函数,输入数据如果分布在几十元甚至上百元的尺度,梯度很容易饱和,训练初期 loss 会跳动得厉害。因此价格序列几乎都要先做归一化。最常见的做法是 MinMaxScaler,把价格映射到 0 到 1 之间。

顺序上有个新手极容易踩的坑:必须在切分训练集和测试集之后,只对训练集做fit,再用同一个 scaler 去transform测试集。这样做的原因后面第 5 章会展开,这里先保证别把顺序写反。

import pandas as pd from sklearn.preprocessing import MinMaxScaler # 读取并排序,数据按日期升序排列是时间序列建模的前提 df = pd.read_csv('data/processed/cucumber.csv', parse_dates=['date']) df = df.sort_values('date').reset_index(drop=True) # 按 8:2 切分训练集和测试集,注意这里不能用随机切分 train_size = int(len(df) * 0.8) train_price = df['price'].iloc[:train_size] test_price = df['price'].iloc[train_size:] # 只对训练集 fit,测试集数据不能参与计算 min/max scaler = MinMaxScaler(feature_range=(0, 1)) train_scaled = scaler.fit_transform(train_price.values.reshape(-1, 1)) test_scaled = scaler.transform(test_price.values.reshape(-1, 1))

这段代码里最值得注意的就是scaler.fit_transform和scaler.transform的配对关系。fit_transform会计算训练集的最小值和最大值,transform只是套用这些参数,不会重新计算测试集的统计量。如果写成先合并全部数据再整体归一化,测试集的信息就提前进入了模型训练过程,验证指标会虚高,部署后会明显缩水。

归一化完成后,接下来要构造滑窗样本。LSTM 的输入形状通常是(batch_size, seq_len, input_size),input_size 在单变量预测时等于 1。下面的函数把一个连续价格数组切成了很多个“窗口+标签”样本。

import numpy as np def make_sequences(data, seq_len=7): xs, ys = [], [] for i in range(len(data) - seq_len): x = data[i:i + seq_len] y = data[i + seq_len] xs.append(x) ys.append(y) return np.array(xs, dtype=np.float32), np.array(ys, dtype=np.float32) # 用 7 天窗口预测第 8 天价格 X_train, y_train = make_sequences(train_scaled, seq_len=7) X_test, y_test = make_sequences(test_scaled, seq_len=7) print(X_train.shape, y_train.shape)

这里seq_len=7意味着训练样本第一条是第 1 到第 7 天的价格,标签是第 8 天;第二条是第 2 到第 8 天,标签是第 9 天,依此类推。窗口越大,模型能看到的历史越长,但同时可用的训练样本越少,因为序列长度固定时,前seq_len个位置没有足够的“过去”。对蔬菜价格这类周期大约一周的序列,7 天和 14 天是比较常用的窗口。如果你不知道选多少,先跑 7,再跑 14,对比验证集误差再决定。

3.3 按蔬菜品种建模还是混合建模:两种做法的取舍

数据集里往往有几十个品种,大家第一反应是全部塞进一个模型。这样做的优点是样本量大,缺点是不同品种的价格水平差异太大:大白菜常年两块钱以内,四季豆冬天能冲到十几块。共用一个输出尺度时,LSTM 会把重点集中在价格数值更大的品种上,大白菜的预测误差反而不太会受到惩罚,因为它的绝对值太小。

主流做法有两种。第一种是按品种拆分,单独训练多个 LSTM 模型,每个模型只学一个品种的价格规律。这种做法的优点是完全解耦,缺点是品种一多,训练和调参的工作量成倍上涨。第二种是混合建模,也就是把所有品种放在一起,但给模型额外输入一个品种编号作为特征。特征工程上,可以对品种做 one-hot 编码,也可以训练一个 Embedding 层,让模型自己去学习品种之间的相似性。

# 按品种拆分训练的常见写法 variety_list = df['variety'].unique() for variety in variety_list: sub = df[df['variety'] == variety].sort_values('date') # 每个品种都单独走一遍归一化和滑窗构造 # 然后训练一份独立的 LSTM pass

我个人的经验是:如果毕设要求呈现“一整套完整系统”,按品种拆分更容易讲清楚,因为你可以展示模型在不同品种上的差异化表现;如果想突出模型的泛化能力,或者数据集较小、单个品种样本不够,那就走混合建模。混合建模只需要给模型多设一个输入分支,PyTorch 里用nn.Embedding就能实现,这里不展开。新手做毕设,优先选择按品种拆分,逻辑清楚,调试方便,答辩时也能有更多话说。

4. 训练 LSTM 模型:结构、参数与结果解读

4.1 模型结构:几层 LSTM、Dropout 和全连接层怎么接

滑窗构造完成后,接下来就是模型本身。一个能跑通的 LSTM 预测模型,核心结构一般只有三层:LSTM 层、全连接输出层,中间可以夹 Dropout。下面的代码是一个最常见的教科书级结构,也是这套源码里最可能出现的写法。

import torch import torch.nn as nn class PriceLSTM(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=input_size, hidden_size=hidden_size, num_layers=num_layers, batch_first=True, dropout=dropout ) self.fc = nn.Linear(hidden_size, 1) def forward(self, x): # x 形状: (batch_size, seq_len, input_size) out, _ = self.lstm(x) # 取最后一个时间步的输出,相当于把“看完整个序列后的理解”交给全连接层 out = out[:, -1, :] return self.fc(out)

几个参数需要认真理解。input_size=1是因为当前输入只有“价格”这一个特征,如果你后面加入了温度、节假日等特征,就要改成对应的数值。hidden_size=64是 LSTM 隐状态的维度,相当于模型记忆的容量,容量过小会记不住周期规律,容量过大则容易过拟合。num_layers=2表示堆叠两层 LSTM,层数越多模型可以建模更高层的时间抽象,但训练耗时和内存占用也会上升。batch_first=True表示输入形状是“批次在第一位”,如果忘了这个参数,拿出来的数据形状就会和输入维度错位,报错信息会让人绕很久。

Dropout 要注意一点:nn.LSTM的dropout参数只在num_layers > 1时才生效,单层 LSTM 就算设置了dropout=0.2也不会有任何正则化效果。此外,最后一层 LSTM 后面再接一个nn.Dropout(0.2)也很常见,有的代码会把 Dropout 放在全连接层之前,这两种位置都可以,但不要为了调参把两处都设得很大,否则模型可能学不到东西。

4.2 训练循环与评估指标:不要只看 loss 曲线

模型定义好之后,训练循环本身并不复杂。PyTorch 的标准迭代逻辑就是前向传播、算损失、反向传播、更新参数。这里给出最简版本,足够作为源码阅读对照。

from torch.utils.data import TensorDataset, DataLoader X_train_t = torch.from_numpy(X_train) y_train_t = torch.from_numpy(y_train) train_loader = DataLoader( TensorDataset(X_train_t, y_train_t), batch_size=32, shuffle=True ) model = PriceLSTM(input_size=1, hidden_size=64, num_layers=2, dropout=0.2) optimizer = torch.optim.Adam(model.parameters(), lr=0.001) criterion = nn.MSELoss() for epoch in range(50): model.train() epoch_loss = 0.0 for x_batch, y_batch in train_loader: optimizer.zero_grad() pred = model(x_batch) loss = criterion(pred.squeeze(), y_batch) loss.backward() optimizer.step() epoch_loss += loss.item() if (epoch + 1) % 10 == 0: print(f'epoch {epoch + 1}, loss: {epoch_loss / len(train_loader):.6f}')

DataLoader里的shuffle=True是大多数分类任务的标准配置,但时间序列任务里它需要被谨慎对待。后面避坑章节会细讲,这里先记住一点:对滑窗样本做洗牌,会把原本按时间排列的样本顺序打乱,但每个样本内部的窗口顺序没有变化,训练本身仍然有效。关键在于训练集和验证集必须按时间先后切分,不能让验证集日期出现在训练集之前。

训练完成后,评估才是重头戏。很多人只看 loss,loss 是归一化尺度上的误差,看着 0.001 很兴奋,反归一化后才发现误差有五六毛钱。更直观的指标是 MAPE,也就是百分比误差。下面这段代码计算的是“平均绝对百分比误差”。

def mape(pred, true): pred = np.array(pred) true = np.array(true) return np.mean(np.abs((true - pred) / true)) * 100

MAPE 在菜价预测里比 MSE 有用得多,因为它直接告诉你“平均偏离真实值百分之几”。比如 MAPE 等于 15,意味着预测价格与真实价格平均偏差 15%,2 元一斤的黄瓜平均偏掉三毛钱,这个精度放在实际市场里已经很有说服力。评价模型好坏,不能只看单点误差,还要把验证集的预测结果反归一化后和真实价格画在一起,肉眼看曲线拟合程度。训练集表现好、验证集表现差,是过拟合;验证集和测试集都表现平平,可能是序列长度或者隐藏层容量没设好。

4.3 三个最值得调的参数:序列长度、隐藏层、学习率

LSTM 的可用超参数不少,但对蔬菜价格这种规模不大的数据集,真正对结果影响最显著的只有三个:序列长度、隐藏层大小、学习率。

参数常用范围影响
seq_len7、14、30控制模型回看历史长度;太短学不到周期,太长压缩训练样本
hidden_size32、64、128记忆容量;过大容易过拟合,过小欠拟合
lr0.0005、0.001、0.003学习率过高导致 loss 震荡,过低收敛极慢

seq_len的选择可以结合蔬菜价格的波动周期来看。叶菜类一般一周到十天一个供应周期,根茎类波动更慢,所以 7 和 14 是起步首选。hidden_size不建议直接上 256,蔬菜价格序列样本量通常只有几百到几千条,隐藏单元太多会让模型轻松背下训练集,验证集却错得离谱。lr对 Adam 优化器来说,0.001 是安全的起点。如果 loss 前几个 epoch 不降反升,先看看是不是lr设大了;如果 loss 下降很慢,那可以试试提高到 0.003。

调参没有银弹,我习惯的做法是固定其他参数,只动一个变量,跑完记录验证集 MAPE,再回来看结果。同时开太多参数组合,最后出了问题你根本不知道是哪一项的锅。毕设项目能交差的标准其实不高:训练曲线收敛、验证集 MAPE 在 15% 上下、预测图能跟上真实曲线的整体走势,就已经达到“基于深度学习 LSTM 实现蔬菜价格预测”的预期了。

5. 避坑:蔬菜价格预测中最容易翻车的 5 个细节

5.1 归一化泄漏:测试集信息跑到训练集里

现象:训练集和验证集的 loss 都低得漂亮,预测曲线单独看也正常,但把测试集真实价格放上去,发现预测曲线整体比真实值低一截,或者缩在一个很窄的区间里。

原因:归一化时没有先切分训练集和测试集。如果对整条价格序列直接做fit_transform,测试集的最大值和最小值已经参与了计算,模型在训练时等于提前知道了未来数据的取值范围。预测阶段套用这个 scaler 后,新数据里的极端价格很容易被压出既定区间。

解决:严格遵循“先切分,再拟合”的顺序。训练集上scaler.fit_transform,验证集和测试集只做transform。代码改动很小,但影响非常大。

# 错误:全量数据 fit,会引入未来信息 scaler = MinMaxScaler() all_scaled = scaler.fit_transform(price.reshape(-1, 1)) # 正确:只在训练集上 fit scaler = MinMaxScaler() train_scaled = scaler.fit_transform(train_price.reshape(-1, 1)) test_scaled = scaler.transform(test_price.reshape(-1, 1))

5.2 时间顺序被打乱:随机切分让模型“作弊”

现象:代码里用train_test_split(test_size=0.2, random_state=42)切分数据后,验证集 MAPE 表现很好,换成按时间顺序切分再测,误差直接涨了几倍。

原因:train_test_split默认是随机采样。随机切分时,训练集里混入了日期上位于验证集之后的样本。对时间序列来说,这就是未来数据泄漏。LSTM 在训练时已经见过“未来”的价格走势,验证自然漂亮,但部署时未来的数据根本不存在,模型立刻现出原形。

解决:用按时间顺序的索引切分,或者使用TimeSeriesSplit。下面的写法最直观。

# 按时间顺序切分,前 80% 训练,后 20% 测试 train_size = int(len(df) * 0.8) train_series = df['price'].iloc[:train_size] test_series = df['price'].iloc[train_size:]

5.3 多步预测的误差越滚越大

现象:单步预测每天误差很小,但让模型连续预测未来 30 天,预测曲线要么慢慢变成一条水平线,要么越走越偏,最后飞到天上。

原因:连续预测时,第一步预测的误差会被当作第二步的输入,误差像滚雪球一样累积。LSTM 在训练时用的是真实历史价格作为输入,而预测阶段输入的是自己上一轮的输出,分布不一致,这在学术上叫“暴露偏差”。

解决:不要用“预测一天→把预测值拼到输入里→再预测一天”的滚动方式做长周期预测,除非你明确知道误差累积的影响可以接受。更稳定的做法是一次性输出未来 N 天价格,也就是把输出维度改成 N,或者在训练时随机用一部分预测结果替代真实值作为输入,让模型学会纠正自己的误差。对毕设来说,最简单的改进是评估时只看未来 1 到 7 天的预测,并明确写出误差累积的边界。

5.4 数据里的尖峰价:模型学会了“晴天霹雳”

现象:真实价格曲线在某几天突然暴涨,比如从 2 元跳到 6 元,但模型的预测曲线在这些位置总是反应迟钝,往往晚两天才跟上去,而且滞后还会污染后面几天的预测。

原因:暴雨、台风、节假日、市场短期缺货都会造成价格尖峰,但这些信息没有体现在输入特征里。模型只能从历史价格里推测,可这种突发性尖峰在训练集里只是偶发事件,模型为了整体 loss 最小,宁可把尖峰当噪声忽略掉。

解决:两个方向。第一,修正训练数据,把明显异常且无法解释的尖峰做削峰处理,比如用前后三天的中位数替代;第二,保留尖峰,但把节假日、天气这类特征作为额外输入加入模型,让模型知道“暴雨后的涨价是合理反应”而不是偶然噪声。如果毕设数据里没有天气字段,最简单的做法是只对尖峰做标记,让模型在后续反向传播时少被这些极端值牵着走。

5.5 跨品种预测时价格尺度不一致

现象:把所有蔬菜品种放在一起训练,loss 收敛得很快,但单独看每个品种的预测图,发现高价品种拟合得好,低价品种简直是条直线。

原因:不同蔬菜的价格范围差异悬殊。大白菜价格常年在一两元,四季豆旺季能到十几元。共享一个归一化尺度时,LSTM 在均方误差的驱动下,把绝大多数容量用来拟合高价品种的波动,低价品种的误差绝对值太小,对 loss 的贡献微不足道,自然学不好。

解决:最省事的方法是按品种拆分模型,一个品种独立归一化、独立训练。正如第 3 章所说,这个方法会带来多套模型和配置,但胜在稳定。如果你有品种编号字段,并且想保留混合建模,记得对每个品种单独做归一化,再把品种 id 作为离散特征输入,通过 Embedding 层把品种差异交给模型自己学习。

6. 再进阶一步:多步预测与“预测值-真实值”对照验证

当你已经把单步预测跑通,验证集 MAPE 也能稳定在 15% 以内,下一步值得做的是把预测结果变成更可信的业务输出。我每次做完训练后的第一件事,不是调参,而是把验证集最后 30 天的真实价格拿来做一次整体对齐,画出两条曲线并排放在一起。这一步能立刻暴露出前面 5 个坑里至少一半的问题。

线画好之后,如果只看单天效果不错,那可以再做一次回测式验证。常见做法是 walk-forward:从数据集中间某个位置开始,每次都只用当前时刻之前的窗口做预测,然后向后移动一步,看历史预测误差稳定不稳。

def walk_forward_validate(model, window, scaler, pred_steps=7): model.eval() future = [] cur_window = window.copy() with torch.no_grad(): for _ in range(pred_steps): x = torch.tensor(cur_window, dtype=torch.float32).unsqueeze(0) pred = model(x).item() future.append(pred) cur_window = np.concatenate( [cur_window[:, 1:, :], np.array([[[pred]]], dtype=np.float32)], axis=1 ) # 反归一化,得到真实价格尺度下的预测值 return scaler.inverse_transform(np.array(future).reshape(-1, 1))

这段代码展示了递归多步预测的基本逻辑:预测出一个点,就把它接到窗口末尾,同时丢掉窗口最前面的旧点,继续预测下一天。值得注意的是第 5 章讲过的误差累积问题,跑这个函数时你会直观看到预测曲线在前 1 到 3 步内贴近真实值,越往后越平缓。我习惯把递归结果和直接多步输出各跑一组,两个结果一对比,就能判断模型到底是因为学到规律而预测得好,还是只是因为惯性延展了上一步的走势。

做完这些之后,不管项目是不是到做多步预测的深度,都建议回到损失曲线的震荡幅度上看一眼。如果 loss 曲线上下跳得厉害,多半是lr偏高或者 hidden_size 偏大。我现在的调参习惯是:先不管那些“深度学习玄学”,把一条能跑通的基线段落先稳定下来;然后拿真实价格和预测价格的残差图去看,哪些日期误差最大,再回过头查这些日期是不是节假日、下雨天或市场断供期。能解释清楚的误差,才是对你模型预期的真实反馈。

这个方向无论作为毕设答辩还是入门时序预测实战,都是一个容易讲清楚、也容易验证的项目。真正把它做好,靠的还是对数据质量较真、对参数变化敏感、对预测边界诚实。希望这些整理出来的细节,能帮你在自己的数据集上少踩几个坑,早点跑出第一条能看过眼的预测曲线。

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

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

JMeter压测脚本录制实战:四种方式、HTTPS关联与参数化避坑

每次带新人做性能测试,我都会先问一句:你的压测脚本是怎么来的?十有八九的回答是“用 Jmeter 录的”。Jmeter 录制压测脚本确实快,浏览器点一遍,HTTP Sampler 就哗啦啦生成一堆,比手写省太多时间。但录制这…

作者头像 李华
网站建设 2026/10/1 4:06:09

Substance Painter武士角色PBR纹理全流程:从白模到引擎的实战指南

1. 从一张白模到能进引擎的武士:这套纹理流程到底在解决什么很多人第一次接触 Substance Painter 做角色纹理,脑子里想的都是“打开软件、拖几个材质球、画两笔就完事”。真上手一个 AAA 级别的武士角色,才发现事情完全不是这么回事。一个完整…

作者头像 李华
网站建设 2026/10/1 4:04:31

Flutter for OpenHarmony购物清单开发全解析

从我做这款生活助手App的第一天起,购物清单就被摆在优先级最高的一栏。原因很简单——它可以高频出现在家庭日常里,而高频率使用的功能最容易暴露出一个跨端框架的真实水平。这次我没有用HarmonyOS原生去写,而是选了Flutter for OpenHarmony这…

作者头像 李华
网站建设 2026/10/1 4:04:10

防火墙源代码.zip解析:从解包到双网卡透明网关部署

简介:基于费尔防火墙 1.0 的源代码压缩包是一份面向网络安全开发者、高校学生及防火墙技术爱好者的学习资料,旨在帮助读者从底层理解防火墙的包过滤、规则控制与异常行为处理逻辑。压缩包仅约529KB,内部包含核心源码、功能说明文档&#xff0…

作者头像 李华
网站建设 2026/10/1 4:04:04

SVM支持向量机从原理到Python实战:核函数、参数调优与工程落地

1. 为什么到现在还在学SVM:它到底解决了什么问题标题里写着“从原理到实战”,我实际跑下来发现,真正把SVM讲透又做成全流程的教程其实没那么多。很多文章要么推到数学推导就断更了,要么直接调库喊一句“RBF核效果最好”完事&#…

作者头像 李华
网站建设 2026/10/1 4:03:59

JavaWeb招聘系统毕设源码:含数据库+双前端+可扩展架构

简介:这是一套完整可用的JavaWeb招聘网站系统毕业设计项目,面向计算机专业本科生及Java初学者,解决课程设计、期末大作业与毕业设计选题难、实现难、调试难三大痛点。资源包共361个文件,包含88个核心Java业务逻辑代码、11个JSP前端…

作者头像 李华