资讯动态

LSTM时间序列预测实战:空气质量数据预处理与模型训练全流程

发布时间:2026/10/18 21:26:42 来源:尧图企业网站定制
简介基于LSTM的时间序列预测Python项目包含完整源码与全部数据定位为期末大作业、课程设计及入门深度学习的高分参考。项目以空气污染监测数据为实例完整覆盖时间序列预测的各个环节包括数据读取与清洗、缺失值填充、归一化、滑动窗口构造、LSTM模型搭建、训练验证、误差评估及预测曲线绘制代码注释细致逻辑清晰简单配置环境即可运行并可迁移到金融、气象、能耗等时序场景。压缩包共129个文件以78个py源码脚本为主体按数据预处理、模型构建、训练评估等模块组织配套26个csv数据文件、16个txt说明/日志文件另有h5模型权重与checkpoint训练断点等产物整体大小仅5.42MB文件划分清晰便于按模块查阅。项目自带多份观测数据可直接用于对比实验提升结题报告的完整性。目前已有169人浏览学习尤其适合需要快速完成高质量大作业或从零理解LSTM时序预测流程的开发者。1. 基于LSTM的时间序列预测能跑通、能看懂、能交付的资源包期末大作业最怕的不是模型训练不出来而是训练出来之后老师一问“你这预测的原理是什么、参数为什么这么设、数据是怎么喂进去的”当场卡壳。这份基于LSTM的时间序列预测资源源码和数据是配齐的空气质量污染数据有两份原始文件加一份清洗后的新文件checkpoint 文件夹里还有训练过程中保存的模型权重。也就是说下载之后不需要自己重新训练就能先看到预测效果再对照代码把数据处理、模型搭建、训练保存这一整条链路啃下来。适合正在做期末大作业、课程设计或者第一次接触 LSTM 想拿真实数据练手的开发者——不需要懂复杂的数学推导能跟着代码走通流程就能在答辩时说清楚每一个环节的取舍。2. 数据与预处理pollution.csv 多变量序列如何变成监督学习样本2.1 数据文件里到底有什么先把资源里的文件过一遍。pollution.csv 和 air_pollution.csv 是原始的多变量空气质量数据字段包含时间、污染浓度指标以及若干气象特征属于典型的多变量时间序列。air_pollution_new.csv 是清洗后的版本通常已经完成了缺失值处理时间列也做了规范化适合直接喂给模型。checkpoint 文件夹里存的是训练中按 epoch 保存的权重文件格式是 TensorFlow 的 .index 和 .data 组合后面加载模型时会用到。拿到这种资源第一步不是急着跑模型而是先用 pandas 把数据读进来看结构、看缺失、看时间跨度。import pandas as pd df pd.read_csv(pollution.csv, parse_dates[date], index_coldate) print(df.head()) print(df.shape) print(df.isnull().sum())逻辑说明parse_dates 把日期列解析成 datetime 类型index_col 把日期设为索引便于后续按时间切片。isnull().sum() 统计每一列的缺失数量决定后面用删除还是填充。参数说明如果原始数据的时间列名不是 date需要改成实际列名如果读取后时间索引是乱的加一步 df.sort_index()。这一步不做好后面构造滑窗序列时时间顺序会错位预测结果全偏。2.2 缺失值处理与归一化LSTM 对输入数据的尺度非常敏感因为激活函数默认 tanh 和 sigmoid的敏感区间在 0 附近原始污染数据动不动就是几百的数值直接喂进去会让梯度更新不稳定。所以归一化是必须的不是可选项。from sklearn.preprocessing import MinMaxScaler # 按列填充缺失值用前向填充 df.fillna(methodffill, inplaceTrue) df.dropna(inplaceTrue) # 选择特征列这里取全部数值列 values df.values.astype(float32) scaler MinMaxScaler(feature_range(0, 1)) scaled scaler.fit_transform(values)逻辑说明ffill 用上一个有效值填充缺失适合时间序列场景比均值填充更符合数据连续性。MinMaxScaler 把每列压缩到 0~1 区间fit_transform 先拟合再转换。注意fit 只能用训练数据做不能在整个数据集上 fit 完再切训练测试集否则会有数据泄漏。参数说明如果序列有长时间缺失ffill 会造成一段平坦区LSTM 会在这一段学习到“输出等于上一个值”严重影响预测。遇到这种数据我一般会把连续缺失超过 5 个时间点的片段直接裁掉。feature_range 也可以试 (0.1, 0.9)让边界留一点余量对 tanh 激活更友好。2.3 滑窗构造把时间序列变成监督学习样本LSTM 不能直接吃一维时间序列它需要的是形如 (样本数, 时间步长, 特征数) 的三维输入。滑窗lookback的思想是用前 N 个时间点的数据预测第 N1 个时间点的目标值。import numpy as np def create_dataset(data, lookback3): X, y [], [] for i in range(len(data) - lookback): X.append(data[i:ilookback, :]) y.append(data[ilookback, :]) return np.array(X), np.array(y) lookback 3 X, y create_dataset(scaled, lookback) print(X.shape, y.shape)逻辑说明循环里 data[i:ilookback, :] 取从 i 开始的 lookback 行、全部特征列作为输入样本data[ilookback, :] 取紧接着的那一行作为目标。滑动窗口每次向后移动一个时间点所以样本总数等于 len(data) - lookback。参数说明lookback 取 3 表示用过去 3 个小时预测下一个小时。这个值直接决定模型能看到多长的历史信息取太小模型学不到趋势取太大训练样本减少、计算量增大。空气质量这种数据我一般先用 3 作为基线再试 5 和 10。如果你想预测的目标不是全部特征而是单一列比如只预测 pm2.5构造时把 y 改成 data[ilookback, target_index] 即可。数据切分要注意顺序问题。时间序列不能随便随机打乱训练集和测试集必须按时间顺序切否则模型会在测试集上“提前见过未来”。常见做法是前 80% 做训练、后 20% 做测试切完后再分别做归一化重拟合。train_size int(len(X) * 0.8) X_train, X_test X[:train_size], X[train_size:] y_train, y_test y[:train_size], y[train_size:] print(X_train.shape, X_test.shape)逻辑说明按索引顺序切分保证训练集时间在前、测试集时间在后。这一步是时间序列预测和普通分类任务的本质区别普通分类可以随机划分时间序列不行。参数说明80/20 是基线比例。如果总数据量很小可以上调到 90/10数据量非常大时也可以留出最后 30% 做验证让测试结果更有说服力。切分之后如果发现测试集落在某个异常时段预测误差会偏大这属于正常现象答辩时能解释清楚就行。3. LSTM 模型构建与训练层参数、损失函数与 checkpoint 策略3.1 为什么选 LSTM 而不是普通神经网络空气质量预测这种数据当前时刻的污染浓度和过去几个小时的变化趋势强相关比如凌晨污染积累、白天扩散这种时间依赖性用普通全连接网络很难捕捉因为它把每个时间点当成独立样本处理。RNN 能保留历史信息但标准 RNN 在长序列上会有梯度消失问题记不住太久之前的信息。LSTM 通过输入门、遗忘门、输出门三个门控结构让信息可以选择性保留或丢弃。在这份资源里数据文件有多变量特征模型要学的是“过去几小时的多个指标如何影响未来值”这种映射关系。LSTM 的记忆单元天然适合这种任务这也是期末大作业选它而不是 ARIMA 或普通 MLP 的原因——ARIMA 对多变量支持弱MLP 没有时间结构。3.2 模型结构与参数设置from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint model Sequential() model.add(LSTM(units50, return_sequencesTrue, input_shape(X_train.shape[1], X_train.shape[2]))) model.add(Dropout(0.2)) model.add(LSTM(units50, return_sequencesFalse)) model.add(Dropout(0.2)) model.add(Dense(unitsX_train.shape[2])) model.compile(optimizeradam, lossmse, metrics[mae]) model.summary()逻辑说明第一层 LSTM 设置了 return_sequencesTrue因为后面还接了一层 LSTM需要把完整的序列输出传给下一层最后一层 LSTM 的 return_sequencesFalse只返回最后一个时间步的输出再接全连接层映射到目标维度。两个 Dropout 层分别跟在 LSTM 层后面随机丢弃 20% 的神经元抑制过拟合。Dense 的输出维度等于特征数因为当前构造的目标是预测下一时刻的全部特征值。参数说明units50 是基线设置比 32 有更强的拟合能力又不会像 128 那样容易在少量数据上过拟合。loss 用 mse 是因为预测值是连续数值均方误差对大误差惩罚更重模型会更关注偏离大的点。metrics 里的 mae 用于训练过程中监控平均绝对误差方便和后面的评估统一口径。如果你想只预测单一目标列Dense 输出改成 1y 也要对应改成单列。3.3 训练、早停与模型保存checkpoint ModelCheckpoint( checkpoint/lstm_model.keras, monitorval_loss, save_best_onlyTrue, verbose1 ) early_stop EarlyStopping( monitorval_loss, patience10, restore_best_weightsTrue ) history model.fit( X_train, y_train, validation_split0.1, epochs100, batch_size32, callbacks[checkpoint, early_stop], verbose1 )逻辑说明ModelCheckpoint 在每轮 epoch 结束后检查验证集损失如果比之前最好的结果更好就覆盖保存模型文件。EarlyStopping 在连续 10 轮验证损失都没有下降时终止训练并恢复到验证损失最小的那轮权重。这两个回调组合起来既能防止训练时间过长又能保证最后保存的是最优模型而不是最后一轮模型。参数说明validation_split0.1 表示从训练数据尾部切 10% 做验证集注意这里是在已经切分过的训练集基础上再切不会破坏前面的时间顺序。batch_size32 是显存和训练速度的平衡点数据量小可以降到 16。patience10 给模型 10 次“试探”机会太小容易在损失局部波动时提前停止太大会浪费时间。epochs100 配合早停实际跑多少轮取决于数据一般 30~50 轮就能收敛。训练完成后checkpoint/lstm_model.keras 里就是最优权重的模型文件。这份资源自带 checkpoint 文件夹的好处是你可以跳过训练直接加载权重看预测效果也可以重新训练覆盖旧权重——两种路径都能验证整个流程。4. 预测结果还原与评估反归一化、RMSE 与绘图对照4.1 预测值必须反归一化模型输出的是 0~1 区间的归一化值直接拿来和原始浓度对比没有意义。必须用之前拟合好的 scaler 的 inverse_transform 把预测值和真实值还原到原始量纲。y_pred model.predict(X_test) y_pred_orig scaler.inverse_transform(y_pred) y_test_orig scaler.inverse_transform(y_test)逻辑说明scaler 在训练前用整份数据 fit 过inverse_transform 按列还原到原始数值范围。这里要求 X_test 和 y_test 的特征排列顺序和训练数据完全一致否则还原结果错位。参数说明如果前面构造数据集时只预测单一目标列反归一化就不能直接用整列的 scaler要单独对目标列做 scaler 的 inverse_transform或者把预测值拼到其他特征的均值再整体还原取目标列。这个细节容易忽略很多同学直接对全量特征归一化后又只取单列预测结果还原出来完全不对。4.2 评估指标计算from sklearn.metrics import mean_squared_error, mean_absolute_error import numpy as np rmse np.sqrt(mean_squared_error(y_test_orig[:, 0], y_pred_orig[:, 0])) mae mean_absolute_error(y_test_orig[:, 0], y_pred_orig[:, 0]) print(fRMSE: {rmse:.2f}, MAE: {mae:.2f})逻辑说明这里取了第 0 列做单指标评估通常是主污染浓度指标。RMSE 对大误差敏感能反映预测是否有严重偏离MAE 更稳健反映平均偏差水平。两个指标一起看如果 RMSE 远大于 MAE说明存在少数预测误差非常大的点。参数说明评估指标要根据任务选如果是期末答辩RMSE 和 MAE 两个就够。老师如果问“这个误差水平能不能接受”要结合数据本身的量级回答——比如原始浓度平均在 100 左右RMSE 30 就是可解释的误差水平如果平均浓度才 20RMSE 30 就说明模型基本没学到东西。4.3 预测曲线与真实曲线可视化import matplotlib.pyplot as plt plt.figure(figsize(12, 5)) plt.plot(y_test_orig[:, 0], labelReal, linewidth1.5) plt.plot(y_pred_orig[:, 0], labelPredicted, linewidth1.5) plt.legend() plt.title(LSTM Air Quality Prediction) plt.xlabel(Time Steps) plt.ylabel(Pollution Concentration) plt.show()逻辑说明用两条曲线对比真实值和预测值走势重合度直接反映模型效果。如果预测曲线滞后真实曲线一个时间步说明模型学到的主要是“下一个值等于当前值”的恒等映射这是时间序列预测最常见的问题。参数说明plt.figure 的 figsize 按论文要求调整答辩展示建议 (12, 5) 以上保证清晰。如果预测的是多个特征可以循环画多个子图每个特征一张对比图。绘图这一步是期末作业的加分项比贴一堆数值指标更直观。老师看到曲线重合度高、训练损失曲线平滑下降整个作业的完成度就立住了。5. 常见问题与避坑五个让期末大作业翻车的细节5.1 预测曲线整体滞后一个时间步现象预测曲线形状和真实曲线几乎一样但整体向右平移了一个时间点RMSE 数值看着还行但图上一眼就能看出错位。原因模型学到的最优策略是“当前值 ≈ 上一时刻值”。当 lookback 太小、序列本身没有明显趋势时预测均值回归到上一个观测值这在数学上确实是低误差策略但没有实际预测价值。解决增大 lookback让模型依赖更多历史信息同时检查数据是否真的存在时间依赖如果序列接近随机游走LSTM 能做的就是滞后预测。答辩时主动说明这一点反而显得对模型行为有深入理解。5.2 归一化泄漏导致测试集指标虚高现象测试集上 RMSE 低得离谱但换一批数据重新跑就完全不准。模型表现出“记忆测试集”的倾向。原因在切分训练测试集之前就对全量数据做了 scaler.fit_transformscaler 已经看过测试集的统计信息等于测试数据信息提前进入了训练环节。解决先切分再对训练集 fit然后用训练集的 scaler 转换测试集。也就是 scaler.fit(X_train_raw) 之后再分别 transform 训练集和测试集。这是时间序列预测最容易踩的坑没有之一。5.3 每个 epoch 的损失曲线剧烈震荡现象训练时 loss 忽高忽低前 10 轮不降反升后面的曲线像锯齿一样。原因学习率太大导致梯度更新在最优解附近来回振荡或者数据没有做归一化损失曲面过于陡峭。batch_size 太小也会让梯度估计噪声增大。解决把默认学习率从 0.001 降到 0.0005或者换用更稳定的优化器设置确认归一化步骤真实生效打印一下 scaled 数组的最大最小值应该在 0~1 之间batch_size 提到 64 试试很多数据集上反而比 32 收敛更稳。5.4 checkpoint 加载后预测结果全是同一个值现象加载保存的模型后预测输出几乎恒定所有预测值都接近某个固定数。原因加载模型时输入形状和保存时不匹配或者模型是从头重新定义的但没有 rebuild input shape。另一种常见情况是加载的权重来自训练初期模型还没收敛。解决在预测前用 model.build(input_shape) 重建模型输入形状或者直接 model.predict 前打印 model.summary() 检查输入维度。再确认加载的文件是 save_best_only 保存的最优权重而不是最后一次保存的高 loss 权重。5.5 每次运行结果都不一样无法复现现象同一个文件、同一个模型两次运行预测结果有差异连 loss 曲线都不同。原因神经网络初始化随机、训练数据顺序随机、Dropout 随机丢弃这些随机性没有固定种子每次运行都会产生新的随机序列。解决在代码开头固定随机种子TensorFlow 和 NumPy 都要设置。import random import numpy as np import tensorflow as tf random.seed(42) np.random.seed(42) tf.random.set_seed(42)逻辑说明固定三个库的随机种子保证训练过程的随机数序列一致。这样即使有 Dropout随机丢弃的模式也是可复现的。参数说明种子值选什么都可以只要全代码一致。如果用了 GPU还需要在代码开头设置环境变量 TF_DETERMINISTIC_OPS1否则不同硬件设备上的结果仍然有细微差异。6. 进阶用法多步预测与超参数验证的小技巧前面的流程默认是“用过去 3 小时预测未来 1 小时”的单步预测这在学校作业里通常够用了。但如果想让项目再往上走一个台阶可以试试多步预测——用过去 N 步预测未来 M 步这更贴近真实场景也在答辩时更有讨论空间。常见做法有两种递归预测和直接预测。递归预测是把模型的单步输出拼回输入窗口再预测下一步代码改动小但误差会逐步累积预测越远越偏。直接预测是把 Dense 层的输出维度改成 M一次输出未来 M 个时间点训练时 y 的构造需要对应改成“从 ilookback 到 ilookbackM-1”的区间误差不累积但训练样本构造更复杂。我做这类作业的习惯是先跑通单步预测确认数据和模型链路都没问题再尝试递归多步。原因很简单如果单步预测的滞后问题都没解决多步预测只会把误差放大得更严重。超参数验证方面有一个成本很低的顺序。先固定 lookback3、units50跑一遍拿到基线 RMSE。然后只改 lookback 试 5 和 10记录指标变化。再固定表现最好的 lookback把 units 改成 64 和 128看模型容量增加带来的收益是否值得训练时间的增加。最后微调 dropout 从 0.2 到 0.3。每次只改一个参数否则两个参数同时变动你根本不知道是哪一个起了作用。# 递归多步预测示例代码 def recursive_predict(model, last_window, steps5): predictions [] current_input last_window.reshape(1, last_window.shape[0], last_window.shape[1]) for _ in range(steps): next_val model.predict(current_input, verbose0) predictions.append(next_val[0]) current_input np.concatenate([current_input[:, 1:, :], next_val[:, np.newaxis, :]], axis1) return np.array(predictions)逻辑说明每次预测出下一个时间点的所有特征然后把它拼接到输入窗口末尾同时丢掉窗口最前面的一个时间点窗口长度保持不变。concat 的 axis1 是时间步维度表示向后滑动一个时间步。参数说明steps 决定预测多远建议先从 5 步开始超过 10 步递归误差会明显累积。这套资源的 checkpoint 文件对调试特别有用。训练一次之后后面每次调整评估代码都可以直接加载保存好的权重不用重新训练几十轮。从那以后我每次做这类多变量序列预测都会强制自己在写模型之前先把数据检查和归一化泄漏排查一遍这两个地方出问题后面所有环节都会跟着翻车。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