尧图精选

蔬菜价格预测LSTM项目实战:从爬虫到Flask展示的时间序列全流程

🕒 发布时间:2026/10/1 3:31:51 📁 来源:尧图网络
简介基于深度学习LSTM的蔬菜价格预测项目面向希望掌握时间序列预测完整流程的Python学习者、数据挖掘初学者及农产品价格分析爱好者。资源以真实蔬菜价格与天气数据为基础覆盖爬虫采集、数据清洗、多种预测方式对比、Flask可视化及微信公众号查询展示等环节可帮助读者理解LSTM在价格预测中的落地应用。压缩包共181个文件包含142个蔬菜价格csv数据集、25个Python源码、2个docx项目说明文档及少量运行缓存与配置整体仅1.78MB轻量便于下载与二次开发。代码使用scrapy、pandas、sklearn、statsmodels、flask、pymongo等主流库结构清晰适合结合项目说明文档逐模块学习。目前已有251人浏览学习该资源对入门深度学习时序建模与Web展示具有较高参考价值。1. 蔬菜价格预测的 LSTM 项目这份 python 源码把全流程都串起来了做蔬菜价格预测的人一开始多半以为自己缺的是模型拿到这份基于深度学习 LSTM 的 python 源码就会发现真正缺的是从数据爬取、清洗到预测、再到 web 展示的整条链路。项目里带了本地菜心、云南小瓜、西红柿等九个品种的 CSV 数据集爬虫脚本、预测脚本、Flask 展示代码和公众号查询方案一应俱全不是那种只有一个 jupyter notebook 的玩具 demo。适合两类人一是想用 LSTM 做时间序列预测但卡在数据预处理上的新手二是做课程设计或毕业设计、需要完整项目素材的开发者。这份资源能直接回答一个实际问题明天这筐菜心大概能卖到什么价。2. 认识这份资源九个 CSV、四段流程、两层技术栈2.1 数据集长什么样先看 CSV 再谈建模下载解压后数据集目录里是九个 CSV 文件本地菜心、云南小瓜、小塘白菜、本地芹菜、矮脚白菜、青皮冬瓜、西红柿、红尖椒等。这里有个细节目录里矮脚白菜.csv 出现了重复这在实际爬虫项目里非常常见处理的时候直接按文件名去重就行不然 pandas 读进来会重复计算。每个 CSV 一般是一列日期加一列价格项目说明里提到还爬了天气情况所以可能会有温度、降雨之类的字段具体列名以你打开的为准。拿到 CSV 先别急着训练用 pandas 看一眼结构和缺失值import pandas as pd df pd.read_csv(本地菜心.csv, encodingutf-8, parse_dates[date]) print(df.head()) print(df.info()) print(df.isnull().sum())第一行把 date 列解析成 datetime 类型后面画图、按时间排序都会用到。df.info()看每列的非空数量和数据类型isnull().sum()统计缺失值。很多 CSV 是从网页直接存的列名可能是中文也可能是英文缩写先确认字段名再写后面的处理逻辑比盲猜省事得多。提示如果 CSV 是 GBK 编码read_csv会报解码错误把encoding换成gbk即可。这个坑后面避坑章节还会细说。2.2 四段流程与工具选型资源包里的完整技术栈项目说明里写得很清楚整个系统分四条线爬虫、数据处理、预测、web 展示。我把各部分对应的库整理成一张表方便你对照自己的技术栈阶段对应库产出物爬虫scrapy, beautifulsoup蔬菜价格、天气数据存入本地 CSV 或 MongoDB数据处理pandas, numpy清洗后的时间序列 DataFrame预测sklearn, matplotlib, statsmodels, pyflux, datetimeLSTM 模型、ARIMA 对照实验、预测曲线web 展示flask, matplotlib预测数据的网页展示数据库pymongo爬取数据的持久化存储选择这套技术栈的逻辑不复杂scrapy 负责批量抓取beautifulsoup 做页面解析pandas 处理数据statsmodels 提供 ARIMA 作为传统时间序列的对照pyflux 可以跑更复杂的贝叶斯时间序列模型flask 则是轻量 web 框架跟 matplotlib 配合可以直接把预测图渲染到网页上。pymongo 把爬到的原始数据存进 MongoDB方便后续清洗和回溯。这个组合的优点是每个环节都有成熟方案缺点是你得在 pandas 和 sklearn 之间来回切换数据类型初学者容易在 DataFrame 和 numpy array 的转换上卡住。2.3 为什么最终落到 LSTM序列依赖才是价格预测的关键蔬菜价格跟股票价格类似今天的价格对明天的价格有强影响这叫序列自相关。普通的前馈神经网络把人每天的价格当独立样本处理学不到这种先后关系。LSTM 是循环神经网络的一种门控机制让信息可以在时间步之间传递既能记住一周前菜价高企的行情也能对突发的价格跳动做出反应。项目中同时用 statsmodels 跑 ARIMA 做对比ARIMA 对线性序列拟合很好但蔬菜价格受天气、节假日、供应量影响非线性成分多LSTM 在这种场景下通常表现更好。这也是项目把 LSTM 作为主模型而不是 ARIMA 的原因。3. 把价格序列喂给 LSTM归一化、滑窗切分与模型搭建3.1 数据重构把一列价格变成监督学习的样本对LSTM 不是拿一整列价格直接训练的它需要把序列改造成「用过去 N 天的价格预测明天价格」的样本对。这一步如果做错后面调什么都白搭。先做清洗按时间排序、去重、处理缺失值。import numpy as np import pandas as pd df pd.read_csv(本地菜心.csv, parse_dates[date]) df df.sort_values(date) # 时间序列必须按时间排序 df df.drop_duplicates(subset[date]) # 同一日期多条记录取第一条 df df.reset_index(dropTrue) # 缺失价格优先用前一天价格填充连续缺失超过3天则删除该段 df[price] df[price].fillna(methodffill, limit3) df df.dropna(subset[price])sort_values(date)保证时间递增LSTM 对乱序数据极其敏感顺序错了模型学到的是噪音。drop_duplicates保留每个日期第一条记录ffill的limit3限制最多向后填充三天超过三天说明数据断档太久直接删掉比硬填更保险。这里有个常见误用有人喜欢用均值填充所有缺失值这在时间序列里是灾难因为它抹掉了价格随时间的波动信息。3.2 归一化与滑窗切分影响最终效果的隐藏参数归一化几乎是 LSTM 训练的必选项。价格序列的数值范围可能是 1 到 20 元而 LSTM 内部激活函数对输入范围敏感不归一化会导致梯度不稳定。项目用的是 sklearn 的 MinMaxScaler把价格映射到 0 到 1 之间from sklearn.preprocessing import MinMaxScaler scaler MinMaxScaler(feature_range(0, 1)) prices df[price].values.reshape(-1, 1) scaled_prices scaler.fit_transform(prices) def create_dataset(data, lookback7): X, y [], [] for i in range(len(data) - lookback): X.append(data[i:ilookback, 0]) y.append(data[ilookback, 0]) return np.array(X), np.array(y) lookback 7 X, y create_dataset(scaled_prices, lookback) X X.reshape(X.shape[0], X.shape[1], 1)feature_range(0, 1)是常见选择不要轻易改成别的0-1 区间对 LSTM 的 tanh 激活函数最友好。lookback7表示用过去 7 天的价格预测第 8 天的价格这个参数的语义是「一周的行情记忆」对于蔬菜这种短周期农产品7 天是合理的起步值。如果你要预测的是冬瓜这种存放周期长的品种可以把 lookback 调到 14菜心这种叶菜价格波动快5 到 7 天就够了。最后reshape(X.shape[0], X.shape[1], 1)是 LSTM 输入的标准三维格式样本数、时间步长、特征数。这里特征只有价格一列所以第三维是 1如果后面把天气加入特征第三维要改成特征总数。注意fit_transform必须在切分训练集和测试集之前对整个序列做一次。如果先切分再分别归一化测试集的分布信息会丢失预测结果会失真。这个叫数据泄漏避坑章节单独讲。3.3 模型结构层数、神经元数与 Dropout 的位置这个项目里的 LSTM 模型比较克制没有堆很多层因为蔬菜价格数据的样本量通常只有几百到几千条模型太大反而过拟合。我按照项目中常见写法给出模型搭建代码from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.optimizers import Adam model Sequential() model.add(LSTM(units50, return_sequencesTrue, input_shape(lookback, 1))) model.add(Dropout(0.2)) model.add(LSTM(units50, return_sequencesFalse)) model.add(Dropout(0.2)) model.add(Dense(units1)) model.compile(optimizerAdam(learning_rate0.001), lossmse, metrics[mae]) model.summary()两层 LSTM 叠加能捕捉更高层的时间特征第一层return_sequencesTrue是为了把完整的时间步输出传给第二层第二层return_sequencesFalse只输出最后一个时间步的结果。units50是隐层维度对于单变量价格预测 50 足够大再往上加收益递减。Dropout 放在 LSTM 层之后而不是之前这是 Keras 的标准做法0.2表示随机丢弃 20% 的神经元连接防止模型死记训练集的价格曲线。损失函数用mse因为价格预测是回归任务mae作为辅助指标方便直观读数。4. 训练与预测loss 曲线、多步预测与基线对比4.1 训练参数与早停策略怎么判断模型训好了训练本身没什么玄学关键在于观察 loss 曲线判断模型状态。数据集按时间顺序切分前 80% 做训练、后 20% 做测试这是时间序列的标准切法。训练时加上 EarlyStopping防止在验证集上持续过拟合from tensorflow.keras.callbacks import EarlyStopping split_idx int(len(X) * 0.8) X_train, X_test X[:split_idx], X[split_idx:] y_train, y_test y[:split_idx], y[split_idx:] early_stop EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue) history model.fit( X_train, y_train, validation_data(X_test, y_test), epochs100, batch_size32, callbacks[early_stop], verbose1 )split_idx取前 80% 样本做训练注意这里没有 shuffle时间序列一旦打乱模型就学到了未来信息测试集上的指标会虚高。patience10表示验证集 loss 连续 10 个 epoch 不下降就停止训练restore_best_weightsTrue把权重回滚到验证集最优的那一轮。batch_size 设为 32这是时间序列任务里的通用选择如果你的数据量很小可以降到 16。训练完成后把 loss 曲线画出来看看import matplotlib.pyplot as plt plt.plot(history.history[loss], labeltrain_loss) plt.plot(history.history[val_loss], labelval_loss) plt.legend() plt.xlabel(epoch) plt.ylabel(loss) plt.title(LSTM Training Loss) plt.show()两个关键判断训练 loss 和验证 loss 同时下降说明模型正常学习如果训练 loss 一路走低而验证 loss 先降后升说明过拟合开始早停已经介入。如果训练 loss 从一开始就不降问题大概率不在模型而在数据——去检查归一化是否做了、lookback 是否合适。4.2 预测与反归一化还原出真实价格才能算误差模型输出的是 0 到 1 之间的归一化值要做逆变换还原成真实价格然后才能算 RMSE、画对比图。多步预测的做法是用第 7 天的预测值作为新输入滚动预测第 8 天、第 9 天from sklearn.metrics import mean_squared_error, mean_absolute_error 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)) rmse np.sqrt(mean_squared_error(y_true, y_pred)) mae mean_absolute_error(y_true, y_pred) print(fRMSE: {rmse:.3f} 元, MAE: {mae:.3f} 元) plt.figure(figsize(12, 5)) plt.plot(y_true, label真实价格) plt.plot(y_pred, labelLSTM 预测) plt.legend() plt.title(蔬菜价格预测结果对比) plt.show()inverse_transform是做 MinMaxScaler 的逆操作把预测值还原为元。RMSE 和 MAE 的单位都是元比如 RMSE 是 0.8意味着平均偏差 8 毛钱对叶菜这种单价两三块的品种来说已经算不错如果是冬瓜这种大个头、单价一两块的品种0.8 就是不可接受的。对比图能直观看出模型是跟上了走势还是整体滞后常见的现象是预测曲线比真实曲线晚一天——这本质是 LSTM 在学到趋势和波动之间的折中后面进阶章节会讲怎么缓解。4.3 与 ARIMA 对照为什么项目里要同时跑 statsmodels项目里装了 statsmodels 和 pyflux目的就是做对照实验。ARIMA 是传统时间序列模型的代表先对价格序列做差分使其平稳再拟合自回归和移动平均项。你在写实验报告或毕设论文时必须有这个对照组否则无法证明 LSTM 的优势。常见做法是直接statsmodels.tsa.arima.model.ARIMA拟合order 参数用(5,1,0)起步然后拿同样的测试集计算 RMSE。大部分情况下蔬菜价格这种非线性波动数据上LSTM 的 RMSE 比 ARIMA 低 10% 到 30%。但如果你的数据量特别小比如只有 100 条ARIMA 反而可能更稳——LSTM 是需要大数据量喂出来的模型样本太少它就学不到有效规律。这是做对比实验最有价值的发现也是项目里保留两种方案的原因。5. 避坑指南五个不解决就翻车的问题5.1 归一化时的数据泄漏预测结果好得不真实现象测试集 RMSE 极低低到 0.1 元以内但拿真实新数据一测预测完全不准。原因对全量数据做fit_transform后再切分训练集和测试集测试集的 min 和 max 已经参与了缩放计算相当于模型提前知道了测试集的取值范围。解决先切分再在训练集上fitMinMaxScaler然后分别transform训练集和测试集。注意测试集的归一化用的是训练集的 scaler不能重新 fit。5.2 时间序列被打乱验证集指标虚高现象训练时验证 loss 很低但画出预测曲线发现完全对不上时间轴。原因切分数据时用了train_test_split默认的随机切分价格序列被 shuffle模型用未来数据预测过去数据验证指标失去意义。解决时间序列永远按时间顺序切分用索引切分或者train_test_split(shuffleFalse)。这是新手最容易踩的坑也是面试官最爱问的细节。5.3 中文文件名与编码问题pandas 读不进 CSV现象pd.read_csv(本地菜心.csv)报 UnicodeDecodeError或者读进来列名全是乱码。原因爬虫保存的 CSV 可能是 GBK 或 GB2312 编码而 pandas 默认用 UTF-8 读取。解决读取时指定encodinggbk如果还报错用encodinggb18030它对生僻字兼容性更好。最快的排查方式是用记事本打开 CSV 看右下角编码格式。5.4 预测曲线是一条平直线模型根本没学会现象预测值几乎是一条水平线RMSE 比直接拿昨天价格当预测值还差。原因lookback 设置过小导致上下文不足或者归一化后价格波动被压缩得太厉害LSTM 学到的只是均值回归。解决把 lookback 从 7 调到 14同时检查归一化后的数据分布——如果 min 和 max 差距极小说明原始数据可能有大量重复值或异常离群值没处理干净。先画原始价格曲线确认数据有肉眼可见的波动再训练。5.5 小数据集上 LSTM 输给 ARIMA不是模型不行现象数据只有 100 多条跑完对比实验发现 LSTM 的 RMSE 比 ARIMA 还高。原因LSTM 是数据饥渴型模型样本量不足时学到的规律不如 ARIMA 这种参数少的经典模型。解决有两种处理方向。一是加大 lookback 来增加样本构造时的上下文长度但样本量不变二是利用项目里爬取的天气特征把天气作为额外输入特征加入模型信息量增加后 LSTM 的优势才能发挥。项目说明中强调了天气数据的采集就是为了这一步。6. 进阶把玩把 LSTM 模型接进 Flask 接口与公众号查询模型训练完只是第一步项目里真正加分的部分是 web 端和公众号端。用 Flask 封装一个预测接口是常见做法核心技巧有三个。第一用joblib.dump同时保存训练好的模型和 MinMaxScaler推理时一起加载避免现场重新归一化import joblib joblib.dump(model, lstm_price_model.pkl) joblib.dump(scaler, price_scaler.pkl)第二Flask 接口入参一定要做校验。品种名称和日期格式是必检字段品种决定了用哪份 CSV 对应的模型权重。接口每次只接受一个品种加一个目标日期返回该日期的归一化预测值并反归一化成元。第三模型加载放在模块顶层不要写在每次请求里否则高并发下内存会被重复加载的模型占满。公众号那端用 PHP 的原因在于公众号接口的签名校验和 token 换取 PHP 生态最成熟Python 端只需要把 Flask 服务部署到公网服务器PHP 后台通过 HTTP 请求调用预测接口把返回的价格数据渲染成公众号菜单页面。这套架构的边界在于Flask 只负责推理PHP 负责微信侧的协议对接两者通过 JSON 通信职责清晰也方便以后替换任一端的实现。从那以后我每次拿到这类时序预测项目都会强制走一遍「数据排序、去重、归一化、按时间切分」四步检查再讨论模型结构。这四步看似基础实际是决定预测曲线是贴着真值走还是原地画直线的分水岭。这篇笔记里的参数都是可以直接复现的基线值你拿到源码后优先替换成自己的蔬菜价格数据跑一遍再按避坑章节逐个排查基本就能在半天内出第一版结果。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →