☰
随机森林回归实战:空气质量PM2.5预测全流程
2026/9/26 4:32:47 网站建设 项目流程

简介:面向数据挖掘学习者的随机森林空气质量污染预测实战资源,适合具备Python基础、希望掌握分类建模完整流程的读者。包内共3个文件:ipynb代码文件承载从数据清洗、特征工程到随机森林训练与评估的完整分析流程;csv为处理后的污染数据集,可直接用于复现实验;html为分析结果的静态展示,便于快速浏览关键结论。资源整体仅616KB,轻量易得。目前已有129人学习,可用于课程设计、期末项目或入门级数据挖掘比赛的赛题练手。下载后既可作为独立项目按步骤运行,也可对照代码逐行理解随机森林在空气质量预测中的应用细节,节省自行找数据与搭建环境的时间。

1. 空气质量污染预测:随机森林为什么是数据挖掘赛道的常青树

去年做空气质量污染预测模型时,我把常见的数据挖掘实战套路都过了一遍,最后留在生产环境里的不是梯度提升树,而是随机森林算法。原因很朴素:你的训练集里总有那么几天传感器断线,PM10 莫名飙到 600 的异常值,还有湿度与污染物之间说不清道不明的非线性关系——线性回归对这类脏数据毫无还手之力。随机森林对缺失值不敏感、能自动做特征交互、训练速度快,先用它跑出基线,再谈要不要上更重的模型。

标题里这个包就是这条思路的完整落地版:数据集加代码,从 CSV 原始表到模型预测一条线走完。适合刚学完 python 数据分析与数据挖掘实战、想跑通第一个完整预测项目的人,也适合手头有环境监测数据、想快速验证随机森林回归算法能不能用在自己业务里的从业者。下面从数据清洗开始,把整条链路拆开讲透。

2. 数据集清洗与特征工程:把气象观测表变成随机森林能吃的训练集

2.1 解压 .rar 后,先确认数据集的字段含义

这类包解压后一般是一份 CSV、一份字段说明和一段训练脚本,具体以你拿到的目录为准。第一步不是急着跑代码,而是打开 CSV 看清楚每一列是什么。空气质量数据不是 iris 那种几十行的玩具数据集,它的脏数据问题很典型:时间戳不连续、传感器零值、量纲差异大。

常见的字段大概有这些:

字段含义在模型里的角色
date观测时间,小时级时间键,切分训练测试集的依据
PM2.5细颗粒物浓度(μg/m³)预测目标
PM10可吸入颗粒物浓度相关污染物特征
SO2、NO2、CO、O3气态污染物浓度污染物特征
temp、pressure、humidity温度、气压、湿度气象条件
wind_speed、wind_dir风速、风向扩散条件
是否工作日、节假日0/1 标记活动水平特征

先用df.info()和df.describe()看缺失值分布和量纲差异。如果 PM2.5 的均值在 50 左右而 CO 只有个位数,这不是问题,树模型不怕量纲差异;但你得知道每个列的取值范围,后面做异常值裁剪时心里有数。

2.2 缺失值与异常值:先修数据再谈建模

空气质量数据最常见的缺失原因有三个:设备校准、通信断线、站点维护。短的连续缺失用线性插值能补得比较自然,长段缺失就别硬插了,用同时段的滚动均值回填更稳妥。异常值处理我一般用 IQR 裁剪而不是直接删除,因为删除一条时间序列记录会打破时间连续性,后面的滞后特征跟着一起错位。

import pandas as pd import numpy as np df = pd.read_csv('air_quality.csv', parse_dates=['date']) df = df.sort_values('date').reset_index(drop=True) # 先定义特征列,目标列单独放 feature_cols = ['SO2', 'NO2', 'CO', 'O3', 'PM10', 'temp', 'pressure', 'humidity', 'wind_speed', 'wind_dir'] target = 'PM2.5' # 短缺失:按时间顺序线性插值,最多连续补 6 个点 df[feature_cols] = df[feature_cols].interpolate(method='linear', limit=6) # 长缺失:用过去 24 小时的滚动均值回填 for col in feature_cols: if df[col].isna().sum() > 0: df[col] = df[col].fillna(df[col].rolling(24, min_periods=1).mean()) # 异常值裁剪:用 3 倍 IQR 作为边界 Q1 = df[feature_cols].quantile(0.25) Q3 = df[feature_cols].quantile(0.75) IQR = Q3 - Q1 lower = Q1 - 3 * IQR upper = Q3 + 3 * IQR df[feature_cols] = df[feature_cols].clip(lower, upper, axis=1)

