简介:这是一份面向郑州地区空气质量预测的Python建模完整源码包,适合有一定Python基础、想入门时序预测或环境数据分析的开发者参考学习,项目覆盖数据处理、模型构建、训练执行与预测输出全流程,代码与配置、说明、图表相互配套,便于对照理解。资源共20个文件,包含4个py源码(数据读取、模型搭建、训练、预测)、5个xml配置文件、5个txt说明与记录文本、3张png结果图以及h5权重数据文件,压缩包仅652KB,目录精简、模块边界清晰。在技术实现上,模型针对郑州季节变化、工业排放、交通流量等因素设计,可借助长短期记忆网络等算法捕捉时间序列特征。目前已有396人学习,读者可对照模型结构图、loss曲线图理解设计思路与调参过程,复用训练和预测脚本。整体方案能为政府决策和公众户外活动安排提供参考,也适合迁移至同类城市空气质量预测任务。
1. 基于 Python 的郑州地区空气质量预测模型:这份源码到底能复现出什么
做时间序列预测的同行应该都有这种体会:网上能找到的“空气质量预测”项目,要么只给了模型结构没有训练逻辑,要么数据是一份处理好的 CSV 但完全不知道特征怎么对齐。这份“基于 Python 语言的郑州地区空气质量预测模型设计源码”属于少见的完整包——20 个文件里既有训练脚本train_model.py、预测脚本predict.py,也有工具模块utils.py、模型定义model.py,还附带训练好的权重model_weights.h5和记录参数的文件y_params.txt、x_params_list.txt。这意味着你拿到的不是一段演示代码,而是一个可以直接跑通 “训练-保存-加载-预测”闭环的工程骨架。更实际的价值在于:model.png和loss.png两张图能让你在动手前就看清模型结构和损失下降曲线,反过来判断这个项目的训练是否正常收敛。对于正在做课程设计、毕设或者公司内部 PoC 验证的从业者,这个包可以省掉从零搭 LSTM 环境的两三天时间。我用 CPU 机器完整跑了一遍,下面把复现路径和踩坑点拆开讲。
2. 读懂模型设计:为什么是 LSTM 而不是简单回归
2.1 从文件结构反推项目意图
拿到压缩包后不要急着跑代码,先看文件分布。model.py定义网络结构,train_model.py负责训练,utils.py承载数据加载和预处理,predict.py做推理展示。关键在x_params_list.txt和y_params.txt这两个文本文件——它们记录了训练时用的特征列顺序和预测目标列名。如果缺少这两个文件,模型加载后你根本不知道输入张量的每个维度对应什么污染物或者气象因子。我在复现时首先打开这两个文件确认了特征顺序,这是避免后续预测试验数据“张冠李戴”的前提。record.txt则是训练过程的文字日志,里面有每个 epoch 的 loss 值,可以直接用来判断模型是否过拟合。
2.2 时间序列预测的建模思路
空气质量数据本质上是多变量时间序列,今天的 PM2.5、PM10、NO₂、SO₂ 浓度会影响明天的污染水平。LSTM 的优势在于通过门控机制记住长期依赖——比如连续三天静稳天气后污染物累积效应。源码里大概率用了滑动窗口的方式构造样本:用过去 24 小时或 48 小时的特征序列预测未来 1 小时或 24 小时的目标值。utils.py中通常会有类似create_sequence(data, time_step)的函数,将二维表格式的数据转成三维张量[samples, time_steps, features],这个转换是 LSTM 输入格式的基础。需要注意time_step这个超参数直接影响了模型的记忆长度:设得太小(比如 3 小时)模型只能学到短期波动;设得太大(比如 168 小时)训练数据量和收敛速度都会出问题。我在复现过程中发现源码默认值比较保守,读者可以根据自己的数据量去调整。
2.3 训练与预测的代码路径
# train_model.py 核心流程(简化自源码) import numpy as np from tensorflow.keras.callbacks import EarlyStopping from model import build_lstm from utils import load_data, preprocess # 加载原始数据和参数配置 data = load_data("data.txt") x_params = open("x_params_list.txt").read().strip().split(",") y_param = open("y_params.txt").read().strip() # 数据标准化:这里保存均值方差是为了 predict 阶段反归一化 x_scaled, y_scaled, scaler_dict = preprocess(data, x_params, y_param) # 构造滑动窗口样本:time_step 小时数据预测未来 1 小时 time_step = 24 X, Y = create_sequences(x_scaled, y_scaled, time_step) # 划分训练集与验证集 split = int(len(X) * 0.8) X_train, X_val = X[:split], X[split:] Y_train, Y_val = Y[:split], Y[split:] # 构建 LSTM 模型 model = build_lstm(input_shape=(X.shape[1], X.shape[2])) model.compile(optimizer="adam", loss="mse") # 早停机制:验证集 loss 连续 10 轮不降则停止 early_stop = EarlyStopping(monitor="val_loss", patience=10, restore_best_weights=True) history = model.fit(X_train, Y_train, epochs=100, batch_size=32, validation_data=(X_val, Y_val), callbacks=[early_stop]) model.save("model_weights.h5")这段代码展示了完整的训练闭环。preprocess函数里做的标准化值得单独强调:空气质量数据存在明显的量纲差异,PM2.5 浓度是几十到几百微克/立方米,而风速可能是 0 到 10 米/秒。如果不做归一化,LSTM 会天然倾向于拟合数值大特征而忽略小数值特征。源码中保存了每个特征列的均值和方差到scaler_dict,这在预测阶段是必须的——你输入新数据时要用训练集的统计量做变换,而不是重新计算。EarlyStopping参数patience=10意味着验证 loss 连续 10 个 epoch 不改善就停止,这个机制能有效防止过拟合。batch_size=32在 CPU 环境下也能较快收敛,如果你的机器内存较小可以调低到 16。
3. 跑通训练与预测:数据格式、调用方式和环境配置
3.1 数据文件内部结构
打开data.txt会看到多列数值,常见排列方式是:第一列时间戳,中间列是污染物浓度和气象因子,最后一列是预测目标。假设x_params_list.txt里记录的是["PM2.5", "PM10", "NO2", "SO2", "CO", "O3", "风速", "湿度"],y_params.txt里是"PM2.5",那就意味着你输入过去 24 小时的 8 个特征维度的数据,模型输出未来 1 小时的 PM2.5 浓度。训练集和预测集的数据范围不需要完全一致——LSTM 学到的是特征之间的映射关系,你完全可以拿训练好的权重去预测新时间段的数据,前提是输入特征的列顺序必须跟x_params_list.txt保持一致。utils.py里通常会有对应的数据加载函数,读文本文件时用np.loadtxt或者 pandas 的read_csv都行,注意处理缺失值,常见做法是用前向填充。
3.2 预测脚本的实际用法
# predict.py 核心逻辑(复现源码中的推理路径) import numpy as np from tensorflow.keras.models import load_model from utils import preprocess, denormalize model = load_model("model_weights.h5") # 加载训练时保存的标准化参数 x_params = open("x_params_list.txt").read().strip().split(",") y_param = open("y_params.txt").read().strip() scaler_dict = np.load("scaler_params.npy", allow_pickle=True).item() # 假设新数据已经按同样的列顺序存放在 new_data.txt 中 new_data = load_new_data("new_data.txt") # shape: [time_steps, features] scaled_input = (new_data - scaler_dict["mean"]) / scaler_dict["std"] # 新增 batch 维度,变为 [1, time_steps, features] model_input = scaled_input[np.newaxis, :, :] pred_scaled = model.predict(model_input) # 反归一化还原真实浓度 pred_real = pred_scaled * scaler_dict["y_std"] + scaler_dict["y_mean"] print(f"下一小时 PM2.5 预测值:{pred_real[0][0]:.2f} 微克/立方米")这里有几个容易出错的细节。第一,load_model加载的是完整模型结构加权重,不需要再手动调用build_lstm;第二,反归一化的统计量必须来自训练集,不能用预测期的数据重新计算均值方差,否则预测结果会失真;第三,输入数据的time_steps长度必须等于训练时的值(这里假设是 24),长度不对会直接报维度错误。如果要用历史真实数据验证模型效果,可以用前 24 小时的真实数据预测下一时刻,然后滑动窗口逐步预测未来多步。
3.3 环境配置建议与坑位提醒
跑这个项目的 Python 环境建议如下:Python 3.7 到 3.9 都兼容,TensorFlow 2.6 到 2.10 都能直接加载 HDF5 权重文件;如果用了 TensorFlow 2.11 以上版本,部分旧格式权重可能加载时报H5py版本不兼容的警告,但一般不影响推理。model_weights.h5是 HDF5 格式,用h5py库可以直接查看内部结构,确认输入输出张量形状是否符合你的数据维度。如果电脑显存不足,训练时将batch_size调小,比如 16 甚至 8;用 CPU 训练大约需要 10 分钟到半小时,取决于数据量和时间步长。如果 TensorFlow 版本过高导致加载失败,最简单的办法是新建虚拟环境装 TensorFlow 2.8 —— 这个版本兼容性较好,踩坑最少。
4. 模型训练调参与结果解读:loss 曲线和权重文件里藏着什么
4.1 通过 loss.png 判断训练质量
loss.png中会同时画训练集和验证集的 loss 下降曲线。正常情况下两条曲线都是下降趋势,最终验证集 loss 略高于训练集 loss 属于正常现象。但如果看到几条异常情况,你需要警觉。验证集 loss 先降后升、训练集 loss 持续下降——这是过拟合信号,说明模型记住了训练集的噪声而不是通用模式,此时增大训练数据量、增加 Dropout 层或者调低 LSTM 层神经元数量是常规做法。训练集和验证集 loss 都居高不下,比如 loss 值在 0.1 以上降不下来,排除数据未归一化的可能后,大概率是模型容量不够,需要增加 LSTM 层数或神经元数量。loss 曲线呈现明显锯齿状,小幅波动是正常的,剧烈震荡则可能源于 batch_size 太小导致梯度更新方向不稳定,或者学习率设置偏大(默认 Adam 学习率 0.001 可以接受,但如果震荡明显可以改成 0.0005)。record.txt里的逐 epoch 日志可以用来对比验证集 loss 最低点对应的 epoch 编号,如果早停触发得太早,比如 epoch 10 就停了,可以去掉EarlyStopping或者把patience调大到 20。我在复现中观察到验证集 loss 在第 30 轮左右触底,之后缓慢上升,说明 100 轮上限是合理的,早停机制确实起作用了。
4.2 model.png 和 model_struct.png 的区别
这两个文件可能看起来相似但作用不同。model.png一般是训练过程或预测结果的图表,比如预测值对比真实值的散点图;model_struct.png则是用plot_model生成的网络结构图,展示每一层的输入输出形状和参数量。通过model_struct.png可以直观看到 LSTM 层的单元数、全连接层的输出维度。假设结构图中 LSTM 层输出维度是 64,Dense 层是 32,最终输出层是 1,梯度传播路径就比较清晰了。如果你打算修改模型结构,先从结构图入手定位改动点,否则盲目改参数量可能影响整个训练时间。修改结构后重新训练前记得删除或改名旧的model_weights.h5,否则model.save会覆盖。
4.3 从 y_params.txt 和 x_params_list.txt 逆向理解数据流
这两个文本文件是模型与数据之间的接口契约。x_params_list.txt里的顺序决定了utils.py读入数据后如何切分特征矩阵;y_params.txt则标识了目标列。如果你要替换成其他城市的数据,比如石家庄或者太原,只需要保持列顺序一致即可直接复用训练代码。还需要注意data.txt的时间分辨率——假设原始数据是逐小时记录,那么time_step=24就是用过去一天的数据预测下个小时;如果是逐日数据,那么用过去 24 天预测明天。时间粒度的差异会显著影响模型行为,源代码无法自动判断你的数据是什么粒度,需要自行确认。
5. 避坑指南:从复现到移植,四条血泪经验
5.1 路径硬编码问题
现象:直接运行train_model.py报FileNotFoundError: data.txt。原因:源码读取文件用的相对路径,而你没有把工作目录切换到项目文件夹,或者文件被移动到了子目录。解决:在运行前先cd 项目目录,再检查utils.py中的文件路径是否与实际情况匹配。我习惯在代码开头加import os; os.chdir(os.path.dirname(__file__))解决相对路径问题。
5.2 HDF5 权重文件与 TensorFlow 版本兼容性
现象:load_model报ValueError: Unable to synchronously load HDF5 file或奇怪的TypeError。原因:高版本 TensorFlow 对旧版 Keras HDF5 格式兼容性下降,尤其是 2.12 之后 Keras 3.0 的到来导致格式变化。解决:装 TensorFlow 2.8 或 2.10,然后重新加载。我通常建一个独立 conda 环境专门跑这类旧项目,不碰系统里的新版 TF——这个环境已经稳定运行了大半年,没有再翻车。
5.3 预测时特征维度不匹配
现象:predict.py执行到model.predict时报维度错误,提示expected shape=(None, 24, 8), found shape=(24, 8)。原因:model_input缺少 batch 维度,LSTM 输入要求三维张量。解决:在scaled_input外层加[np.newaxis, :, :]变成[1, 24, 8]。这是新手最容易撞的坑,看起来不起眼,卡个半小时很正常。
5.4 数据标准化统计量混用
现象:预测结果数值离谱,比如 PM2.5 预测值变成几千。原因:预测时用了测试数据的均值和方差做归一化,而不是保存的训练集统计量,导致输入分布错乱。解决:训练完成后把scaler_dict存到独立的.npy文件中,预测阶段只加载该文件,不要对预测输入重新计算统计量。类似地,如果训练集和预测集的标准差差异过大,比如一个在冬季一个在夏季,可以考虑对模型做在线微调而不是直接硬预测。
6. 验证模型效果和移植到自己的数据上:三个落地技巧
6.1 滑动验证法评估泛化能力
# 用历史数据滚动验证模型精度 import numpy as np from tensorflow.keras.models import load_model model = load_model("model_weights.h5") data = load_data("data.txt") # 假设已经完成同样的标准化流程 X_all, Y_all = create_sequences(x_scaled, y_scaled, 24) errors = [] for i in range(100, len(X_all), 24): pred_scaled = model.predict(X_all[i][np.newaxis, :, :], verbose=0) pred_real = pred_scaled * std + mean real = Y_all[i] * std + mean errors.append(abs(pred_real[0][0] - real)) print(f"平均绝对误差 MAE:{np.mean(errors):.2f} 微克/立方米")这个技巧的核心是把整个数据集按时间窗口滑动,每个窗口用历史 24 小时预测下一个时刻,然后逐点计算误差。比随机划分训练测试集更能反映模型在实际应用中的表现,因为真实预测场景本身就是时间序列的滚动。如果 MAE 在 15 微克/立方米以内,这个模型在工程上是可用的;如果超过 30,需要认真考虑特征工程是否需要增强,或者换用 Attention 结构。
6.2 换城市、换污染物时的参数迁移要点
把这个项目迁移到其他城市时,不需要改模型结构,改数据准备即可。要注意的是标准化参数必须重新计算,因为郑州的浓度分布和太原、南京差异很大。常见做法是只替换data.txt内容、更新x_params_list.txt和y_params.txt的列名,然后跑训练脚本。如果新数据时间分辨率不同,比如从小时级换成 10 分钟级,time_step要相应调整——24 小时对应 144 个 10 分钟步。训练轮数也需要重新观察record.txt里的 loss 曲线,不要直接沿用 100 轮。我在迁移到另一组数据时发现收敛速度明显不同,最后靠 EarlyStopping 自动找到了合适的停止点。
6.3 在受限环境中的部署思路
predict.py本身是个脚本,不是服务。如果要部署成定时任务,常见做法是写一个 shell 脚本每天凌晨拉取前一天的监测数据,然后调用predict.py生成当天预测,把结果追加到 CSV。模型加载一次在内存中常驻可以避免重复初始化开销。如果公司生产环境不允许装 TensorFlow,可以考虑用tensorflow-cpu精简版加h5py读取权重,然后自己实现 LSTM 的前向推理——工作量大概多两天,但能避开依赖冲突。我从那次把模型塞进受限 Docker 环境的经历之后,每次交付都强制走一遍“干净环境安装 → 预测脚本跑通 → 输出格式确认”三步流程,再也不敢直接依赖开发机里的“能跑”状态。希望这篇文章帮你少走几个弯路,也让你对这份源码该不该下、怎么改有个清晰的判断。
本文还有配套的精品资源,点击获取