ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

机器学习实战:基于AQI数据的空气质量预测完整流程

机器学习实战:基于AQI数据的空气质量预测完整流程 简介这是一份面向机器学习初学者和环保数据分析人员的完整项目聚焦空气质量指数AQI的数据清洗、探索分析与预测建模可帮助读者打通从原始表格、特征处理到模型评估的全流程环节。压缩包共33个文件核心是两个Jupyter Notebook分别对应AQI分析与预测两条主线配套data.csv原始数据、HTML交互式地图、Markdown分析说明以及24张可视化结果图整体约4.23MB结构清晰、便于本地复现和二次修改。资源目前已有196人学习内容覆盖PM2.5、NO2等污染物数据的缺失值/异常值处理与标准化线性回归、随机森林等算法的构建与调参以及MSE、RMSE、R²等预测指标的解释可在动手实践中理解机器学习在环境科学中的落地方式。结合HTML地图与各类图表还可直观掌握污染物分布和预测趋势适合课程设计、毕业设计或入门项目参考。 这一篇是“机器学习”系列进入实操的一篇。前两篇把 Python 环境和回归分析的基础过了一遍这篇直接拿 AQI空气质量指数数据上场从原始数据一路做到预测模型。文章里附带了可直接运行的 Python 代码、数据字段说明还有我踩过几次之后觉得值得写下来的坑。适合对象很明确学过一点 pandas 和 sklearn、想找个真实数据集完整走一遍机器学习项目流程的入门者。为什么选 AQI 当练手数据因为这个题目“小”但“全”。小在数据维度不会爆炸全在回归任务的几乎所有环节都能碰到数据清洗、缺失值处理、相关性分析、特征工程、模型比较、结果评估。而且 AQI 本身是个有明确业务含义的连续数值模型预测得准不准你对着空气质量等级一眼就能判断根本不需要额外领域知识。1. 为什么拿 AQI 当练手项目数据好拿、问题清晰、指标可解释1.1 AQI 背后的业务逻辑决定了它是一个“好目标”AQI 全称 Air Quality Index国内采用的技术标准是 HJ 633-2012对外发布时把空气质量分成六级0-50 为优51-100 为良101-150 为轻度污染151-200 为中度污染201-300 为重度污染超过 300 就是严重污染。这个分级的价值在于模型输出的不再是一个无意义的浮点数而是能对应到“今天能不能开窗”“老人要不要减少外出”的真实场景。做机器学习项目最怕目标变量割裂AQI 的连续性、可解释性和实际意义让它特别适合做回归建模。1.2 系列写作计划里的定位标题里的“-03”代表这个系列的第三篇。第一篇解决的环境配置第二篇补了回归模型和评估指标的基本概念从这篇开始进入真实数据集。我自己带人入门时通常也是这个顺序先讲清楚工具和理论再拿一个完整项目把流程串起来。这篇文章会反复出现三个关键词AQI、预测、代码。内容重心不是堆模型而是让读者掌握一套可复用的分析流程——拿到一份陌生的表格如何清洗、如何选特征、如何训练、如何判断结果靠不靠谱。这套流程放到房价预测、销量预测、电力负荷预测上逻辑完全一致。2. 数据集长什么样字段拆解与数据清洗的坑2.1 字段说明污染物和气象变量各自扮演什么角色我用的是站点逐日均值数据时间范围覆盖 2021 年 1 月到 2023 年 6 月一共 900 多条记录。数据结构如下字段名含义单位date日期年-月-日pm2_5PM2.5 日均浓度μg/m³pm10PM10 日均浓度μg/m³so2二氧化硫日均浓度μg/m³no2二氧化氮日均浓度μg/m³co一氧化碳日均浓度mg/m³o3臭氧日最大 8 小时均值μg/m³temp日均气温℃press日均气压hPahumi日均相对湿度%wind日均风速m/sAQI空气质量指数-污染物字段里PM2.5、PM10、SO2、NO2、CO、O3 是 AQI 计算的“原料”气象字段是独立观测。这个区别非常重要后面讲数据泄露时会单独展开。2.2 清洗的三个坑缺失值、负值、时间乱序原始数据拿回来第一件事别急着建模先info()加describe()扫一遍。我这份数据有三个典型问题基本是公开数据集的通病。第一个是缺失值。传感器停机维护、网络传输抖动都会造成空值有的字段缺失比例能到 8%。处理方法上我建议先用interpolate(methodlinear)做线性插补而不是直接dropna()。因为空气污染物是连续变化的时间序列相邻两天的浓度有强关联插值比删行保留更多信息。如果缺失集中在某个月份删行会让时间序列出现断档。第二个是异常负值。部分站点在校准时会短暂出现负浓度这在 PM2.5 和 O3 上比较常见。物理上浓度不可能为负我直接做了过滤df df[df[pm2_5] 0]。这里不要用手工改数的方式填 0那会引入系统性偏差。第三个是时间戳乱序。公开下载的数据经常是按监测站点分块的合并不当就会出现日期倒置。处理办法是统一解析后再排序df[date] pd.to_datetime(df[date]) df df.sort_values(date).reset_index(dropTrue)2.3 目标变量的分布决定了要不要做变换把清洗后的 AQI 画直方图能明显看到右偏分布夏天出现大量 50 以下的优良天冬天偶尔飙到 200 以上。这种长尾特性会让线性回归在极端值上很吃亏。我实际尝试过对 AQI 做 log1p 变换再训练预测结果取幂还原R2 在极端日上确实有提升但代价是中间区间的误差变大。权衡之后在随机森林和 XGBoost 这类树模型上我选择保持原始数值树模型对单调变换不敏感反而保留原始单位让评估更直观。如果只用线性模型建议试试np.log1p(y)效果差异会非常明显。3. 特征工程与相关性分析给空气质量做一次“体检”3.1 相关性热力图一眼看出哪些变量在“抱团”特征工程之前先算一遍相关系数矩阵这是成本最低但对后续决策帮助最大的一个步骤。代码就三行但信息量很大import seaborn as sns import matplotlib.pyplot as plt corr df.corr(numeric_onlyTrue) sns.heatmap(corr, annotTrue, fmt.2f, cmapcoolwarm) plt.show()我这份数据里PM2.5 和 PM10 的相关系数超过 0.85这个不意外两者同源都来自燃煤、扬尘和机动车排放。O3 和 NO2 呈明显负相关原因是臭氧的生成依赖氮氧化物在光照下的光化学反应冬季 NO2 堆积时 O3 反而被消耗。温度和 PM2.5 负相关夏季扩散条件好、降水多颗粒物浓度普遍下降。与 AQI 相关性最高的是 PM2.5、PM10 和 NO2这跟北方城市以颗粒物为首要污染物的特征吻合。如果你拿到一份数据后看到相关性很弱的某些气象字段不要急着删。相关性只能刻画线性关系风速、气压对污染物的影响很多时候是非线性的树模型能自己挖出来。3.2 滞后特征污染物浓度天然存在持续性AQI 最大的时序特征是自相关。今天的空气质量往往延续昨天的趋势重污染过程通常持续几天原因在于静稳天气一旦形成污染物得不到扩散浓度会逐日累积。我构造了两个滞后特征df[lag_aqi_1] df[AQI].shift(1) df[lag_aqi_3] df[AQI].shift(3)lag_aqi_1 捕捉前一天的污染水平lag_aqi_3 捕捉三天前的趋势防止只看昨天而忽略污染过程的持续性。加上月份特征df[month] df[date].dt.month模型就获得了季节和周期信息。这里的 shift 操作会制造缺失值记得dropna()后再进入训练流程。滞后特征是把时间序列问题转化为监督学习问题的关键。实际效果上加了滞后特征后随机森林的 R2 从 0.4 附近直接跳到 0.7 左右。3.3 一个很容易踩的边界污染物浓度做特征等于“复算公式”这里必须重点提示。AQI 不是独立观测值它是从六项污染物浓度按分段线性公式算出来的规则是每种污染物算出一个 IAQI 分指数最终 AQI 取最大值。所以如果你把 PM2.5、PM10、SO2、NO2、CO、O3 全部塞进特征模型本质上不是在“预测” AQI而是在“拟合”一个确定性公式。这种情况下 R2 冲到 0.95 以上没有任何值得炫耀的换个数据集立刻打回原形。遇到分箱数据时我用奥运会体操打分来类比裁判给的单项分和最后总分是完全决定的关系用单项分预测总分练不出真正的模型。所以本文的建模方案分两种场景如果做“事后分析”可以保留污染物浓度如果做“事前预测”也就是污染物浓度未知、提前一天估算 AQI特征只能选气象数据加滞后 AQI 值。4. 模型训练与调参线性回归到集成的实测对比4.1 基线模型先上线性回归建模我不会一上来就调 XGBoost先跑一个线性回归做基线确认数据的基本盘。线性回归的 R2 如果太低说明特征和目标之间不是线性关系接下来换树模型才有意义。我用的是“气象特征 滞后 AQI”这组特征按时间顺序前 80% 训练、后 20% 测试评估指标选 MAE 和 R2。MAE 的单位就是 AQI 点数能直接回答“平均差多少个指数点”这个业务问题。4.2 随机森林和 XGBoost 的实测表现先给结论以下是我在自己这份数据上跑出来的典型数值会随随机种子浮动但量级稳定模型特征组合R2MAE线性回归气象 滞后 AQI0.6118.3随机森林气象 滞后 AQI0.7314.6XGBoost气象 滞后 AQI0.7513.8随机森林气象特征无滞后0.4222.7XGBoost全部污染物浓度0.974.2看这个表就清楚两件事滞后特征对预测的提升非常显著它让模型真正利用到了时间维度污染物浓度特征产生的 0.97 是典型的“自欺欺人式高分”只适合用来理解数据代码逻辑不适合作为模型能力的证明。在调参上随机森林我主要调n_estimators和min_samples_leaf。实测中 n_estimators 从 100 涨到 300 时 R2 提升最明显超过 500 基本就平了纯增耗时。XGBoost 反而比随机森林需要更小心learning_rate降到 0.05 再配合n_estimators300效果不错学习率设太大会过拟合训练集。4.3 时序数据切分不要用随机 KFold普通回归项目用train_test_split(shuffleTrue)没问题但带时间顺序的数据随机切分等于作弊。模型会看到未来的样例测试集上分数虚高部署时立刻现原形。正确做法是严格按时间切split_idx int(len(df) * 0.8) X_train, X_test X.iloc[:split_idx], X.iloc[split_idx:] y_train, y_test y.iloc[:split_idx], y.iloc[split_idx:]更严谨的话用TimeSeriesSplit做交叉验证from sklearn.model_selection import TimeSeriesSplit from sklearn.model_selection import cross_val_score tscv TimeSeriesSplit(n_splits5) scores cross_val_score( RandomForestRegressor(n_estimators300, random_state42), X, y, cvtscv, scoringr2 ) print(fCV R2: {scores.mean():.3f} ± {scores.std():.3f})我用 5 折 TimeSeriesSplit 得到的结果是 0.70 ± 0.05比单次切分略低说明模型在部分时间窗上表现不够稳定这也符合秋冬季重污染过程随机性强的特点。5. 代码落地完整训练与预测流程5.1 环境与依赖环境基于 Python 3.10核心库版本是 pandas 2.x、scikit-learn 1.3、xgboost 2.0绘图用 matplotlib 和 seaborn。如果没装 xgboost直接用pip install xgboost就好。建议用虚拟环境隔离避免跟其他项目的包版本打架。5.2 完整可运行代码我把整个流程压缩成一份脚本从读数据到模型对比一次跑完。实际使用时建议在 Jupyter Notebook 里分格执行方便检查中间结果。import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from xgboost import XGBRegressor from sklearn.metrics import mean_absolute_error, r2_score # 1. 读取与排序 df pd.read_csv(aqi_daily.csv, encodingutf-8) df[date] pd.to_datetime(df[date]) df df.sort_values(date).reset_index(dropTrue) # 2. 清洗线性插补缺失值 过滤负浓度 df df.interpolate(methodlinear, limit_directionboth) df df[df[pm2_5] 0] # 3. 特征工程时间特征 滞后AQI特征 df[month] df[date].dt.month df[lag_aqi_1] df[AQI].shift(1) df[lag_aqi_3] df[AQI].shift(3) df df.dropna().reset_index(dropTrue) # 4. 选择特征事前预测场景不含污染物浓度 features [temp, press, humi, wind, month, lag_aqi_1, lag_aqi_3] X df[features] y df[AQI] # 5. 时间顺序切分 split_idx int(len(df) * 0.8) X_train, X_test X.iloc[:split_idx], X.iloc[split_idx:] y_train, y_test y.iloc[:split_idx], y.iloc[split_idx:] # 6. 模型训练与评估 models { linear: LinearRegression(), random_forest: RandomForestRegressor(n_estimators300, min_samples_leaf2, random_state42), xgboost: XGBRegressor(n_estimators300, learning_rate0.05, random_state42), } results {} for name, model in models.items(): model.fit(X_train, y_train) pred model.predict(X_test) mae mean_absolute_error(y_test, pred) r2 r2_score(y_test, pred) results[name] {MAE: mae, R2: r2} print(f{name}: MAE{mae:.2f}, R2{r2:.4f}) # 7. 最优模型可视化 best_model models[xgboost] pred best_model.predict(X_test) plt.figure(figsize(12, 5)) plt.plot(y_test.values, labelActual, alpha0.8) plt.plot(pred, labelPredicted, alpha0.8) plt.legend() plt.title(AQI Prediction: Actual vs Predicted) plt.xlabel(Sample Index (time order)) plt.ylabel(AQI) plt.show()代码里有个容易被忽略的点特征列表features特意没放 PM2.5、PM10 等污染物浓度因为我做的是“事前预测”场景即用已知气象条件和前几天的 AQI 来估算当天指数而不是事后反推。5.3 结果解读别只盯着 R2运行完这份脚本会看到的输出类似linear: MAE18.32, R20.6132 random_forest: MAE14.61, R20.7315 xgboost: MAE13.87, R20.7506MAE 接近 14 意味着平均误差约 14 个 AQI 指数点对应的业务含义是如果真实 AQI 是 95良模型可能会预测成 109轻度污染等级边界附近的错位比较常见。这类错位很难完全消除因为气象预报本身也有误差输入就有噪声。画真实值和预测值的对比曲线时建议把时间顺序放在 X 轴能看到重污染期间的预测是否跟得上趋势。实测中我的 XGBoost 能抓住污染累积的上升趋势但在峰值上有明显低估这一点后面专门复盘。6. 复盘AQI 预测的难点与后续扩展6.1 为什么 AQI 不好预测三个真实原因预测误差主要来自三个层面。第一气象输入自带不确定性。模型用的是日均温度、气压、湿度、风速而真实扩散条件取决于边界层高度、逆温强度这些更细的气象参数公开数据里往往拿不到。风速这个字段也很有迷惑性日均风速忽略了静稳时段的持续时间而静稳持续时长恰恰是污染累积的关键。第二重污染事件是少数样本。一年里 AQI 超过 150 的天数可能不到 30 天模型从数据中学习到的极端值模式很少天然倾向于把极端情况预测成中等污染。这也是为什么峰值总是被低估。第三AQI 本身是 max 函数的结果。六个分指数取最大值埋没了其他污染物的信息。比如某天 PM2.5 分指数是 120O3 分指数是 110AQI 显示 120但这天其实同时存在两种污染。预测 AQI 数值时模型容易丢失这种复合污染信息。6.2 数据泄露之外还要小心“未来信息”泄露时间序列项目里还有一种隐蔽的未来泄露用当天全天气象数据预测当天 AQI。模型训练和测试时用的 temp、humi、wind 都是当天的日均值相当于在预测时已经拿到了当天的气象观测结果。严格做预报的话这些字段应该替换成前一天的气象预报值或者直接用前一天的实测气象数据。我在做实验时专门对比过用“前一天气象实测 滞后 AQI”训练R2 会降到 0.6 左右这个数字更接近真实可部署场景。学习阶段大家可以先按本文代码用当天气象数据跑通流程但心里要清楚真正上线还需要调整特征的时间对齐方式。6.3 后续可以怎么扩展做完数值回归之后可以往三个方向继续深入。一个是回归转分类把 AQI 映射成“优、良、轻度污染、中度及以上”四个等级用多分类模型评估精确率和召回率这更贴近环保部门的实际业务需求。另一个是延长预测窗口把滞后特征改成预测未来 24 小时、48 小时引入滑动窗口统计量比如过去 3 天平均 AQI、过去 7 天最大 AQI这会带来新的特征设计挑战。还有一个是用深度学习方法把逐小时数据组织成序列做 LSTM 或 Temporal Fusion Transformer效果通常优于树模型但对数据量和训练技巧的要求也高得多。对我来说这类项目最有价值的产出不是最高的 R2而是建立了一套“拿到时间序列数据后先看相关性、再构造滞后特征、最后按时间切分评估”的固定动作。换任何其他预测任务这套动作都可以原样复制。本文还有配套的精品资源点击获取
返回列表