这段代码里有两个参数值得说。limit=6是插值的最大连续缺失数,超过 6 个点的连续缺失宁可保留,也不要让插值凭空虚造一段曲线。rolling(24, min_periods=1)的窗口对应 24 小时,因为空气质量有明显日周期,用前一天的均值来回填长缺失比用全局均值合理得多。

clip裁的是异常值而不是删行。传感器偶尔会爆出一个 800 的 PM2.5 读数,这种值可能真实,也可能来自故障,删掉它你会丢掉当天的全部信息,裁剪到边界至少保留“这天污染很重”的信号。

2.3 特征工程:滞后项、滚动统计与周期编码

随机森林不能直接理解“昨天下午的风向让 PM2.5 降下来了”这种时序关系,你得把历史信息显式做成特征。滞后特征是空气污染预测的重中之重:当前时刻的 PM2.5 高度依赖过去几小时的浓度和气象条件。滚动统计能把“持续变差”和“偶尔飙高”的区别表达出来。

# 滞后特征:过去 1、3、6、24 小时的 PM2.5 for lag in [1, 3, 6, 24]: df[f'pm25_lag_{lag}h'] = df[target].shift(lag) # 滚动统计:最近 6 小时的均值、最大值、标准差 df['pm25_roll_mean_6h'] = df[target].rolling(6).mean() df['pm25_roll_max_6h'] = df[target].rolling(6).max() df['pm25_roll_std_6h'] = df[target].rolling(6).std() # 风向是环形变量,0 度和 360 度是同一个方向,直接当数值会出错 df['wind_dir_sin'] = np.sin(np.deg2rad(df['wind_dir'])) df['wind_dir_cos'] = np.cos(np.deg2rad(df['wind_dir'])) # 周期特征:小时、星期几、是否周末 df['hour'] = df['date'].dt.hour df['dayofweek'] = df['date'].dt.dayofweek df['is_weekend'] = (df['dayofweek'] >= 5).astype(int) # 滞后和滚动会引入 NaN,丢掉建模起点前的不完整行 df = df.dropna().reset_index(drop=True)

滞后项为什么重要?空气质量预测本质上是在做时间序列的自回归,pm25_lag_1h通常会是所有特征里重要度最高的一个。hour和dayofweek则帮模型捕捉早晚高峰和周末效应——很多城市的 PM2.5 在工作日早高峰有明显抬升,这不是气象要素能解释的。

wind_dir_sin和wind_dir_cos这个编码常被新手忽略。风向 0 度和 360 度物理上是同一个方向,如果不做环形编码,模型会把 0 和 360 当成距离很远的两个值,学习出一段根本不存在的关系。

2.4 切分数据集:按时间切,千万别打乱

分类任务里的随机切分在这里不能用。空气污染数据是强时序数据,今天的特征和明天的特征高度相关,随机打乱后模型会看到“未来”的信息,测试集分数虚高,上生产环境立刻现原形。

feature_cols = [ 'SO2', 'NO2', 'CO', 'O3', 'PM10', 'temp', 'pressure', 'humidity', 'wind_speed', 'wind_dir_sin', 'wind_dir_cos', 'pm25_lag_1h', 'pm25_lag_3h', 'pm25_lag_6h', 'pm25_lag_24h', 'pm25_roll_mean_6h', 'pm25_roll_max_6h', 'pm25_roll_std_6h', 'hour', 'dayofweek', 'is_weekend', ] # 按时间顺序切 8:2 cutoff = int(len(df) * 0.8) train = df.iloc[:cutoff].reset_index(drop=True) test = df.iloc[cutoff:].reset_index(drop=True) X_train = train[feature_cols] y_train = train[target] X_test = test[feature_cols] y_test = test[target]

切分比例 8:2 是经验值,但如果数据集覆盖了完整的春夏秋冬,最好保证训练集和测试集里都有四季数据。如果数据只覆盖一年,按前 8 个月训练、后 4 个月测试,测试集缺冬季样本,模型对冬天的预测能力就是没验证过的。时间切分的边界条件值得多花两分钟想清楚。

提示:df.iloc[:cutoff]切的是行序号,前提是前面sort_values('date')已经按时间排好序。如果你在中途做过dropna或重排,务必重新检查顺序。

