ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

LSTM蔬菜价格预测:时间序列建模与PyTorch实战全解析

LSTM蔬菜价格预测:时间序列建模与PyTorch实战全解析 简介面向计算机相关专业正在做毕设的学生及有项目实战需求的学习者这份基于深度学习LSTM的蔬菜价格预测项目提供完整Python源码、项目说明文档与真实数据集。项目以菜心、西红柿、红尖椒等常见蔬菜的历史价格为基础利用长短期记忆网络完成时序数据预处理、特征构建、模型训练与结果评估可帮助读者理解LSTM在时间序列预测中的完整流程也可直接作为课程设计、期末大作业或毕业设计的参考实现。压缩包共181个文件主要包括142个csv价格数据、25个py源码文件、10个pyc编译文件、3个docx项目说明及1个md说明文件整体仅1.86MB目录结构清晰便于查阅。目前已有271人学习下载。读者可获得可运行的完整代码、配套数据集与说明文档既能快速搭建预测框架又能从中学习数据清洗、模型调参、预测评估等实战方法尤其适合需要完整项目支撑的毕设场景对提升项目能力很有帮助。1. 为什么一个毕业设计级的LSTM蔬菜价格预测项目值得你花两天时间跑通“今天菠菜进价两块八明天大概能卖多少”这类问题放在几年前大多靠老师傅拍脑袋而“基于深度学习LSTM实现蔬菜价格预测”这个标题给的是一条能摆脱拍脑袋的通用解法用LSTM神经网络读取过去七天的价格序列预测未来一天或几天的价格走向并且整套东西以python源码、项目说明和数据集的形式打包在一起从数据清洗、滑窗构造、模型训练到预测曲线每个环节都有章可循。它不是一个高不可攀的极限案例而是时间序列入门里最扎实的一条路——数据量不大、特征不复杂、训练完效果肉眼可见。适合正在为毕设找方向的在校生也适合想在真实业务里试水深度学习的从业者。接下来把原理、代码和踩坑讲透让照着复现的人能跑出结果。2. LSTM凭什么做蔬菜价格预测序列建模原理与选型理由2.1 菜价序列的特性和RNN的天然短板蔬菜价格和股票、电力负荷一样是典型的时间序列数据但它的脾气比股票温和得多日度数据存在明显的七天周期批发市场周末交易量下滑、价格走低节假日消费高峰价格跳涨加上季节性的供给波动让这条曲线同时带上了趋势、周期和脉冲三种成分。这类数据如果丢给普通机器学习模型处理比如把最近七天的价格当作七个独立特征喂给线性回归或XGBoost不是不行而是模型完全丧失了对“顺序”的感知——第几天是周六、昨天和前天谁离今天更近这些信息在输入维度上是等价的模型分不清先后。RNN就是专门为“带顺序的数据”设计的它按时间步逐个读取输入把前一步的隐状态传递到后一步。但经典RNN有一个结构性的弱点反向传播经过多个时间步之后梯度要么指数衰减到接近零要么指数膨胀到爆炸。结果是模型能记住上一步的价格却很难把五天前的价格变化有效传到当前预测。菜价预测恰恰依赖这种中周期记忆——五天前的低价往往预示今天的补涨上周的异常跳变又该被忽略。这个“记不住该记住的、忘不掉该忘的”短板让RNN在长依赖场景里名不副实也直接催生了LSTM。反直觉的地方在于菜价预测的难点从来不是“模型不够聪明”而是“数据里的周期信号被噪声盖住了”。LSTM的价值不是把预测做得多精确而是提供了一种让模型自己学会“该保留什么、该遗忘什么”的机制。个人实际经验是在几百到几千条日度数据上只要滑窗和归一化处理得当单层LSTM就能稳定抓住七天周期这比把网络堆深、堆宽带来的收益大得多。2.2 三个门与细胞状态LSTM如何主动记住“该记住的”先看细胞状态c_t它是一条从序列开头流向结尾的隐藏通道可以理解为一本长期记账本。遗忘门读取上一个时间步的输出h_{t-1}和当前输入x_t经过sigmoid后输出一个0到1之间的值决定上一时刻的细胞状态c_{t-1}里有多少内容保留到当前时刻。输入门决定当前时刻的新信息有多少写入细胞状态它由两部分组成一个sigmoid层决定“写多少”一个tanh层生成候选值。输出门最后决定从细胞状态中读出多少内容通过tanh把细胞状态压到-1到1之间再和sigmoid输出相乘得到当前时间步的隐状态h_t。拿一周的菠菜价格举例。假设序列里存在规律周二价格通常是一周低点周六价格冲高。模型在训练中会发现预测周六价格时周二的低点信息比周四的普通波动更有参考价值于是遗忘门会偏向保留细胞状态里“周二低点”的痕迹输入门则会对周四的普通价格给出较低的写入权重以免噪声污染长期记忆。如果有一天因为台风导致菠菜价格临时翻倍输入门又会抑制这条异常信息进入细胞状态输出门再决定当前时刻是应该强调“台风跳变”还是强调“七天周期”。这套机制让LSTM天然适配“有节奏、有记忆、有异常脉冲”的序列而这些特征菜价全占了。遗忘门管“忘多少”输入门管“记多少”输出门管“用多少”三者组合起来等于把记忆策略变成了可学习的参数。之后Google提出的GRU把三个门精简成两个门参数更少、训练更快但在中小规模序列上LSTM的精度通常仍略胜一筹这也是毕设项目里LSTM出现频率远高于GRU的原因。2.3 和其他模型对比ARIMA、GRU、Transformer为什么不是首选做时间序列预测绕不开ARIMA、GRU和Transformer这几个常被拿来对比的对象。我一般会在项目说明阶段给出一张选型表帮助先定方向而不是一上来就追最新模型。模型适用场景优点在菜价预测上的问题ARIMA平稳、线性、短期序列实现简单、可解释性强菜价非平稳节假日脉冲和七天周期用差分难以完全消除经典RNN顺序数据结构简单梯度消失五天前信息权重几乎为零LSTM非平稳、中长周期序列门控记忆机制能捕捉周期与脉冲需要较多数据超参数敏感GRU类似LSTM参数少、训练快同数据量下精度不一定更高适合做对照实验Transformer大数据量、长序列并行计算、全局注意力千条量级数据容易过拟合训练成本高选型理由很直白蔬菜价格数据集通常只有几百到几千条日度记录Transformer的自注意力机制在小样本上极容易把训练集背下来验证集泛化很差ARIMA对平稳性要求高菜价七天周期加节假日跳变靠差分很难收拾干净。LSTM在这个数据规模区间里表现最稳PyTorch对它的封装又足够干净。所以在“lstm设备寿命预测实战”、量化交易策略、电力负荷预测这些常见的深度学习中时间序列项目里大家用的都是同一套范式滑窗取一段历史观测映射到未来一个点或几个点差异只在特征工程和预测步长的设置上。对毕设而言LSTM还有一层优势能够清楚讲出门控机制的原理而不是像Transformer那样一张注意力图很难向答辩老师解释清楚。3. 把原始菜价数据集整理成LSTM能吃的样本清洗、归一化与滑窗拿到一个毕设级项目包稳妥的第一步不是急着打开训练脚本而是先看数据长什么样。常见做法是数据集以csv形式躺着列名大概是date、variety、unit_price这几类。先用pandas读进来看shape、dtypes和describe快速建立对数据量级和价格区间的感知再动手清洗。做“lstm时间序列预测python”项目最常用的一套数据管线基本就是下面这个流程可以直接抄。3.1 拿到数据集之后先做的三件事读列名、看时间跨度、查缺失值价格序列最怕两种脏数据一种是重复日期同一天同一个品种出现两行价格还不一样另一种是休市空行周六周日或法定节假日批发市场不开日期直接空缺。前者要去重后者要补全或用插值处理不能留着乱序空洞让滑窗窗口错位。下面是一份典型的清洗代码import pandas as pd import numpy as np df pd.read_csv(vegetable_price.csv, parse_dates[date]) df df.dropna(subset[date]) # 日期为空直接删 df df.sort_values(date) # 严格按时间排序 df df.drop_duplicates(subset[date, variety], keeplast) df[unit_price] pd.to_numeric(df[unit_price], errorscoerce) df df.dropna(subset[unit_price])这段代码里keeplast保留了同一天同品种的最后一条记录适合“早市价和晚市价并存、以晚市为收盘价”的统计口径如果项目说明里写的是“取当天均价”那这里应该改成按日期分组求均值。errorscoerce把字符串价格转成数值转不了的自动变NaN留到下一步dropna统一清理。还有一个常被忽略的动作检查是否有价格小于等于0的脏记录这类数据通常是录入错误不能靠插值救先删掉更稳。接下来处理周末空行。正确做法不是删掉空日期而是补全日期索引再对缺失价格插值df df.set_index(date) df df.reindex(pd.date_range(df.index.min(), df.index.max(), freqD)) df[unit_price] df[unit_price].interpolate(methodlinear) df df.reset_index()这里用methodlinear做线性插值对短促的空缺够用如果缺失跨度超过三天用methodtime按时间间隔加权会更合理。最容易翻车的是多品种数据混在一起做全局插值——大白菜缺了三天结果用菠菜的价格去填这种错误在最终预测曲线上几乎看不出来但误差会全部转嫁到LSTM的输入上。正确做法是先按品种分组组内各自reindex和interpolate最后再拼回一个DataFrame。提示清洗阶段把每个品种的“价格缺失天数统计”打出来看一眼缺失超过总天数10%的品种建议直接放弃否则插值出来的信号全是噪声。3.2 归一化训练集fit、测试集transform不要给模型开“天眼”LSTM的激活函数是tanh和sigmoid敏感区间集中在0附近输入尺度太大时梯度会饱和到几乎消失所以价格序列进模型前必须归一化。最常见的选择是用MinMaxScaler把价格压到0到1之间还是用StandardScaler做标准化。菜价不是零均值稳定序列MinMax更直观反归一化也容易StandardScaler对异常值更鲁棒如果数据里某天因为极端天气价格涨了五倍MinMax会把大部分普通价格压到很窄的区间这时应改用StandardScaler。我一般先用MinMax跑通再换StandardScaler复测一遍取验证集效果更好的。真正要命的坑在下面这段代码的写法上。归一化器只能fit在训练集上如果先把整个数据集fit一遍再做train/test切分测试集的极值已经混进了缩放参数等于模型训练时偷偷看到了未来价格的范围预测结果虚高。这个问题在毕设答辩里被老师问到的概率极高属于典型的数据泄露。from sklearn.preprocessing import MinMaxScaler price df[unit_price].values.reshape(-1, 1) split_idx int(len(price) * 0.8) scaler MinMaxScaler(feature_range(0, 1)) train_scaled scaler.fit_transform(price[:split_idx]) test_scaled scaler.transform(price[split_idx:])price[:split_idx]按时间顺序切出前80%做训练scaler.fit_transform只在这80%上计算min和maxscaler.transform直接复用训练集的缩放参数处理测试集保证测试集信息没有任何机会进入训练过程。8:2的切分比例对日频数据基本够用但如果数据量不足300条建议改成7:3宁可少一点训练样本也要给验证留出足够长度来观察周期波动。3.3 滑窗构造样本look_back、预测步长和样本量怎么配LSTM不吃“一整条序列”它吃的是“一条序列的切片”。把过去7天的价格作为输入、未来1天的价格作为输出这种构造方式叫滑窗。look_back选多少直接决定模型能看到多远的历史。菜价预测里7天是底线因为七天周期摆在那里窗口太小模型看不清周期窗口也不是越大越好大了样本数变少训练数据不够用。3000天数据配look_back7能构造出约2990个样本完全够用300天数据配look_back14也还能跑但要警惕样本太少导致过拟合。def create_sequences(data, look_back7, forecast_step1): X, y [], [] for i in range(len(data) - look_back - forecast_step 1): X.append(data[i:i look_back, 0]) y.append(data[i look_back:i look_back forecast_step, 0]) return np.array(X), np.array(y) X_train, y_train create_sequences(train_scaled, look_back7) X_test, y_test create_sequences(test_scaled, look_back7) X_train X_train.reshape(-1, look_back, 1) X_test X_test.reshape(-1, look_back, 1)forecast_step没有传值默认是1也就是只预测明天。若想直接预测未来三天把forecast_step3传给函数y的形状会从(batch, 1)变成(batch, 3)对应模型的output_size也要改成3。reshape(-1, look_back, 1)最后这步至关重要PyTorch的nn.LSTM期望输入是(batch, seq_len, input_size)三维张量其中第三维是特征数。现在只有“价格”这一个特征所以填1后面想加入节假日、天气、前一日成交量等特征就把最后一维改成特征总数同时调整滑窗构造函数让每个时间步都携带完整特征向量。滑窗还有一个常见变体重叠取样。如果数据集很短只有150天普通滑窗只能构造140多个样本训练起来捉襟见肘。常见做法是把滑窗步长从1改成2或3让相邻窗口重叠能变出更多样本代价是相邻样本高度相关模型容易记住窗口间重复的部分。我一般只在数据实在不够时才这么干并且会盯紧验证集loss防止“样本多了、泛化差了”。4. 搭建LSTM模型并跑通训练PyTorch源码级最小复现数据管线跑通模型这层反而简单。PyTorch把LSTM封装得接近“开箱即用”难点不在写模型而在理解张量维度和训练循环里的细节。很多人搜“lstm模型代码”时找到的是Keras版本换到PyTorch后形状问题频发。这里用PyTorch给出一套能直接跑的最小实现重点讲清楚每个维度是怎么来的。4.1 模型定义nn.LSTM的参数和batch_first先定义网络结构。nn.LSTM里的hidden_size不是“隐藏层神经元数量”这么简单的概念它实际是隐状态向量h的维度也就是LSTM单元输出向量的长度。hidden_size太小模型记不住七天周期的完整模式太大参数数量暴涨在几千条数据上基本必然过拟合。经验值是单变量输入时hidden_size取32到128之间先从64起跑再看验证集表现上下调。import torch import torch.nn as nn class PriceLSTM(nn.Module): def __init__(self, input_size1, hidden_size64, num_layers1, 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, _ self.lstm(x) # out: (batch, seq_len, hidden_size) out out[:, -1, :] # 取最后一个时间步的隐状态 return self.fc(out) # (batch, output_size)这段代码有两个关键点。batch_firstTrue让第一个维度是batch对应输入X_train的shape是(batch, look_back, 1)如果忘了这个参数默认第一个维度是seq_len后面所有reshape都要反过来报错信息还特别绕。out[:, -1, :]取每个样本序列最后一个时间步的隐状态因为要预测的是“看完过去7天之后”的下一天只需要最后一个时刻的输出。nn.Linear把hidden_size映射到output_size单步预测时output_size1输出一个数。num_layers1建议作为起点。很多源码默认写2层甚至3层多层LSTM确实有更强的抽象能力但前提是数据量能喂饱它。几百条价格数据配2层64维LSTM参数总量上千很容易把训练集背下来。如果单层验证loss不理想再加第二层并同步调大dropout。注意PyTorch的nn.LSTM里dropout参数只在num_layers大于1时才生效这是源码里容易看漏的边界。input_size输入特征数只建模价格时为1加节假日特征后按实际特征数调整hidden_size隐状态维度建议64起步num_layers隐藏层数数据量小先取1batch_firstTrue配合整个数据管线使用4.2 训练循环MSE、Adam、早停训练循环是整个复现里最值得认真读的部分。损失函数选MSELoss因为回归任务中均方误差对大误差的惩罚更重能推动模型关注价格跳变如果后续发现预测对异常值过于敏感可以换成HuberLoss。优化器用Adam学习率从0.001起步。菜价数值经归一化后范围平缓0.001通常能在50个epoch内收敛到像样水平。import torch.optim as optim X_train_t torch.tensor(X_train, dtypetorch.float32) y_train_t torch.tensor(y_train, dtypetorch.float32) X_test_t torch.tensor(X_test, dtypetorch.float32) y_test_t torch.tensor(y_test, dtypetorch.float32) model PriceLSTM(input_size1, hidden_size64, num_layers1, output_size1) criterion nn.MSELoss() optimizer optim.Adam(model.parameters(), lr0.001) batch_size 32 n_epochs 80 best_val_loss float(inf) for epoch in range(n_epochs): model.train() for i in range(0, len(X_train_t), batch_size): X_batch X_train_t[i:i batch_size] y_batch y_train_t[i:i batch_size] optimizer.zero_grad() pred model(X_batch) loss criterion(pred, y_batch) loss.backward() optimizer.step() model.eval() with torch.no_grad(): val_pred model(X_test_t) val_loss criterion(val_pred, y_test_t).item() if val_loss best_val_loss: best_val_loss val_loss torch.save(model.state_dict(), best_lstm.pt) if (epoch 1) % 20 0: print(fepoch {epoch1}/{n_epochs} train {loss.item():.6f} val {val_loss:.6f})y_train的shape在create_sequences里已经是(batch, 1)所以不需要再unsqueeze这一点与很多图像分类代码不同。model.train()和model.eval()之间的切换不能省略虽然单层LSTM里没有dropout和BN但养成这个习惯能避免以后加层时验证阶段还开着dropout导致每次预测结果随机波动。torch.save(model.state_dict(), best_lstm.pt)只在验证loss创新低时执行这是简化的早停保证最终拿到的模型文件是验证集上表现最好的那一份。batch_size需要单独强调。训练样本2000条以上时用32没问题样本只有几百条时建议降到8或16否则一个batch里覆盖不到足够多的价格周期模式梯度方向容易偏。反过来如果数据量上万batch_size提到64或128训练速度明显提升精度通常不会下降。4.3 用保存的模型做预测并反归一化训练阶段的输出是归一化后的值最终交付时必须还原成“元/公斤”这种原始单位。反归一化只需要调用之前保存的scaler代码很短model.load_state_dict(torch.load(best_lstm.pt)) model.eval() with torch.no_grad(): pred_scaled model(X_test_t).squeeze().numpy() pred_price scaler.inverse_transform(pred_scaled.reshape(-1, 1)).flatten() real_price scaler.inverse_transform(y_test_t.numpy().reshape(-1, 1)).flatten()这里有个容易踩的细节测试集归一化时用的是scaler.transform反归一化必须复用同一个scaler对象不能new一个再fit否则min和max不一致还原出来的价格完全错位。判断结果是否合理的快速方法是打印pred_price和real_price的均值、标准差如果均值差超过10%基本可以断定是反归一化或数据切分出了问题先别急着调模型结构回去查数据管线。5. 避坑菜价预测最容易翻车的5个现场做“lstm预测”做多了会发现踩过的坑几乎都在数据管线层面模型本身反而不太出问题。下面五条是常见实现里最容易复现的翻车现场按“现象 → 原因 → 解决”的顺序说透方便对照排查。5.1 训练时shuffle了数据集顺序全乱了现象训练loss一路下降验证loss曲线却只是随机波动预测值和真实价格完全错位甚至验证集上出现过拟合般的好成绩。原因很多人沿用图像分类的习惯在DataLoader里设置了shuffleTrue把原本有先后关系的时间序列打散了。LSTM的滑窗样本之间天然顺序相关shuffle不仅破坏顺序还让验证集混进了未来信息训练出来的是一个“背答案”的模型。解决DataLoader的shuffle参数设成False或者干脆不用DataLoader像第四章那样手动按batch切片。切分训练集和测试集的代码里也不要出现任何random操作。5.2 归一化的后悔药fit放到了整个数据集上现象训练正常、验证正常反归一化之后预测曲线比真实曲线整体上移或下移偏差稳定在某个常数附近。原因MinMaxScaler().fit_transform(whole_data)在全部数据上计算了min和max测试段的极端价格提前参与了缩放导致预测值在归一化空间里“作弊”反归一化后自然对不齐。解决严格按第三章的写法fit只在训练段调用transform在测试段复用同一个scaler。# 错误写法测试信息进入缩放参数 scaler MinMaxScaler() whole_scaled scaler.fit_transform(price) # 正确写法测试段只transform scaler MinMaxScaler() train_scaled scaler.fit_transform(price[:split_idx]) test_scaled scaler.transform(price[split_idx:])5.3 预测曲线“滞后一拍”是怎么回事现象预测曲线和真实曲线形状几乎重合但整体向右平移了一个单位今天的预测值约等于昨天的真实值。原因look_back窗口里昨天的价格权重太大LSTM在MSELoss的驱动下发现“复制最近观测值”最容易把loss压到最低于是退化成一个恒等复制器并没有真正学到周期规律。这在小步长价格预测里非常常见也是“lstm时间序列预测python”项目里被问得最多的现象之一。解决方法有三条。一是做一阶差分把价格序列换成“今天的价格相对于昨天的变化量”再训练预测完再累加回原值diff_series price[1:] - price[:-1] # 用diff_series训练LSTM得到pred_diff后恢复价格 pred_price np.cumsum(pred_diff) price[0]二是把预测步数加大到forecast_step3或7逼模型去看更远的未来削弱近邻观测的支配作用。三是去掉输入窗口里最近的1到2个观测值让模型不能直接“抄”昨天的价格只能依赖更早的模式做推断。三种方法可以并行试看验证集上谁先摆脱滞后。5.4 hidden state的维度错乱和黑匣子问题现象一训练就报错常见报错形如“Expected hidden size (1, 64, 1)got (1, 1, 64)”或者loss卡在某个值附近纹丝不动怎么看都是个黑匣子。原因nn.LSTM在默认情况下自动使用全零隐状态但如果你手动传入h0、c0维度必须严格是(num_layers * num_directions, batch, hidden_size)。很多人按(batch, hidden_size)传入维度直接错位还有人忘记在每次batch迭代后重置h0和c0导致隐状态从上一个batch带过来状态越攒越乱。解决初学者最省事的办法是不传h0和c0让PyTorch每次forward自动初始化全零隐状态。如果必须用双向LSTM或者做状态复用先打印h0.shape和输入x.shape逐一对齐维度再接进forward。模型定义里的batch_firstTrue管的是输入输出维度不影响h0的维度约定这是最容易混淆的地方。5.5 最后一个epoch的模型不等于最优模型现象训练跑了100轮最后20轮验证loss已经开始反弹加载保存的模型回去预测效果却很差曲线明显过拟合。原因保存模型的位置放错了用的是训练循环结束后model的状态而不是验证loss最低时刻的快照。深度学习中普遍存在过拟合拐点末尾epoch的模型往往已经不是最优解。解决把torch.save(model.state_dict(), best_lstm.pt)放进每个epoch验证后的条件分支里只有val_loss best_val_loss才保存。同时打印出“当前第几轮、历史最优val_loss是多少”方便分析最优模型出现在第几轮。早停本身就是疫情前的老牌正则化手段在价格预测这种小数据集任务里比堆dropout更有效。6. 评估的最后一公里拿naive baseline当镜子别让答辩老师问倒6.1 一个自带评估的验证函数RMSE、MAE、MAPE和naive baseline模型训练完不要急着出图先跑一个评估函数把预测误差量化。三个常用指标里MAPE用百分比表示平均误差答辩时最直观——“平均误差3.8%”比“RMSE是0.12”好理解得多。但真正能给模型预测能力定性的是和一个朴素baseline的对比。import numpy as np from sklearn.metrics import mean_absolute_error, mean_squared_error def evaluate(pred_price, real_price): pred_price np.asarray(pred_price).flatten() real_price np.asarray(real_price).flatten() rmse float(np.sqrt(mean_squared_error(real_price, pred_price))) mae float(mean_absolute_error(real_price, pred_price)) mape float(np.mean(np.abs((real_price - pred_price) / real_price)) * 100) return {rmse: rmse, mae: mae, mape: mape} # naive baseline: 用昨天的真实价格预测今天 naive_pred real_price[:-1] naive_rmse float(np.sqrt(mean_squared_error(real_price[1:], naive_pred))) print(evaluate(pred_price, real_price), naive rmse:, naive_rmse)这个baseline代表的是“不学任何模型也能做到的预测水平”。菜价日度变化率通常不大如果LSTM的RMSE只比naive低几个百分点说明模型学到的规律有限该回头检查窗口长度和特征而不是急着堆层。在本人做过的价格类项目里LSTM相比naive的优势通常在10%到30%之间低于10%基本可以认为模型没有有效捕获周期信息。答辩时如果只报LSTM自己的误差指标而不提baseline老师一问“那直接用昨天价格预测呢”容易当场卡壳。6.2 从单步预测走向滚动多步注意误差累积毕设里只做“预测明天”往往显得单薄答辩老师通常会追问“能不能预测未来七天”。滚动预测是常见做法用今天预测明天的结果把它拼到输入序列末尾再丢掉序列最早一天用这个新窗口预测后天如此循环推完一周。def rolling_predict(model, init_seq, steps, scaler): model.eval() preds [] current torch.tensor(init_seq, dtypetorch.float32).unsqueeze(0) # (1, look_back, 1) with torch.no_grad(): for _ in range(steps): out model(current) # (1, 1) p scaler.inverse_transform(out.numpy()).flatten()[0] preds.append(p) next_scaled out.numpy().reshape(1, -1) next_val scaler.transform(next_scaled).flatten()[0] current torch.cat([ current[:, 1:, :], torch.tensor([[[next_val]]], dtypetorch.float32) ], dim1) return preds滚动预测有两个坑需要提前知道。一是误差随步数累积预测第7天的RMSE通常比第1天高出一倍这是正常现象不是模型坏了二是每次拼入输入序列的都是预测值预测误差会传导给后续所有天所以滚动超过14天时结果更适合展示为“价格区间”而不是“精确值”。最后说一个习惯训练结束后第一件事永远是画一张图把真实价格、LSTM预测、naive baseline三条线叠在一起。看图比看指标快得多——滞后一拍、周期偏转、节假日脉冲被抹平一眼全暴露。这套流程做过很多轮凡是验证集上LSTM输给naive的项目几乎都能在图上找到数据清洗或窗口设置的硬伤。把三条线画清楚再谈调参方向不会跑偏。希望帮到你。本文还有配套的精品资源点击获取
返回列表