ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

线性回归实战:PM2.5预测机器学习大作业完整指南

2026/10/3 14:01:59 拓冰建站 浏览量
线性回归实战:PM2.5预测机器学习大作业完整指南 简介一份机器学习课程大作业的完整实现围绕合肥地区PM2.5浓度预测任务包含Python源码与配套数据。项目面向机器学习初学者及数据科学相关课程学生可帮助理解线性回归建模全流程从历史空气质量数据采集、特征矩阵构造到梯度下降法优化参数。压缩包共21个文件大小2.58MB含12个CSV数据文件覆盖训练集、测试集、拼接特征及预测结果3个PY脚本实现数据预处理、模型训练与评估另有NPY模型参数、说明文档和示意图目录结构清晰便于直接运行和二次开发。已有280人学习下载。读者可借此掌握线性回归的矩阵形式与梯度更新公式获得完整实验记录与可复现预测流程适用于课程设计或入门回归预测的实战参考。1. 基于线性回归的PM2.5预测一次能“跑通也能讲清”的机器学习大作业机器学习大作业选什么题直接决定你期末周的心态。在《机器学习》课程里基于线性回归的PM2.5预测源码这类项目是数据科学方向最稳的选择之一数据集公开易得算法原理课上学过代码量不大可视化效果好论文和答辩都有话可说。它解决的问题很具体——根据气象条件、风速、湿度等观测值预测空气中PM2.5浓度本质上是“用历史数据拟合一个连续函数”的回归任务。新手能借此完整走一遍数据分析的流程熟手则可以在特征工程和模型对比上挖深度。这篇笔记我会把从数据清洗到模型评估的全过程拆开连同我踩过的坑一起写出来你可以照着复现也可以直接作为大作业的源码骨架。2. 数据与特征准备PM2.5预测的“地基”决定模型上限2.1 认识数据从UCI北京空气质量数据集里选对字段做PM2.5预测公开数据集中最常用的是UCI机器学习库里的Beijing PM2.5 Data Set。它记录了2010年到2014年北京某个监测站逐小时的空气质量与气象观测值。常见的做法是拿到一份CSV文件每行代表一个小时字段包括year年、month月、day日、hour时、pm2.5目标变量、DEWP露点温度、TEMP摄氏温度、PRES气压、cbwd组合风向、Iws累计风速、Is累计降雪、Ir累计降雨。我一般先把数据读进来用Pandas做初步观察。关键的一步是确认数据的时间跨度、字段类型和缺失情况这决定后面特征工程怎么做。import pandas as pd df pd.read_csv(PRSA_data.csv) print(df.shape) # 看有多少行多少列 print(df.dtypes) # 检查每列数据类型 print(df.isnull().sum()) # 统计缺失值 print(df.head(10)) # 看一眼前10行逻辑说明df.shape返回一个元组第一个元素是行数第二个是列数。PM2.5数据集通常是43800行左右4年×365天×24小时但实际会有缺失所以我们看到的具体行数会略有出入。df.dtypes用来确认数值列和对象列其中cbwd是字符串对象类型后面要单独处理。df.isnull().sum()能快速定位哪些列有缺失预警数据质量。参数说明如果你没下载到这个数据集也可以用pd.read_csv读入任何包含PM2.5浓度和时间字段的表格但要注意字段名一致。我建议把year、month、day、hour四个字段合并成一个datetime列方便后续按时间筛选和排序。因为大部分数据处理库的索引操作对datetime列支持得最好。2.2 缺失值与异常值处理预测前必须做的两步清洗PM2.5数据集最常见的缺失有两种一是pm2.5列本身存在连续的NaN可能因为设备维护导致停机二是某些小时记录干脆没有对应行。很多新手上来就dropna()结果发现可用数据少得可怜模型根本训不动。这里我给你我的处理思路先看缺失比例再决定是填充还是丢弃。# 先按时间排序防止原始数据乱序 df[datetime] pd.to_datetime(df[[year, month, day, hour]]) df df.sort_values(datetime).reset_index(dropTrue) # pm2.5缺失比例超过30%就直接丢行否则用前向填充 missing_ratio df[pm2.5].isnull().mean() if missing_ratio 0.3: df df.dropna(subset[pm2.5]) else: df[pm2.5] df[pm2.5].fillna(methodffill) # 观察替换后pm2.5的分布 print(df[pm2.5].describe())逻辑说明pd.to_datetime把拆分的时间字段合成一列底层的实现是逐字段拼接后解析效率不算高但胜在直观。sort_values按时间排序是必须的因为线性回归假设样本独立同分布若打乱顺序训练集中会混入未来数据。fillna(methodffill)是前向填充用上一个观测值补当前缺失值这比均值填充更符合PM2.5的时序特性——污染物浓度在短时间内有连续性。参数说明missing_ratio阈值0.3是经验值。缺失超过30%时前向填充会引入大量重复样本造成模型对这组数据的过拟合。低于这个阈值比如某个月的数据断了几小时前向填充是安全的。你要注意fillna(methodffill)默认不会填充头部缺失所以还得加一个bfill()把开头几行的NaN补上。异常值这部分很多人会用3σ原则或者IQR去筛PM2.5浓度但我建议谨慎操作。因为PM2.5的分布右偏严重重污染日的浓度可能远超均值加三倍标准差直接删掉反而让模型失去对极端污染事件的预测能力。我一般只删除明显不可能的负值和超过监测上限的异常记录。2.3 特征工程时间周期、风向编码与滞后特征线性回归对特征的要求是“每个特征与目标变量之间存在相对线性的关系”而PM2.5浓度与气象变量的原始关系并不线性。但有两个特征工程技巧能让线性模型的表现显著提升。第一个是时间周期特征。PM2.5有明显的小时周期早晚高峰高、午后低和季节周期冬季高、夏季低。如果把hour直接当作数值特征模型会学到“小时越大PM2.5越高”的错误关系。标准解法是把时间拆成正弦和余弦分量import numpy as np df[hour_sin] np.sin(2 * np.pi * df[hour] / 24) df[hour_cos] np.cos(2 * np.pi * df[hour] / 24) df[month_sin] np.sin(2 * np.pi * df[month] / 12) df[month_cos] np.cos(2 * np.pi * df[month] / 12)逻辑说明np.sin和np.cos将线性时间映射到单位圆的坐标上本质是保留了“23点和0点接近”这个直觉——凌晨23点和0点都是低排放时段但数值上23和0在普通线性特征里距离很远。周期编码后相似时间点在二维平面上的距离就小了线性模型也就学得到这种周期性。参数说明分母的24和12分别对应小时和月的周期长度。如果你想表达“工作日和周末的差别”也可以加一个is_weekend二元特征。但要注意线性回归对特征尺度敏感不同特征的取值范围差异较大时需要考虑标准化否则梯度下降训练会震荡。第二个是滞后特征。PM2.5浓度是惯性系统上一小时的浓度对当前值影响极大。构造滞后特征的方法是对目标变量做shift操作df[pm2.5_lag1] df[pm2.5].shift(1) df[pm2.5_lag24] df[pm2.5].shift(24) df df.dropna() # shift产生的NaN行直接删掉逻辑说明shift(1)取上一小时的值shift(24)取昨天同一小时的值分别捕捉短期惯性和日周期基准。加入滞后特征后线性回归的R²通常能从0.4左右提升到0.7以上这是整个项目投入产出比最高的步骤。参数说明shift步长越大计算时要用越多的历史数据训练集也会因dropna减少。做大作业时你可以对比“只加lag1”“加lag1和lag24”“再加lag2”三种配置的效果写进报告里就是一组漂亮的消融实验。风向字段cbwd是分类变量可选项为NE、NW、SE、SW和cv静风。线性回归不能直接吃字符串常见做法是pd.get_dummies做独热编码df pd.get_dummies(df, columns[cbwd], drop_firstTrue)逻辑说明drop_firstTrue会删掉第一个类别列避免“虚拟变量陷阱”——当分类变量有k个取值时只需k-1个二元特征就能完整表达否则线性回归的解析解会因为特征完全共线性而无法计算。参数说明如果你用的是statsmodels的OLS它内部会检测完全共线性并自动处理但scikit-learn的LinearRegression不会报错而是给出一个“看似正确但系数不可解释”的模型。所以在大作业里我会明确加上drop_firstTrue说明参考文献里通常引用的是含k-1个哑变量的模型。2.4 划分训练集与测试集时间序列数据的切分原则这是新手最容易翻车的环节没有之一。很多教程用train_test_split(..., random_state42)做随机切分但那是针对独立样本的。PM2.5是时间序列相邻小时之间的样本高度相关随机切分会让测试集混入“训练集时刻的后一小时”模型表现虚高得离谱答辩时一问就穿帮。我用的切分方法是按时间顺序取前80%做训练最后20%做测试train_size int(len(df) * 0.8) train df.iloc[:train_size].copy() test df.iloc[train_size:].copy() feature_cols [DEWP, TEMP, PRES, Iws, Is, Ir, hour_sin, hour_cos, month_sin, month_cos, pm2.5_lag1, pm2.5_lag24] X_train train[feature_cols].values y_train train[pm2.5].values X_test test[feature_cols].values y_test test[pm2.5].values逻辑说明iloc[:train_size]是按位置切片不是按时间筛选但因为前面已经做了sort_values所以位置顺序就是时间顺序。这样划分后测试集完全在训练集的时间之后模型在测试集上的误差才能真实反映它的泛化能力。参数说明0.8这个比例适合数据量为4年的场景。如果你的数据只有一年建议提高到0.85保证测试集仍有足够多的样本。注意feature_cols列表里没有包括year、month、day这些原始时间字段因为它们已经被周期特征替代如果你保留了原始数值字段模型会学到“某一年整体偏高”这种虚假关联这是另一个大作业里常见的错误。3. 线性回归建模与源码实现从公式到可运行的完整代码3.1 最小二乘原理为什么线性回归能拟合污染扩散线性回归算法的假设是目标变量y可以用特征X的线性组合加上误差项表示。形式化地写就是y X·w b ε。PM2.5的浓度变化虽然受化学反应、二次生成等非线性机制影响但在短时段内污染物扩散与风速、湍流、湿度等气象条件的关系可以用线性近似描述这就是为什么线性回归在这个问题上能有不错的基线效果。训练目标是最小化残差平方和RSS(w) Σ(yᵢ - Xᵢ·w)²。它的闭式解是w (XᵀX)⁻¹Xᵀy也就是最小二乘估计。当特征数量不多本例只有十几个样本量有几万个时闭式解的计算复杂度为O(n·d²)训练时间在毫秒级远比训练一个神经网络的成本低。scikit-learn的LinearRegression默认采用最小二乘的奇异值分解实现数值稳定性好不需要手动计算矩阵求逆。这里要提一个概念线性回归对特征工程的依赖远高于对模型调参的依赖。你做同样的特征处理用sklearn的LinearRegression和用PyTorch写一个单层全连接网络结果几乎一样。所以大作业的重点不是换模型而是把特征解释清楚。3.2 模型训练代码sklearn这样用才对很多人的第一版代码会漏掉标准化直接裸特征训练。在特征量级差异大时比如PRES是1000左右的量级Is是0到几毫米梯度下降会有收敛问题。虽然sklearn的LinearRegression用正规方程求解理论上对尺度不敏感但加了L2正则的Ridge或使用梯度优化的变体就受影响了。这里我建议用Pipeline统一处理既干净又能在答辩时展示“工程规范”。from sklearn.linear_model import LinearRegression from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline model make_pipeline( StandardScaler(), LinearRegression() ) model.fit(X_train, y_train) y_pred model.predict(X_test)逻辑说明make_pipeline把标准化和线性回归串成一个对象fit时先计算训练集的均值、方差然后对X做变换再送入回归器。测试时predict会自动用同一组标准化参数处理X_test不会把测试集信息泄漏到训练过程中。参数说明StandardScaler对每列做(z - mean) / std把特征的分布拉成均值0、标准差1。LinearRegression()默认带截距项参数fit_interceptTrue所以不需要额外加一列常数特征。如果你的特征中存在高度相关的列可以用Ridge(alpha1.0)之类加正则的模型替代后面避坑章会详细说。训练完成后我习惯把权重打印出来看一眼正负方向这一步虽然不产指标但对理解模型很关键import pandas as pd # 取pipeline最后一步的模型 lr model.named_steps[linearregression] coef_df pd.DataFrame({ feature: feature_cols, coef: lr.coef_ }).sort_values(coef, keylambda s: s.abs(), ascendingFalse) print(coef_df)逻辑说明model.named_steps可以按名称访问Pipeline里的组件。打印coefficients后你会看到pm2.5_lag1的系数最大且为正说明上一小时浓度对当前浓度影响最大这符合物理直觉Iws风速系数为负说明风速越大污染物扩散越快浓度越低。这些观察写进大作业报告里能显著加分。参数说明.sort_values的key参数是pandas 1.1以上版本才支持的功能功能是按系数绝对值排序。低版本环境可以先生成coef_df[abs_coef] coef_df[coef].abs()再排序效果一样。3.3 模型评估RMSE、MAE与R²怎么解读线性回归预测PM2.5的评估指标大作业里最常用三个均方根误差RMSE、平均绝对误差MAE和决定系数R²。它们的侧重点不同需要结合起来看。from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score import numpy as np rmse np.sqrt(mean_squared_error(y_test, y_pred)) mae mean_absolute_error(y_test, y_pred) r2 r2_score(y_test, y_pred) print(fRMSE: {rmse:.2f} μg/m³) print(fMAE: {mae:.2f} μg/m³) print(fR²: {r2:.4f})逻辑说明mean_squared_error计算平均平方误差开根号后量纲回到μg/m³方便直观理解误差大小。mean_absolute_error直接求绝对差平均受极端值影响比RMSE小。r2_score计算决定系数表示模型解释了测试集目标变量方差的百分比。一套好的结果大概是RMSE在30到50 μg/m³之间R²在0.7到0.85之间具体取决于特征工程做了多少如果加入滞后特征后R²还不到0.5那大概率是数据切分或特征选择出了结构性问题。参数说明这三个指标都要求y_test和y_pred长度一致且都是数值型数组。训练集的R²往往比测试集高5到10个百分点这是正常现象。如果训练集R²接近0.99而测试集只有0.3说明过拟合需要检查是否把未来信息泄漏进了训练集或者没有做时间切分。3.4 结果可视化把预测值和真实值画在一起可视化是评估回归模型最直观的手段也是大作业报告里的“门面”。我一般会画三张图时间序列对比图、散点图、残差图。这里先给时间序列对比图和散点图的代码。import matplotlib.pyplot as plt plt.figure(figsize(12, 5)) # 只画最近72小时避免线条太密 n_show 72 plt.plot(test[datetime].iloc[:n_show], y_test[:n_show], labelActual, linewidth1.5) plt.plot(test[datetime].iloc[:n_show], y_pred[:n_show], labelPredicted, linewidth1.5, alpha0.8) plt.xlabel(Time) plt.ylabel(PM2.5 (μg/m³)) plt.legend() plt.xticks(rotation45) plt.tight_layout() plt.savefig(pm25_prediction_trend.png, dpi150)逻辑说明test[datetime].iloc[:n_show]取测试集的前72个时间点这样横轴是真实的时间刻度图例标在右上角。预测值用半透明线条叠加能直观看到模型能不能跟上浓度起伏的节奏。参数说明figsize(12,5)是宽屏比例适合横向时间轴。dpi150保证导出图片清晰。如果你用的是jupyter notebook加一行%matplotlib inline就能嵌入显示。散点图用来检查预测值是否存在系统性偏差plt.figure(figsize(6, 6)) plt.scatter(y_test, y_pred, s3, alpha0.5) plt.plot([0, max(y_test)], [0, max(y_test)], colorred, linestyle--, linewidth1) plt.xlabel(Actual PM2.5) plt.ylabel(Predicted PM2.5) plt.tight_layout() plt.savefig(pm25_scatter.png, dpi150)逻辑说明plt.scatter中横轴是真实值纵轴是预测值。如果模型很好点会密集分布在红色对角线附近。当点在对角线上方聚集说明模型系统性高估在下方则低估。PM2.5预测的一个规律是在低浓度段0到50点通常较为集中在高浓度段大于200预测值往往偏低这是因为高浓度事件在数据集中出现频率低模型“没见过足够多的极端样本”。参数说明s3控制散点大小在数据量大时防止重叠。alpha0.5让重叠区域的密度更明显。max(y_test)用于确定对角线的范围保证对角线从原点到测试集最大浓度值。4. 线性回归预测PM2.5的5个避坑记录现象、原因与解决办法4.1 预测值出现负数现象模型跑完预测结果里有相当一部分PM2.5浓度是负的比如-15.3 μg/m³。这在物理上完全不合理——浓度不可能是负数。原因线性回归的本质是用直线拟合数据在特征取值落在训练集分布边缘时直线外推就会越过零轴。PM2.5数据集中在低浓度区域当风速大、湿度低的组合出现时模型很容易把预测值推成负的。解决最直接的办法是对预测结果做下限截断y_pred_clipped np.clip(y_pred, a_min0, a_maxNone)np.clip把数组中小于0的元素改成0大于等于0的保持不变。这样处理后RMSE会略微下降一点因为原来的绝对值误差变小了但指标更贴近实际意义。要注意这个操作最好只在测试集上做不参与训练。想在模型内部解决可以改用Lasso或带非负约束的优化器但对大作业来说截断就够了。4.2 用train_test_split随机切分导致评估虚高现象测试集R²高达0.9以上RMSE低得离谱换成时间切分后又掉到0.7左右。答辩时老师问一句“你的测试集是怎么划分的”场面会很尴尬。原因前面说过PM2.5是时间序列数据相邻样本间存在强自相关。随机切分把同一天甚至相邻小时的数据一部分放进训练集、一部分放进测试集本质上是让模型看了“答案”再去考试。解决严格按照时间顺序划分并且报告中要写清“训练集为前80%的时间段测试集为后20%的时间段”。比较规范的做法是先把数据按时间排序再取连续区间。注意划分之后不要做任何跨区间的特征统计尤其是标准化时只能用训练集的均值和方差。4.3 风向类别变量直接编码成0/1/2/3数值现象模型训练不报错但画出系数发现风向特征的系数特别大且不同随机种子下符号不稳定。原因把字符串类别映射成整数比如{NE: 0, NW: 1, SE: 2, SW: 3, cv: 4}等于强行给类别排序线性回归会把这个数字当成真实的数量关系——“风向4”不应该是“风向1”的四倍效果。这种编码方式既破坏了类别的无序性又引入了虚假的线性关系。解决使用pd.get_dummies做独热编码或者用sklearn.preprocessing.OneHotEncoder。注意独热编码后特征数量从一个变成k-1drop_first所以特征列数会变化模型输出的coef向量长度也比之前多几个维度这份不要和旧的参数数组混用。另外独热编码列之间存在负相关一个样本只能命中一个风向但drop_first已经消了解析解问题不必再担心。4.4 PRES和TEMP高度相关导致系数符号与直觉相悖现象模型跑出来后PRES气压的系数为正你原本预期气压升高对应天气转晴、污染物易于垂直扩散系数应该是负的。换一组训练数据后系数的正负号又变了。原因气压和温度之间存在较强的负相关冷高压天气温度低多个特征之间存在多重共线性。最小二乘系数在共线性存在时方差会变得非常大符号翻转完全依赖于训练集中微小扰动这被戏称为“系数符号不稳定”。解决检查特征相关性矩阵并做取舍corr train[[TEMP, DEWP, PRES, Iws, pm2.5]].corr() print(corr)如果发现PRES和TEMP的相关系数绝对值超过0.7就只保留其中更可信的一个。我个人习惯保留TEMP而删掉PRES因为露点温度DEWP在物理上对PM2.5的影响更直接。另一个做法是改用Ridge回归L2正则能约束系数幅度一定程度上缓解共线性导致的符号不稳定代价是R²可能降低0.01到0.02。4.5 只看R²不检查残差现象R²有0.75报告写得挺满但把残差真实值减预测值按时间画成图发现一片片连续的正残差或负残差成块出现模型在一个时间段系统性低估、在另一个时间段系统性高估。原因R²衡量的是整体方差解释程度但时间序列里模型很可能在特定天气过程或季节里犯相似的错误这些错误在整体指标中被平均掩盖了。残差的时序自相关正是“模型没捕捉到某些动态因素”的信号。解决画出残差图并计算残差的自相关系数residuals y_test - y_pred plt.figure(figsize(12, 3)) plt.plot(test[datetime], residuals, linewidth0.5) plt.axhline(y0, colorred, linestyle--) plt.ylabel(Residual (μg/m³)) plt.tight_layout() plt.savefig(pm25_residuals.png, dpi150)逻辑说明residuals y_test - y_pred是逐样本求差plt.axhline(y0)画一条零参考线。理想的残差图应该像一条均匀分布在零线两侧的“噪声带”没有明显的长周期波动。如果残差在连续大块时间里都在零线以上说明模型在那段时间里系统性低估浓度而在另一些时段系统性高估。这通常提示缺少重要特征比如季节趋势或一次沙尘事件影响。大作业里残差分析是展示你“有诊断能力”的亮点不建议省掉。5. 从大作业到可展示的项目模型对比、滚动预测与答辩准备5.1 用残差的正态性验证模型假设线性回归的形式化假设里有一条是“误差项独立且服从零均值、同方差的正态分布”。大作业可以做一个简单的正态性检验对残差做标准化画出直方图或者用scipy.stats.shapiro检验。但要注意样本量大时Shapiro检验很敏感几千条数据下几乎必然拒绝正态性所以重点不是“是否通过检验”而是画出分布看在哪个位置偏离。PM2.5预测的残差通常是右偏的——模型对高浓度事件低估造成正残差的尾巴更长。知道这一点在答辩时就能主动说明“线性回归的局限”而不是等着被老师问。5.2 滚动预测与多步预测的陷阱如果你想让大作业更进一步可以做多步预测并指出滚动预测中的误差累积。常见方案是用t时刻的真实观测去预测t1再用这个预测值作为t1的输入、预测t2依此类推。代码如下# 从测试集第一个样本开始滚动预测未来24小时 predictions [] current_input X_test[0].copy() for step in range(24): pred model.predict(current_input.reshape(1, -1))[0] pred np.clip(pred, 0, None) predictions.append(pred) # 更新滞后特征 current_input[-2] pred # pm2.5_lag1 # pm2.5_lag24 在步长24时保持原值24步后需更新 if step 1 24: current_input[-1] predictions[step 1 - 24]逻辑说明current_input最初是X_test的第0行包含真实历史信息。每次预测后要把新预测值写入pm2.5_lag1的位置即特征列表的倒数第二列供下一次预测使用。pm2.5_lag24在前24小时内仍是真实历史值超过24步后才用24步前的预测值填充。参数说明这段代码用current_input[-2]定位特征是硬编码它要求特征列表顺序固定。实际项目中我更建议用一个字典保存特征值按名称更新避免索引错位。滚动24步预测的RMSE通常会比单步预测高20%到50%误差累积是线性模型做多步预测遇到的自然瓶颈。5.3 与决策树/随机森林对比让大作业有“讨论深度”大作业报告只写线性回归内容会比较单薄。常用的对比方向是随机森林它对非线性关系和特征交互有更强的拟合能力。在同一套特征、同一个时间切分下跑通随机森林对比两者在测试集上的RMSE和R²from sklearn.ensemble import RandomForestRegressor rf_model RandomForestRegressor( n_estimators200, max_depth15, min_samples_leaf5, n_jobs-1, random_state42 ) rf_model.fit(X_train, y_train) rf_pred rf_model.predict(X_test) r2_rf r2_score(y_test, rf_pred)逻辑说明RandomForestRegressor对特征量纲不敏感所以不需要StandardScaler。n_estimators200表示200棵决策树max_depth15限制每棵树的深度防止过拟合min_samples_leaf5保证叶子节点最少有5个样本。参数说明随机森林在PM2.5上的表现通常会略优于线性回归R²可能从0.78提升到0.83但训练时间从不到1秒变成几十秒。你要在报告中把结论写成“线性回归在简洁性与可解释性上有优势随机森林在高浓度事件的预测上略好”这是一句既诚实又稳妥的总结。5.4 答辩前必做的三个验证实验第一个是预测值的分布对比。把测试集真实值和预测值各自画直方图检查模型是否“学歪了”比如预测值整体方差变小回归到均值效应。这几乎是线性回归的通病承认比回避好。第二个是对极端天气日期做案例分析。选几天重污染事件把你的预测值和真实值画在一起讨论模型在哪一步跟丢了。这类分析能展示你不仅会调包还有一线工程师式的诊断直觉。第三个是特征删除的敏感性分析。每次删掉一列特征重新训练并记录R²的变化。删除pm2.5_lag1后R²大幅下降说明模型对历史浓度依赖强删除Iws后R²小幅下降说明风速对扩散过程有增量解释力。这个实验写进报告等于把特征工程环节从“我用了这些特征”升级成“我验证了这些特征的价值”。做机器学习大作业这几年我最大的教训是数据清洗阶段偷的懒最后都会变成评估阶段无法解释的误差。线性回归模型本身没什么玄学真正让结果翻车的往往是切分、填充和编码这些不起眼的环节。希望这份项目笔记能帮你在期末周少踩几个坑把时间花在真正能加分的分析和论证上。本文还有配套的精品资源点击获取