3. 随机森林算法原理与选型:为什么先跑基线而不用梯度提升

3.1 随机森林算法原理:两句话讲清黑匣子

随机森林算法原理并不复杂,它就是两件事:对训练数据做有放回抽样,每棵树在不同样本上生长;每次分裂时只随机挑选一部分特征来竞争。抽样制造了树与树之间的差异,特征随机让单棵树无法垄断某个强特征,最后把几十上百棵树的预测取平均,方差被压下来,偏差基本不变。

单棵决策树的问题不是拟合能力,而是稳定性。训练集稍微换几个样本,树结构可能完全变了,预测结果大幅波动。随机森林把这种不稳定性转化为优势:单棵树可以过拟合到自己的样本上,但每棵树过拟合的方向不同,平均之后互相抵消。这正是 bagging 能work的数学直觉。

回归场景下,每棵树的每次分裂都在做同一件事:遍历候选特征的候选阈值,找出让 MSE 下降最多的切分方式。所以随机森林回归算法的本质是大量局部线性逼近的组合,它不需要你预设 PM2.5 与湿度之间是线性还是指数关系,数据自己告诉你。

3.2 为什么先上随机森林:与线性回归和 XGBoost 的对比

很多人一上来就问“为什么不用 XGBoost”,我的回答是:空气质量预测这种十几个特征、上万行样本的中小数据集,随机森林的训练成本最低、默认参数下表现最稳,足够当一个可靠基线。

维度线性回归随机森林回归XGBoost 类梯度提升
非线性交互需要手工构造交叉项自动学习自动学习
对缺失值必须预先填充少量缺失影响有限原生支持
对异常值容易被拉偏单棵树影响有限需要谨慎调参
几千行样本可用很稳容易过拟合
调参难度低低高,参数多且敏感
可解释性系数直接可读特征重要度特征重要度 + SHAP

空气污染预测的特征大多是“温和相关”:湿度对 PM2.5 有影响,但不是决定性的;风速小时污染物堆积,风速大时扩散,这个转变点在哪里、跟湿度怎么交互,线性回归需要你手工构造项,而树模型天然就能切分出来。XGBoost 在同样数据上通常能比随机森林高两三个点的 R2,但要调的学习率、子树深度、正则项一大堆,新手很容易调出一版过拟合的模型还不自知。

3.3 随机森林回归核心参数:先理解后调参

随机森林回归算法的参数不多,但每个参数的生效机制不同,闷头随便试就是玄学。下面是我的常用起始配置,按“影响效果”从大到小排。

参数作用常见起始值调大/调小的效果
n_estimators树的数量500调大降方差,但收益递减
max_depth单棵树最大深度None 或 20调大拟合更强,易过拟合
min_samples_split内部节点继续分裂的最小样本数5调大抑制过拟合
min_samples_leaf叶节点最少样本数3调大让预测更平滑
max_features每次分裂考虑的特征数'sqrt' 或 0.4调小增加树间差异,调大拟合更强
max_samples每棵树的抽样比例0.8调小增加树间差异
oob_score是否计算袋外评分True相当于内置验证集
n_jobs并行核数-1性能参数,不影响精度

n_estimators加到一定数量后收益递减,500 棵树在大多数场景下够用,加到 2000 只增加训练时间不增加精度。max_features是随机森林区别于普通 bagging 的关键,回归默认是'sqrt',也就是每棵树每次分裂只看三分之一左右的特征,这让那些弱特征也有机会参与分裂,树之间相关性降低,平均效果才会好。

max_samples=0.8是我习惯的推荐值,每棵树只用 80% 的样本训练,袋外的 20% 样本可以用来做无偏评估。oob_score在回归任务里返回的是袋外样本的 R2,它比训练集分数真实得多,可以用来快速判断参数改得对不对。

3.4 回归评估指标:R2、MAE、RMSE 各说各话

回归模型的评估不能只看一个数,三个指标讲的是三件事。R2 看整体拟合度,含义是模型比“用均值预测”好多少,但它对样本分布敏感,数据里重污染日很少时,R2 可能很高而重污染日一个都预测不准。MAE 是平均绝对值误差,10 μg/m³ 的误差就是 10,业务人员能直接理解。RMSE 对大误差加权放大了,它能暴露模型“平时都准,偶尔崩一次”的问题。

