尧图精选

LSTM空气质量预测与可视化分析:Python实战教程

🕒 发布时间:2026/10/1 17:46:33 📁 来源:尧图网络
简介这是一套基于长短期记忆网络LSTM的空气质量数据预测与可视化分析系统采用Python编程语言实现面向计算机相关专业的高阶课程实践、毕业设计及机器学习入门人群。系统覆盖数据预处理、异常值清洗、归一化、多层LSTM网络构建、全连接输出与Matplotlib/Seaborn可视化等完整流程并经学术导师评审综合评分99分均可复现污染物浓度趋势预测与预测精度变化曲线。压缩包共312个文件约6.98MB包含Python源码.py、.pyc、数据表格.csv、.sqlite3、Web可视化页面.html、.css、.js、样式表.scss等目录结构划分清晰便于按模块查阅。目前已有58人学习下载。使用者可依据附带的环境配置说明与示例数据集直接部署运行或调整超参数、优化器进一步探索不同空气质量预警场景适合作为课程作业、毕业设计及项目实训的重要参考资料。1. 这个空气质量预测系统值得用LSTM做吗环境监测每小时产生一条记录PM2.5、PM10、NO2 这些数据堆了大半年却只在月底统计一下均值这大概是很多环境数据从业者手头的真实状态。基于 LSTM 的空气质量预测与可视化分析系统Python 实现要解决的就是把“沉睡的监测记录”变成“未来 24 小时的浓度趋势”用过去几十个小时的多污染物序列预测下一小时甚至未来几小时的 PM2.5 浓度再把预测结果画成能直接放进日报的趋势图。这套方案适合手里有历史监测数据、想预测但没搭过深度学习流程的人也适合做设备寿命预测、传感器数据时序建模的工程师参考。它的核心不是刷出一个漂亮的 R²而是把数据清洗、LSTM 建模、结果反标准化、可视化验证这一整条链路跑通。2. 数据准备从原始监测 CSV 到可训练的 LSTM 样本LSTM 不直接吃 DataFrame它吃的是形状固定的浮点数张量。数据里有空洞、有飘高值、有量纲不一致这些不处理干净后面模型结构再标准也白搭。这一章按“清洗 → 切分 → 标准化 → 构造窗口”的顺序把原始 CSV 变成可训练样本。2.1 监测数据长什么样字段、时间粒度与缺失值规律空气质量监测站导出的 CSV常见结构是时间列加若干污染物浓度列有的还带气象参数。小时级数据中最常用的字段如下。字段含义典型量纲备注datetime监测时间小时级必须解析成时间索引PM25细颗粒物μg/m³预测目标一般选它PM10可吸入颗粒物μg/m³与PM2.5强相关NO2二氧化氮μg/m³交通源污染指示SO2二氧化硫μg/m³燃煤源指示CO一氧化碳mg/m³燃烧源指示O3臭氧μg/m³夏季光化学污染AQI空气质量指数无量纲由各污染物折算时间粒度建议选小时级。分钟级数据噪声太大模型学到的是传感器抖动而不是污染规律日级数据又太稀一年才 365 条不够喂 LSTM。小时级数据既有日内周期早晚高峰、夜间累积又不会让序列长到训练不动。缺失值在这个场景里很常见。仪器校准会整段缺失通信断点会造成长达几小时的缺口还会偶尔出现浓度突然飙到上千的异常点。常见的错误是先 dropna 再训练这个坑后面单独讲先按稳妥方式处理。import pandas as pd import numpy as np # 读取监测数据datetime 直接解析为索引 df pd.read_csv(air_quality.csv, parse_dates[datetime], index_coldatetime) df df[[PM25, PM10, NO2, SO2, CO, O3, AQI]] # 统计缺失量决定用哪种处理策略 missing_info df.isna().sum() print(missing_info) # 短段缺失前向填充兜底 线性插值平滑 df df.ffill().interpolate(methodlinear, limit_directionboth) # 异常值裁剪中位数 ± 5 倍 MAD避免个别飘高值拖坏训练 mad (df - df.median()).abs().median() lower df.median() - 5 * mad upper df.median() 5 * mad df df.clip(lower, upper)这里没有用均值填充是因为污染物浓度突变很有信息量均值会把突变抹平。也没有直接用 3σ 法则因为空气质量数据是重尾分布高浓度事件本来就有均值容易被极端值带偏MAD 基于中位数更稳。先 ffill 再 interpolate 的原因是线性插值需要两端有值纯 interpolate 在开头结尾会留下 NaNffill 先把边界兜住。注意 if 成段缺失超过几十个小时ffill 会制造一段平台期这种情况应该直接删除这一段并记录时间索引而不是强行补。2.2 先切分再缩放MinMaxScaler 的正确使用顺序很多入门代码先把整个数据集 MinMaxScaler 一下再切分训练验证集这是时间序列预测里最容易踩的泄漏问题。MinMaxScaler 的 min 和 max 来自全量数据等于验证集和测试集的取值范围已经混进训练阶段模型等于提前看过部分未来信息验证指标虚高上线就崩。正确做法是先按时间顺序切分再只在训练集上 fit验证集和测试集只做 transform。另外要单独给预测目标 PM2.5 建一个 scaler因为后面评估时要把预测结果反标准化回真实浓度。from sklearn.preprocessing import MinMaxScaler def split_and_scale(df, train_ratio0.7, val_ratio0.15): values df.values.astype(np.float32) # [T, feature_num] n len(values) n_train int(n * train_ratio) n_val int(n * val_ratio) # 严格按时间顺序切绝不随机打乱 train_raw values[:n_train] val_raw values[n_train:n_train n_val] test_raw values[n_train n_val:] # 特征统一缩放只 fit 训练段 scaler_X MinMaxScaler(feature_range(0, 1)) train_scaled scaler_X.fit_transform(train_raw) val_scaled scaler_X.transform(val_raw) test_scaled scaler_X.transform(test_raw) # 预测目标单独缩放单独保存 scaler_y pm25_idx df.columns.get_loc(PM25) scaler_y MinMaxScaler(feature_range(0, 1)) train_y scaler_y.fit_transform(train_raw[:, pm25_idx].reshape(-1, 1)) val_y scaler_y.transform(val_raw[:, pm25_idx].reshape(-1, 1)) test_y scaler_y.transform(test_raw[:, pm25_idx].reshape(-1, 1)) # 返回特征序列和目标序列以及用于反标准化的 scaler_y return (train_scaled, val_scaled, test_scaled, train_y, val_y, test_y, scaler_y) train_x, val_x, test_x, train_y, val_y, test_y, scaler_y split_and_scale(df)为什么选 MinMaxScaler 而不是 StandardScaler污染物浓度是非负的高浓度重尾会把 Z-score 的均值和方差拉偏MinMax 把数据压到 0~1 之间对 LSTM 这类以 sigmoid/tanh 为内部激活的网络更友好。PM2.5 在低浓度时接近 0MinMax 不会生成负值符合浓度物理意义。切分比例 70/15/15 是经验值。空气质量小时级数据通常有几千到几万条这个比例够用。如果数据量不足两千条验证集和测试集会因为窗口滑掉开头一段后变得太小这时可以把比例调成 80/10/10或者改用时间序列交叉验证。2.3 滑动窗口把时序序列变成监督学习样本LSTM 不是直接吃一维序列它需要的是“过去一段窗口的特征 → 未来一个值”这样的样本对。这一步叫滑动窗口也叫序列到样本的转换。def make_windows(data, target, seq_len, horizon): 把连续时间序列切成 (X, y) 监督样本 data: 缩放后的全特征序列 [T, feature_num] target: 缩放后的目标序列 [T, 1] seq_len: 回看窗口长度小时 horizon: 预测未来第几个时刻 X, y [], [] for i in range(len(data) - seq_len - horizon 1): X.append(data[i : i seq_len]) # 目标取窗口结束后的第 horizon 个值 y.append(target[i seq_len horizon - 1]) return np.array(X), np.array(y) SEQ_LEN 24 # 用过去 24 小时预测未来 HORIZON 1 # 预测未来 1 小时 X_train, y_train make_windows(train_x, train_y, SEQ_LEN, HORIZON) X_val, y_val make_windows(val_x, val_y, SEQ_LEN, HORIZON) X_test, y_test make_windows(test_x, test_y, SEQ_LEN, HORIZON)X_train 的形状是 [样本数, 24, 7]7 是特征数正好符合 PyTorch 里 LSTM 要求的 (batch, seq_len, features) 布局。y_train 的形状是 [样本数, 1]是未来那一小时的 PM2.5 归一化浓度。SEQ_LEN 取 24 是因为空气污染有明显的日内周期凌晨累积、早高峰抬升、午后扩散、夜间再累积24 小时刚好覆盖一轮完整周期。如果数据里有明显的周周期比如周末工业排放降低可以尝试 168一周小时数但序列太长会显著拖慢训练建议先跑 24验证集上效果不够再考虑加长。HORIZON 取 1 是单步预测模型结构最简单想预测未来 6 小时可以把 HORIZON 改成 6但样本数会同步减少且误差会随预测距离累积。窗口构造这一步完成后训练数据就不缺了。接下来进入模型搭建。3. 用 PyTorch 搭 LSTM 空气质量预测模型结构设计与训练参数数据准备好之后模型结构并不复杂。空气质量预测本质是回归问题LSTM 负责从历史序列里提取时序特征最后接一个全连接层输出浓度值。这一章把模型骨架、训练循环和关键参数一次说清。3.1 为什么选 LSTM 而不是 RNN 或普通 MLP普通 RNN 在长序列上存在梯度消失问题时间步一长前面的信息传不到最后。空气质量的规律恰恰是长依赖今天夜间 PM2.5 的累积状态会影响明天早上的峰值跨了十几个小时。LSTM 用遗忘门、输入门、输出门控制信息保留与丢弃能把这种跨小时的相关性带到最后一步。那为什么不用 MLP 直接喂“最近 24 小时的 PM2.5”做回归可以但 MLP 把 24 个时间步当独立特征学不到“第 3 小时到第 4 小时的变化趋势”这种时序关系。LSTM 的循环结构在时间步之间共享权重天然建模序列演化。实现层面PyTorch 比 Keras 更适合这个场景。一是调试方便网络中间输出随手可以打印检查二是滚动预测、自定义训练循环都更灵活三是小时级空气质量数据量不大CPU 也能训练不需要分布式环境。3.2 模型骨架两层 LSTM 加回归头的实现代码import torch import torch.nn as nn class LSTMPredictor(nn.Module): def __init__(self, n_features, hidden_size64, num_layers2, output_size1, dropout0.2): super().__init__() self.lstm nn.LSTM( input_sizen_features, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, # 输入形状用 (batch, seq_len, features) dropoutdropout # 两层及以上时层间 dropout 生效 ) self.reg_head nn.Sequential( nn.Linear(hidden_size, 32), nn.ReLU(), nn.Linear(32, output_size) ) def forward(self, x): # x: [batch, seq_len, n_features] out, _ self.lstm(x) # out: [batch, seq_len, hidden_size] last out[:, -1, :] # 取最后一个时间步的隐状态 return self.reg_head(last) # 输出 [batch, 1]两个关键设计点。第一取 out 最后一个时间步而不是全部时间步做平均因为预测未来浓度时最新的状态信息量最大平均池化会把前面老旧状态混进来。第二LSTM 的 dropout 参数只对多层结构的非最后一层生效单层网络传 dropout 会被 PyTorch 忽略这个行为容易让人误以为设置了没效果。hidden_size 取 64 是小时级数据的稳妥起点。64 能捕获多污染物之间的交互又不至于在几千条样本上过拟合。数据量超过两万条可以试 128但训练时间会翻倍。num_layers 取 2 是权衡结果1 层容易欠拟合3 层在空气质量这类信噪比不高的数据上提升有限反而更容易过拟合。3.3 训练循环loss、优化器、早停与随机种子训练循环要解决三个问题用什么损失函数、学习率怎么设、什么时候停。import torch.optim as optim from torch.utils.data import TensorDataset, DataLoader # 转成 PyTorch Datasetbatch_first 的数据直接喂 train_ds TensorDataset( torch.tensor(X_train, dtypetorch.float32), torch.tensor(y_train, dtypetorch.float32) ) val_ds TensorDataset( torch.tensor(X_val, dtypetorch.float32), torch.tensor(y_val, dtypetorch.float32) ) train_loader DataLoader(train_ds, batch_size64, shuffleTrue) val_loader DataLoader(val_ds, batch_size64, shuffleFalse) model LSTMPredictor(n_featuresX_train.shape[2], hidden_size64, num_layers2) optimizer optim.Adam(model.parameters(), lr1e-3) criterion nn.MSELoss() # 固定随机种子否则调参像抽卡同一份代码两次结果不同 def set_seed(seed42): np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) set_seed(42) best_val_loss float(inf) patience 0 max_patience 5 # 连续 5 轮验证损失不降就早停 for epoch in range(50): model.train() train_loss 0.0 for xb, yb in train_loader: optimizer.zero_grad() pred model(xb) loss criterion(pred, yb) loss.backward() optimizer.step() train_loss loss.item() # 验证阶段关掉梯度节省内存并防止影响 BN 等层状态 model.eval() val_loss 0.0 with torch.no_grad(): for xb, yb in val_loader: pred model(xb) val_loss criterion(pred, yb).item() val_loss / len(val_loader) print(fepoch {epoch1:02d} | train_loss {train_loss:.4f} | val_loss {val_loss:.4f}) if val_loss best_val_loss: best_val_loss val_loss torch.save(model.state_dict(), best_lstm.pt) patience 0 else: patience 1 if patience max_patience: print(early stop) breakloss 用 MSELoss 而不是 MAE是因为 MSE 对大误差的惩罚更重。空气质量预测的业务上高浓度事件重污染过程比低浓度的小误差更值得关注MSE 会让模型优先拟合那些浓度飙升的时刻。代价是会让整体 MAE 略高这是合理的取舍。学习率 1e-3 配 Adam 是默认起点。空气质量数据特征尺度在 0~1 之间梯度不会爆炸这个学习率基本安全。如果发现 val_loss 震荡不降把 lr 降到 3e-4如果训练集 loss 都降不下去先查数据预处理而不是调网络。shuffleTrue 在这里是允许的。虽然样本来自连续时间序列但训练集内部 shuffle 只是打乱了 batch 组合顺序不会把未来的时间步混进训练集因为切分阶段已经隔离了。time shuffle 反而能让每个 batch 的样本来源更分散梯度更新更稳定。early stopping 的 patience 设 5。空气质量数据噪声大验证损失会在 5~10 轮里来回波动patience 太小会过早停掉太大又浪费时间。训练完成后加载 best_lstm.pt不要用最后一轮的权重。4. 预测效果评估与可视化分析不是画张曲线就完事模型训练完了接下来要回答“预测得到底准不准”。这一步要做的不是把两条曲线叠在一起喊一句“拟合得很好”而是用指标衡量误差、用图形定位系统性偏差再用相关性分析验证特征选择是否合理。4.1 三个指标看预测质量RMSE、MAE 与 R²先加载最优权重在测试集上做预测。注意预测结果是在 0~1 区间里的归一化值必须反标准化成真实浓度才能算指标。from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score model.load_state_dict(torch.load(best_lstm.pt, map_locationcpu)) model.eval() with torch.no_grad(): pred_norm model(torch.tensor(X_test, dtypetorch.float32)).numpy() # 反标准化MinMax 逆变换公式 # 原始值 归一化值 / scale_ data_min_ pm25_idx df.columns.get_loc(PM25) pm_scale scaler_y.scale_[0] pm_min scaler_y.data_min_[0] y_true y_test / pm_scale pm_min y_pred pred_norm.ravel() / pm_scale pm_min rmse mean_squared_error(y_true, y_pred, squaredFalse) mae mean_absolute_error(y_true, y_pred) r2 r2_score(y_true, y_pred) # MAPE 要过滤掉接近 0 的样本否则分母爆炸 mask y_true 10 mape np.mean(np.abs((y_true[mask] - y_pred[mask]) / y_true[mask])) * 100 print(fRMSE: {rmse:.2f} μg/m³) print(fMAE: {mae:.2f} μg/m³) print(fR²: {r2:.3f}) print(fMAPE(PM2.510): {mape:.1f}%)RMSE 是主指标它放大高浓度样本的误差重污染过程预测偏了RMSE 立刻变难看。MAE 反映整体平均偏移适合衡量日常预测稳定性。R² 看趋势解释力但时间序列的 R² 比回归场景更容易虚高因为相邻小时浓度本来就高度自相关不能单独拿来下结论。我一般不设绝对达标线而是用“RMSE 控制在当季 PM2.5 均值的三分之一以内”作为可用线不同城市、不同季节差异很大硬套数字没有意义。MAPE 在这里只统计 PM2.5 大于 10 的样本。夜间浓度常年在个位数徘徊真实值 5 预测 8绝对误差只有 3MAPE 却会算出 60% 的误差率严重拉低指标可信度。4.2 预测曲线、误差分布与按小时误差箱线图可视化分析的第一张图永远是“真实曲线 vs 预测曲线”。画图前先把 matplotlib 的中文字体配好否则图里全是方块。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False # 解决负号显示成方块的问题 test_index df.index[train_size val_size SEQ_LEN:] # 与 X_test 对齐 # 注意窗口构造截掉了开头的 SEQ_LEN 个时间点 fig, ax plt.subplots(figsize(14, 5)) ax.plot(test_index, y_true, label真实 PM2.5, linewidth0.8, colorblack) ax.plot(test_index, y_pred, labelLSTM 预测, linewidth0.8, colorcrimson) ax.set_xlabel(时间) ax.set_ylabel(PM2.5 浓度 (μg/m³)) ax.legend() plt.tight_layout() plt.savefig(pred_curve.png, dpi150)这张图重点看两个地方峰值出现的位置是否滞后以及低浓度段预测是否系统性偏高。滞后问题后面避坑章节专门讲这里先用图确认整体趋势是否跟得住。第二张图是误差分布第三张图按小时分组看误差用来定位“哪个时段模型最不靠谱”。error y_true.ravel() - y_pred.ravel() # 误差直方图看偏差是否集中在 0 附近是否右偏 fig, ax plt.subplots(figsize(8, 4)) ax.hist(error, bins50, colorsteelblue, edgecolorwhite) ax.set_xlabel(预测误差 (μg/m³)) ax.set_ylabel(频数) plt.tight_layout() plt.savefig(error_hist.png, dpi150) # 按小时分组误差箱线图定位早晚高峰的系统性偏差 error_df pd.DataFrame({hour: test_index.hour, error: error}) fig, ax plt.subplots(figsize(12, 5)) error_df.boxplot(columnerror, byhour, axax) ax.set_xlabel(小时) ax.set_ylabel(预测误差 (μg/m³)) plt.suptitle() plt.tight_layout() plt.savefig(error_by_hour.png, dpi150)按小时箱线图很能说明问题。比如早上 7~9 点误差整体为正说明早高峰排放过程模型低估了夜间误差离散度大说明低浓度时段噪声本来就高。看到这种规律下一步不是调 LSTM 参数而是考虑加气象特征或把早晚高峰单独建模。4.3 相关性热力图用可视化辅助选特征可视化分析不只在评估阶段有用建特征之前更该看一眼相关性矩阵。import seaborn as sns corr df[[PM25, PM10, NO2, SO2, CO, O3, AQI]].corr() plt.figure(figsize(8, 6)) sns.heatmap(corr, annotTrue, fmt.2f, cmapcoolwarm, vmin-1, vmax1, linewidths0.5) plt.tight_layout() plt.savefig(corr_heatmap.png, dpi150)PM10 与 PM2.5 的相关性通常很高因为同源且粒径分布有重叠CO 和 PM2.5 强相关一般指向燃烧源O3 与 PM2.5 的关系复杂夏季高 O3 往往伴随光化学二次气溶胶生成冬季则常常负相关。这些规律可以帮助决定哪些列要进模型。但要注意相关性高不代表因果CO 与 PM2.5 都来自机动车排放去掉 CO 模型未必变差。热力图只是辅助特征选择的起点真正判断得靠“加特征 vs 不加特征”的对照实验。5. 避坑空气质量时间序列预测最常见的 5 个翻车点LSTM 预测空气质量这事模型结构翻车的少数据流程翻车的多。下面五条是我自己踩过的坑按“现象 → 原因 → 解决”写清楚。5.1 预测曲线比真实曲线慢一拍现象预测曲线和真实曲线形状几乎一样但整条线往右平移了一个时间步真实浓度开始涨了预测还在低位等到真实值到顶了预测才追上来。原因单步预测的模型在训练时发现最优策略就是“复读最近的值”。因为 PM2.5 小时序列自相关性极强前一小时浓度对后一小时浓度的解释力超过任何特征MSE 损失下模型学会了抄近道。解决先把滞后程度定量化确认不是肉眼错觉。from scipy.stats import pearsonr # 计算预测与真实值的同期相关以及与真实值滞后1小时的相关 corr_same pearsonr(y_true[1:], y_pred[1:])[0] corr_shift pearsonr(y_true[:-1], y_pred[1:])[0] print(f同期相关: {corr_same:.3f}, 预测比真实滞后1小时的相关: {corr_shift:.3f})如果 corr_shift 明显大于 corr_same基本可以断定滞后。缓解办法有三个把 HORIZON 改成 3 或 6 做真正的多步预测让模型为更远的目标优化加入温度、风速、边界层高度等气象特征切断“只靠上一小时浓度”的捷径或者接受单步滞后只把预测用于未来 3 小时以上的趋势判断。5.2 直接 dropna 导致时间轴断裂现象训练曲线看起来正常一旦看预测曲线发现某个时刻预测值凭空跳变而真实数据并没有对应突变。原因很多人清洗数据时直接 df.dropna()缺失行被删掉后物理时间上相隔几小时甚至几天的两条记录在 DataFrame 里变成了相邻行。滑动窗口把这两个不相邻时刻拼成一个样本模型学到的是“跨越缺失段的伪规律”。解决先看缺失率缺失率低于 5% 用 ffill 加插值成段缺失超过 24 小时的直接砍掉这段并在构造窗口时让窗口不跨过断裂点。# 找出成段缺失的区间窗口构造时跳过 missing_mask df.isna().any(axis1) # 连续缺失超过 24 小时的段记录起止时间切窗口时丢弃跨段样本这个“跳过跨段样本”的细节我吃过一次大亏。补上之后模型在真实污染过程中的表现立刻正常了。5.3 全量数据 fit MinMaxScaler 造成信息泄漏现象验证集指标非常漂亮R² 上了 0.95一放到新数据上预测就明显失真。原因标准化时对整个数据集调用了 fit_transform测试集的 min 和 max 已经参与训练数据缩放。模型在训练时等于见过了未来数据的取值边界这是典型的泄漏。解决严格按 2.2 节的顺序——先按时间切分再只在训练段上 fit scaler验证和测试段只 transform。切分必须在任何统计量计算之前完成。5.4 MAPE 遇到 PM2.5 接近 0 时算出一百多现象MAPE 算出来一两百怎么看都不合理但 RMSE 和 MAE 都正常。原因PM2.5 浓度在清洁天气下可能只有个位数真实值 5、预测值 3绝对误差很小MAPE 分母是 5直接算出 40% 误差率。夜里一堆接近 0 的样本MAPE 瞬间爆炸。解决不要对全量样本算 MAPE过滤掉 PM2.5 低于 10 的样本再算并同时报 RMSE 和 MAE。做业务汇报时MAPE 结合 RMSE 一起说避免单指标误导。5.5 随机种子不固定导致调参像抽卡现象同样的代码、同样的数据两次训练结果不一样今天跑 R² 0.82明天跑 0.78想比较两组参数谁更好都没法定论。原因PyTorch 的权重初始化、DataLoader 的 shuffle、甚至操作系统的线程调度都会带来随机性。神经网络本身不是确定性过程。解决固定全链路随机种子并保存一份训练参数记录。import random random.seed(42) np.random.seed(42) torch.manual_seed(42) torch.cuda.manual_seed_all(42) # 如果追求严格可复现把 DataLoader 的 worker 随机种子也固定 def _worker_init(worker_id): np.random.seed(42 worker_id) DataLoader(train_ds, batch_size64, shuffleTrue, worker_init_fn_worker_init)固定种子后同参数结果稳定调参才有可比性。这不算玄学而是工程上可复现性的基本要求。6. 把模型用起来滚动预测、模型导出与后续验证训练和评估做完模型还躺在 Jupyter 里。要真正用起来至少需要三步写一个滚动预测函数做多步外推把模型和 scaler 参数一起导出规划定期重训练。滚动预测的思路是把预测出的浓度值当作已知值拼回输入序列再预测下一个时刻。下面是预测未来 24 小时的简版实现。def rolling_forecast(model, last_seq, steps24, first_pred_idxpm25_idx): last_seq: [seq_len, n_features] 已归一化的最近窗口 model.eval() seq last_seq.clone() preds [] with torch.no_grad(): for _ in range(steps): p model(seq.unsqueeze(0)) # [1,1] preds.append(p.item()) new_step seq[-1].clone() new_step[first_pred_idx] p.item() # 把新预测的PM2.5接进去 seq torch.cat([seq[1:], new_step.unsqueeze(0)], dim0) return np.array(preds)这段代码最要注意的是 new_step 的处理。多污染物预测时我们只滚动 PM2.5其他特征列暂时沿用最近观测值。这是常见近似误差会随时间步累积预测未来 3~6 小时还能看超过 12 小时就只能看趋势方向不要当精确值用。严格做法是给每个特征都建预测模型或者引入气象预报值作为外部输入。导出模型时把 scaler 参数一起保存否则换了环境无法反标准化。torch.save(model.state_dict(), lstm_air.pt) np.savez(scaler_params.npz, scale_xscaler_X.scale_, min_xscaler_X.data_min_, scale_yscaler_y.scale_, min_yscaler_y.data_min_)空气质量存在明显的季节漂移冬季采暖期排放结构完全不同于夏季同一个模型跨季往往变差。我的习惯是每周用最新数据重训一次或者用新数据对旧模型微调几个 epoch 而不是从零训练。做任何新一批监测数据时先查滞后和标准化泄漏这两个问题再动网络结构这是我做这套系统最深的教训。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →