资讯动态

机器学习实战:从数据清洗到模型评估完整预测AQI空气质量指数

发布时间:2026/9/7 3:31:16 来源:尧图企业网站定制
简介面向环境科学与数据科学学习者的「机器学习-03-AQI分析与预测」资源围绕空气质量指数AQI数据清洗、探索性分析与预测建模展开结合Python、pandas等工具帮助读者掌握从污染物浓度与气象因素预处理到模型评估的完整流程。压缩包共33个文件约4.23MB主要包含Jupyter Notebook代码、data.csv数据集、HTML交互可视化、图表以及Markdown说明文档其中代码与数据配套便于直接运行复现。资源覆盖线性回归、随机森林等常用算法并涉及缺失值处理、归一化、交叉验证及MSE、RMSE、R²等评估指标可让学习者快速上手AQI回归预测任务。可视化图表和说明文档则有助于理解数据分布和预测效果适合作为机器学习与环境数据分析的入门实践案例。目前已有196人浏览学习是一份轻量但完整的速学资料。 做机器学习项目很多人一上来就急着怼模型结果数据还没看明白预测结果出来了一团糟回头还得返工。今天要聊的“机器学习-03-AQI分析与预测”就是国内课程里很经典的一道综合实战从数据清洗、特征工程到模型训练评估完整走一遍空气质量指数AQI的预测流程代码和数据都齐照着跑就能复现。这个项目特别适合两类人一类是刚学完机器学习基础理论、想找个完整案例练手的学生另一类是做环境数据分析相关工作的工程师想看看传统回归模型在这个场景下的效果上限。整体难度不算高用到的都是sklearn、pandas这类常规库但里面坑是真不少比如时间序列不能随便打乱划分、特征里藏着未来信息等等我会把这些都摊开讲。1. 项目背景与整体思路1.1 为什么选AQI作为机器学习实战项目AQIAir Quality Index空气质量指数是一个把多种污染物浓度折算成单一数值的综合性指标数值越高代表污染越严重。我之所以推荐拿它当机器学习练手项目因为它具备几个天然优势。数据获取门槛极低。国内很多城市的环境监测站点数据都是公开的网上也能找到整理好的历史数据集包括PM2.5、PM10、SO2、NO2、CO、O3等污染物浓度再加上温度、气压、湿度这些气象特征维度足够丰富做特征工程时不会觉得巧妇难为无米之炊。问题定义非常清晰。AQI预测本质上是回归问题目标变量是连续值评价指标直观R²、MAE、RMSE一算就知道模型好坏。而且它不像图像、文本那样需要GPU和深度学习框架普通电脑跑随机森林、XGBoost绰绰有余非常适合初学者在本地环境反复实验。最吸引我的是它的现实意义。你训练出来的模型真的能用来预测第二天的空气质量甚至能倒推分析哪个污染物对AQI贡献最大这种“模型结果能落地”的成就感是UCI上那些纯粹练手数据集给不了的。1.2 项目依赖环境与数据准备环境方面不用搞得太复杂Anaconda装好Python 3.8以上就够了。依赖库我列个清单pandas1.2.0 numpy1.20.0 scikit-learn1.0.0 xgboost1.5.0 matplotlib3.4.0 seaborn0.11.0数据方面我用的这份是某城市2013年到2017年逐日监测数据字段包括日期、PM2.5、PM10、SO2、NO2、CO、O3、温度、气压、湿度、风速一共4000多条记录目标列是当天的AQI数值。数据文件不大几MB的CSV但胜在字段类型丰富有连续数值型有风速这种带周期性的特征还有日期这种可以做时间衍生的变量够用了。注意如果你自己下载数据一定要确认AQI的计算方法。国内从2012年开始执行《环境空气质量标准》GB 3095-2012AQI的分级标准和IAQI计算方式有官方文件可查不同数据集如果口径不一致预测结果会差很多。2. 数据探索与预处理2.1 数据集结构与缺失值处理拿到数据的第一步永远是看结构别急着建模。我习惯先跑这几行import pandas as pd import numpy as np df pd.read_csv(aqi_data.csv, parse_dates[date], index_coldate) print(df.shape) # (记录数, 字段数) print(df.info()) # 各字段类型和缺失情况 print(df.describe()) # 数值字段的统计描述跑完之后要重点看两件事。第一是缺失值分布。我这份数据里PM2.5和PM10都有少量缺失占比不到3%但气象字段个别日期整条缺失。处理缺失值时我踩过坑一开始图省事直接按列填充均值结果模型训练出来R²直接掉了0.05因为均值填充把污染峰值的波动全部抹平了。后来改成先用前向填充ffill再后向填充bfill缺失值在时间轴上本来就是连续变化的相邻日期的数值更接近真实情况效果立竿见影。# 时间序列数据优先用相邻值填充 df[PM2.5] df[PM2.5].fillna(methodffill).fillna(methodbfill) df[PM10] df[PM10].fillna(methodffill).fillna(methodbfill) # 气象数据可以用线性插值 df[TEMP] df[TEMP].interpolate(methodlinear)第二是量纲差异。AQI数值在几十到三四百之间污染物浓度有的零点几、有的上百温度、气压更是跨了几个数量级。虽然树模型对量纲不敏感但后面如果尝试线性模型或者神经网络不做标准化会非常吃亏所以在预处理阶段就把所有数值特征做了StandardScaler标准化反正不亏。2.2 特征相关性分析与目标变量洞察预处理完别急着做特征工程先把数据可视化一遍你会对问题有直觉。相关性热力图是最快的切入方式import matplotlib.pyplot as plt import seaborn as sns plt.figure(figsize(10, 8)) sns.heatmap(df.corr(), annotTrue, fmt.2f, cmapcoolwarm, center0) plt.title(Feature Correlation Heatmap) plt.show()看完相关系数几个规律很明显PM2.5、PM10、SO2、NO2、CO与AQI的相关系数都在0.7以上PM2.5更是超过0.9说明颗粒物是AQI的主要贡献者这跟实际体感也吻合雾霾天PM2.5高AQI一定爆表。O3和AQI的相关系数偏弱甚至在某些时段为负这跟臭氧的生成机理有关——夏季强日照条件下臭氧浓度高但那时候颗粒物浓度反而低所以O3对AQI的影响是季节性的。温度、气压、湿度这类气象特征与AQI的相关性普遍在0.2~0.4之间单独看不强但它们是重要的修正变量。比如静稳天气下污染物不易扩散AQI容易走高这种交互效应相关性矩阵是看不出来的要靠模型去学。我还画了AQI随月份的折线图能看到典型的季节性曲线冬季11月到次年1月AQI明显抬升夏季7月到8月走低。这说明时间特征对预测很有价值后面做特征工程时要把月份、季节加进去。3. 特征工程与模型构建3.1 时间特征与滞后特征构造特征工程是决定模型上限的关键环节我在这个项目里做了两组特征。第一组是时间衍生特征。直接把日期拆成年、月、日、星期几再加一个季节标签。这样做是因为空气污染有强烈的周期性冬天取暖季燃煤排放增加叠加静稳天气污染加重周末和节假日的出行模式变化也会影响机动车尾气排放所以星期几这个特征不能丢。df[year] df.index.year df[month] df.index.month df[day] df.index.day df[dayofweek] df.index.dayofweek # 0周一, 6周日 def get_season(month): if month in [3, 4, 5]: return 1 elif month in [6, 7, 8]: return 2 elif month in [9, 10, 11]: return 3 else: return 4 df[season] df[month].apply(get_season)第二组是滞后特征和滑动窗口特征。污染物在大气中有一个累积和扩散的过程今天的AQI跟昨天、前天的AQI高度相关这种自相关性是树模型捕捉不到的必须显式构造出来。# 滞后特征前一天和一周前的AQI df[aqi_lag1] df[AQI].shift(1) df[aqi_lag7] df[AQI].shift(7) # 滑动平均过去7天的AQI均值 df[aqi_rolling7] df[AQI].rolling(window7).mean() # 滑动标准差过去7天的波动程度 df[aqi_rolling_std7] df[AQI].rolling(window7).std()这里有个关键细节构造滞后特征后前几行会因为shift产生NaN必须丢掉或填充不然模型训练时会报错或者学到错误模式。我直接dropna掉最早那几行反正数据量够不差这几条。注意做滞后特征时一定要站在预测时点去看。如果你要预测3月10日的AQI那么3月9日的AQIlag1到预测时已经是已知的这样才能作为特征。如果把未来3月11日的数据也构造进特征就是典型的数据泄漏训练时精度看着极高一到线上就崩。3.2 模型选型与训练集划分模型方面我选了三个做对比线性回归基线、随机森林、XGBoost。选这三个有代表性——线性回归验证特征有效性随机森林看集成学习的提升XGBoost是梯度提升的标杆也是这类表格型数据上最容易出效果的模型。训练集和测试集的划分是这个项目最容易出错的地方。很多初学者直接用train_test_split默认参数下它会随机打乱数据这在普通分类问题上没错但在时间序列数据上是致命的随机打乱意味着训练集里混入了未来的数据模型在测试集上的表现是“作弊”得来的上线后就会现原形。正确的做法是按时间顺序切分from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.ensemble import RandomForestRegressor from xgboost import XGBRegressor from sklearn.linear_model import LinearRegression from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error # 先去掉构造滞后特征产生的前几行NaN df_clean df.dropna() features [PM2.5, PM10, SO2, NO2, CO, O3, TEMP, PRES, HUMI, WIND, month, dayofweek, season, aqi_lag1, aqi_lag7, aqi_rolling7, aqi_rolling_std7] X df_clean[features] y df_clean[AQI] # 按时间排序后按比例切分前80%训练后20%测试 split_idx int(len(df_clean) * 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:] # 标准化树模型可做可不做这里为了对比线性模型统一做 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)为什么选8:2而不是更常见的7:3因为时间序列数据越靠后的日期越接近实际使用场景如果训练集太短模型学不到早期年份的完整季节周期特别是冬季污染高发期的模式会被截断导致预测值普遍偏低。4. 训练结果与预测评估4.1 三个模型的对比结果训练代码不复杂直接fit就好。几个通用参数值得说随机森林我设了n_estimators300max_depth限制为10防止过拟合XGBoost用了learning_rate0.05配合n_estimators300加深了max_depth到6。这些参数我是先跑了一组默认参数再用手动粗调试出来的没做网格搜索——数据量不大网格搜出来的提升有限性价比不高。模型训练完我统一用三个指标评估R²决定系数越接近1越好、MAE平均绝对误差单位与AQI相同、RMSE均方根误差对大偏差更敏感。结果如下模型R²MAERMSE线性回归0.7828.341.2随机森林0.9213.620.4XGBoost0.9510.816.9线性回归R²只有0.78MAE将近30说明AQI与特征之间不是简单的线性关系比如风速对污染物扩散的影响就是非线性衰减的湿度对颗粒物吸湿增长的作用也有阈值效应。树模型能把这种非线性关系学出来所以随机森林和XGBoost明显拉开差距。XGBoost比随机森林高出的部分主要来自它对特征交互的建模能力更强特别是它的逐轮残差拟合在样本量不算大的情况下优势更明显。如果你只追求简单随机森林已经够用了如果想要更好的精度XGBoost是值得的训练时间也就是十几秒的事。4.2 预测效果可视化分析指标只是数字要直观看到预测效果把真实值和预测值画在一起是最直观的方式。我取了测试集最后90天做对比展示import matplotlib.pyplot as plt plt.figure(figsize(14, 6)) plt.plot(y_test.index[-90:], y_test.values[-90:], labelTrue AQI, colorblack) plt.plot(y_test.index[-90:], y_pred_xgb[-90:], labelXGBoost Pred, colororange, alpha0.8) plt.plot(y_test.index[-90:], y_pred_rf[-90:], labelRF Pred, colorblue, alpha0.6) plt.legend() plt.title(AQI Prediction: True vs Predicted (Last 90 Days)) plt.xlabel(Date) plt.ylabel(AQI) plt.show()看曲线能发现两个有意思的规律。第一模型对平滑区间的预测很准比如夏天连续几天AQI在30~50之间徘徊时预测值和真实值几乎重合。第二模型对突变峰值总是预测偏低——比如某天突然从80冲到180模型虽然有滞后特征帮忙但突变日的信息在特征里根本没有体现只有等到第二天数据才能补上。这是数据驱动模型的天然局限不是调参能解决的。残差分析也值得做。我画了残差真实值减预测值随预测值的散点图发现当预测值大于150时残差明显变大且偏正说明模型在重污染天容易系统性低估。如果你想把模型用在预警场景这个偏差要特别留意可以考虑在后面接一个分类模型专门预测“明天会不会是重污染日”。5. 实操中的常见坑与排查清单5.1 数据泄漏头号杀手做这个项目最容易犯、也是后果最严重的错误就是数据泄漏。除了前面说的随机打乱训练集还有两种常见的泄漏方式我都被坑过。第一种是滞后特征里的隐藏泄漏。如果数据里有当天的气象特征比如你用3月10日的实际温度、湿度去预测3月10日的AQI这在预测场景下是合理的因为气象数据是可以实时监测的。但如果你拿3月10日的实际AQI去预测同一天的AQI那就是自欺欺人了——AQI本身就是我们要预测的目标把它当特征当然预测得准。第二种是预处理时用了全量数据统计量。比如先对整个数据集做标准化再做训练集测试集切分这时候scaler在计算均值和方差时已经偷看了测试集的信息。正确做法是先fit在训练集上再transform测试集我在代码里就是这么写的千万别图省事把scaler.fit_transform套在整个X上。5.2 时间序列划分与交叉验证的陷阱时间序列项目上直接套用sklearn的KFold会出问题这一点怎么强调都不为过。KFold默认打乱样本训练集和验证集就不再是时间连续的结果就是模型“提前知道了未来”验证集分数虚高。如果有做交叉验证的需求用TimeSeriesSplit它保证训练集永远是验证集之前的数据。我实测过同一个XGBoost模型用KFold交叉验证平均R²是0.97换成TimeSeriesSplit就只有0.94这3个点的差距全是泄漏带来的水分。做模型对比或者报告成绩时一定要说明用的是哪种划分方式不然数字没有参考价值。5.3 特征与目标的口径对齐最后提醒一个细节AQI和污染物浓度的统计口径要一致。有些数据集里AQI已经是综合计算后的结果而污染物浓度是当天的24小时均值这两者本身就有时间窗口的差异。另外如果数据里有AQI的等级分类字段优、良、轻度污染等训练回归模型前要确认它就是从AQI数值映射过来的没必要重复使用否则又是变相泄漏。还有个阴间bug我帮同学排查过他的代码里用df.shift(1)构造滞后特征结果shift之后没有排序直接把最后一行的NaN填了0导致预测结果奇奇怪怪。时间序列操作一定要确保索引有序shift、rolling这些操作在乱序索引下会出现错位排查起来特别费劲。写在最后的实操体会这个项目我带着好几届同学跑过每次感受最深的就是数据清洗和特征工程花的时间至少是建模的三倍。模型本身反而是最简单的一环sklearn里调几行代码就出来了。你能拿到什么程度的预测精度八成都取决于你对数据的理解深度——是否发现了季节规律、是否构造了有效的滞后特征、是否避开了数据泄漏的坑。如果你跑完这个项目还有余力我建议往三个方向扩展一是把逐日数据换成逐小时数据预测精度会更高但噪声也更大二是试试LightGBM或者CatBoost跟XGBoost对比一下训练速度和精度权衡三是加一个AQI等级分类任务把回归问题扩展成多分类对理解模型评估指标会更全面。机器学习这东西光看不练永远是雾里看花动手跑一遍、踩几个坑比什么都管用。本文还有配套的精品资源点击获取

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

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

免费获取报价