实战里我习惯三列一起打印出来,如果 MAE 很低但 RMSE 很高,说明模型大部分时间预测得很好,但少数日子崩得很惨——这种情况通常是极端污染事件导致的,后面第 5 章会展开讲。对于空气质量预测模型,业务上真正关心的往往不是整体 MAE,而是“重污染日能不能报出来”,但这个信号 MAE 和 R2 都体现不出来,后面要用残差分析来挖。

4. sklearn 实现随机森林回归:从最小可跑代码到特征重要度

4.1 最小可跑模型:RandomForestRegressor 五步起跑

接住第 2 章的X_train、y_train、X_test、y_test,下面这段就是一个完整的随机森林回归训练流程。先不调参,只求跑通,看三个评估指标和 OOB 分数落在什么量级。

from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score # 基线模型:参数给到常见起始值,不追求最优 model = RandomForestRegressor( n_estimators=500, max_features='sqrt', min_samples_leaf=3, oob_score=True, n_jobs=-1, random_state=42, ) model.fit(X_train, y_train) y_pred = model.predict(X_test) print(f"R2 = {r2_score(y_test, y_pred):.4f}") print(f"MAE = {mean_absolute_error(y_test, y_pred):.4f} (ug/m3)") print(f"RMSE= {mean_squared_error(y_test, y_pred, squared=False):.4f} (ug/m3)") print(f"OOB = {model.oob_score_:.4f}")

n_jobs=-1让所有 CPU 核并行训练,几百棵树几分钟就能跑完。random_state=42不是玄学,是保证你下次重跑结果一致,调参时如果随机状态不固定,你分不清分数变化是参数引起的还是随机波动引起的。oob_score=True时会额外计算袋外样本的 R2,不需要再划分验证集就能得到一个相对诚实的评估。

如果你跑出来的 R2 在 0.85 以上,说明数据质量不错,预测目标本身是强自相关的,模型抓住了绝大部分信息。如果 R2 低于 0.6,先别急着调参,回头检查特征是不是没构造对,或者测试集里包含了几段长假——节假日模式没在训练集里出现过,预测差是正常的。

4.2 用 RandomizedSearchCV 调参:参数搜索也怕数据泄漏

网格搜索 GridSearchCV 在这个场景下太费时间,参数组合一多就是几百次全量训练。我更推荐随机搜索 RandomizedSearchCV,它从参数分布里随机采样固定的组合数,30 组左右就能覆盖关键参数的有效区间。

from sklearn.model_selection import RandomizedSearchCV, TimeSeriesSplit # 时间序列交叉验证:防止搜索过程中把未来数据泄漏进去 tscv = TimeSeriesSplit(n_splits=5) param_dist = { 'n_estimators': [200, 500, 800], 'max_depth': [None, 10, 20, 30], 'min_samples_split': [2, 5, 10], 'min_samples_leaf': [1, 3, 5], 'max_features': [0.3, 0.5, 'sqrt'], } search = RandomizedSearchCV( RandomForestRegressor(random_state=42, n_jobs=-1), param_distributions=param_dist, n_iter=30, scoring='neg_mean_squared_error', cv=tscv, random_state=42, verbose=1, ) search.fit(X_train, y_train) print(search.best_params_) print(search.best_score_)

这里有两个容易被忽略的点。第一,cv一定要传TimeSeriesSplit而不是默认的 KFold。随机搜索内部的交叉验证如果按随机折切,模型在每一折里都能看到未来数据,搜索出来的“最优参数”是虚高的。第二,scoring='neg_mean_squared_error'对应 RMSE 的平方,用 MSE 做优化目标会让搜索更关注大误差样本,这符合空气质量预测的业务诉求——宁可整体多错一点点,也要把重污染日尽量报准。

30 组随机搜索跑完大概需要几分钟到十几分钟,取决于样本量。搜索结束后对比 4.1 的基线分数,如果提升不到 0.02,说明基线参数已经够用,没必要为这点提升增加模型复杂度。

4.3 预测曲线与特征重要度:模型到底学到了什么

拟合完不要只看指标,把预测结果画出来,你才能看到模型是整体偏移还是某些时段失灵。下面两段代码是最常用的诊断手段。

import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 只画最后 30 天,曲线重叠度能看出预测是超前还是滞后 plt.figure(figsize=(12, 5)) plt.plot(test['date'].iloc[-720:], y_test.iloc[-720:], label='真实值', linewidth=1) plt.plot(test['date'].iloc[-720:], y_pred[-720:], label='预测值', linewidth=1, alpha=0.8) plt.ylabel('PM2.5 (ug/m3)') plt.legend() plt.show()
  • 720 点是 30 天的小时级数据,画全量一年数据的话曲线太密看不出模式。

预测值如果整体比真实值滞后几个小时,这不是 bug,是你用了pm25_lag_1h这类滞后特征导致的,模型本质上在“用过去一小时预测现在”。解决方法是增加更前置的滞后特征,比如重点看 6 小时前的特征,或者直接把预测目标改为“未来 6 小时的均值”,这属于预测任务的重新定义,看业务需要。

# 特征重要度:杂质减少总和,不是因果效应 importance = pd.Series(model.feature_importances_, index=feature_cols) importance.sort_values(ascending=True).tail(15).plot(kind='barh', figsize=(8, 8)) plt.xlabel('feature importance') plt.show()

特征重要度排序出来后,pm25_lag_1h排第一是大概率事件,说明当前浓度受近因主导。hour、is_weekend这类时间特征排进前五也不意外。真正要警惕的是:重要度只代表“这个特征对分裂的贡献”,不代表“这个特征是污染的原因”。pm25_lag_24h重要度高,仅仅因为污染有日周期惯性,你不能据此写一份“昨天的 PM2.5 导致今天的 PM2.5”的因果关系报告。

4.4 把模型存下来:joblib 持久化与重载

训练一次不容易,调好的模型和特征清单一定要落盘,否则下次重训换台机器,参数又得重新搜一遍。随机森林模型体积不大,几百棵树存下来也就几十 MB,用 joblib 线程安全地读写。

import joblib # 保存模型和配套的特征列表,缺一不可 joblib.dump(search.best_estimator_, 'rf_air_quality.pkl') joblib.dump(feature_cols, 'feature_cols.pkl')

加载时注意一点,search.best_estimator_是已经拟合好的模型,直接predict就行,没有“重新编译”的概念。feature_cols.pkl必须跟模型一起保存,否则你没法保证预测时传进来的特征顺序跟训练时一致。特征顺序对树模型没有概念上的影响,但 DataFrame 列名对不上会报错,这个坑后面讲。

提示:.pkl文件不要跨 Python 大版本乱迁,sklearn 版本差异也可能导致旧模型加载失败。重训模型不贵,模型文件只留最近两版就够,别把上一季度的模型当宝贝留着。

5. 训练避坑记录:数据泄漏、样本不均衡与过拟合的五种典型死法

5.1 先标准化再切分训练测试集,验证分数虚高

现象:R2 高达 0.97,OOB 分数也不错,模型上线后一塌糊涂,预测曲线明显偏离真实值。

原因:你用了StandardScaler拟合全部数据再切分,scaler.fit(df)相当于在训练阶段偷看了测试集的分布信息。树模型本身不依赖标准化,这个坑是复制分类比赛代码时带进来的。

解决:随机森林回归不需要标准化,直接删掉scaler那几行。如果你的项目确实需要标准化(比如同时跑 SVM 作对比),先切分再scaler.fit(X_train),然后只 transform 训练集和测试集,绝不在切分前做任何跨样本的统计计算。

5.2 随机切分打乱时间顺序,模型学到了不存在的时间旅行

现象:跑完train_test_split(df, test_size=0.2)后 R2 异常高,比如 0.93,而按月份手动验证时只有 0.7。

原因:train_test_split默认shuffle=True,模型在训练时看到了“未来的数据”,测试集里混着训练样本的相邻时刻样本,滞后特征直接变成了手术刀。

解决:回到第 2.4 节用iloc按时间顺序切分,或者train_test_split(shuffle=False)。更严格的做法是用TimeSeriesSplit做多折验证,每一折都保证训练集时间早于验证集。记住一句判断标准:任何让测试集能反推出训练集分布的操作都是数据泄漏,时间顺序是最后一道防线。

5.3 重污染日样本太少,模型假装它们不存在

现象:整体 MAE 只有 15,看着不错;但把预测值按月份拆开看,12 月那几天重度污染全被预测成轻度,曲线在峰值处被压平。

原因:PM2.5 分布严重右偏,浓度 200 以上的样本可能只占 2%,随机森林回归用 MSE 做分裂目标,极少数高值样本的误差在总损失里占比太小,模型最优策略就是“保守一点,别报太高”。

解决:对目标列做np.log1p(y)变换,让高值区间的误差在损失函数里变权重更大,预测完再np.expm1还原。还有一个做法是按分位数给样本加权,高污染时段权重提到 3 到 5 倍。注意不要直接复制分类里的 SMOTE,它是合成样本,用在时间序列上会捏造不存在的天气演变过程。

5.4 n_estimators 拉满、max_depth 不限制,OOB 分数反而虚高

现象:训练集 R2 接近 1.0,OOB 分数也漂亮,但换一段新数据预测时波动极大,同一套参数跑两次结果差异明显。

原因:max_depth=None时每棵树都长到纯节点,单棵树对训练集的拟合接近完美,树与树之间相关性极高,bagging 的降方差作用被削弱了。n_estimators增大只能降低一部分方差,治标不治本。

解决:把max_depth限制在 20 左右,配合min_samples_leaf=5让叶子至少有 5 个样本再停止分裂。用第 4.2 节的随机搜索一起调这三个参数,不要单独只调n_estimators。OOB 分要对比着看:如果 OOB 远低于训练集分数,说明单棵树过拟合严重,先限制树的复杂度。

5.5 特征重要度被高相关特征带偏

现象:PM10 和 PM2.5 重要度都很高,但去掉 PM10 重训后模型分数几乎不变,你开始怀疑特征是不是白加了。

原因:PM10 和 PM2.5 高度相关,树分裂时随机选特征,两个特征轮流被选中,重要度被分摊了。这是树模型的已知毛病:高相关特征的重要度会被稀释,而不是按真实贡献分配。

解决:不要单独解读单特征重要度,用sklearn.inspection.permutation_importance做置换重要性,把某个特征打乱后看分数下降多少,这是更接近“真实贡献”的度量。如果两个特征强相关,把它们合并成一组看组贡献,比如浓度类特征一组、气象类一组,决策会正常很多。

6. 验证与部署:TimeSeriesSplit、残差分析与模型持久化收尾技巧

6.1 用 TimeSeriesSplit 验证不同预测提前量的稳定性

随机森林回归模型上线前,我习惯验证三个时间尺度:预测当小时、预测未来 3 小时、预测未来 6 小时。方法很简单,把滞后特征改成更前置的版本,然后在同一套测试集上对比 MAE 和 RMSE。

from sklearn.model_selection import TimeSeriesSplit tscv = TimeSeriesSplit(n_splits=5) scores = [] for fold, (train_idx, val_idx) in enumerate(tscv.split(X_train)): model.fit(X_train.iloc[train_idx], y_train.iloc[train_idx]) val_pred = model.predict(X_train.iloc[val_idx]) val_mae = mean_absolute_error(y_train.iloc[val_idx], val_pred) scores.append((fold + 1, val_mae)) print(scores)

如果 3 小时尺度的 MAE 比 1 小时尺度翻了一倍,说明模型强烈依赖临近时刻的自相关特征,这符合物理直觉,但也提醒你:污染预警场景真正需要的是提前量,不是事后解释。

6.2 残差分析:画出模型失效的时段模式

把y_test - y_pred画出来,按月份聚合看均值,你会看到模型在哪些月份系统性低估、哪些月份高估。这是最容易被跳过但最有价值的诊断。

residual = y_test - y_pred month = test['date'].dt.month resid_by_month = pd.DataFrame({'month': month, 'residual': residual}) resid_by_month.groupby('month')['residual'].mean().plot(kind='bar')

如果残差在采暖季明显为正,说明模型对冬季排放源的高估或低估是系统性的,这时候加一个“供暖月份”的二值特征,往往比继续调参数更有效。残差分析的价值就在于此:它告诉你该改数据还是改模型。

6.3 模型部署后的三个习惯

模型上线不是终点。我现在的习惯是:每次重训后把模型文件、特征清单、训练数据覆盖的日期范围一起存档,三个月后回来看才知道这版模型见过哪些数据;定时用最近一周的真实数据计算 MAE,超过阈值就触发重训;预测结果多输出一个分位数估计,随机森林本身不做概率预测,但你可以用每棵树的预测结果取 5% 和 95% 分位作为波动区间,这比单个点预测对决策有用得多。

这套数据挖掘实战方案花的时间主要在数据清洗和特征构造上,随机森林算法本身反而是最省心的一环。如果你的目标是快速跑通一个可交付的空气质量污染预测模型,用这个路线准没错;如果你后面数据量涨到几十万行、特征上百个,再考虑换梯度提升也不迟——随机森林永远值得作为第一个基线。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询