随机森林实战:从工业设备故障预警到共享单车需求预测
本文通过两个典型实践案例介绍随机森林的主要用途与用法:使用工业设备传感器数据识别设备故障,以及使用天气和时间信息预测共享单车需求。 两个案例分别涵盖不平衡分类与时间序列回归,重点介绍模型原理、特征工程、超参数调整、评价指标,以及如何避免数据泄漏和模型过拟合。
一、随机森林究竟是什么?
要理解随机森林,首先需要理解决策树。
假设一家工厂希望判断生产设备是否存在故障风险。
一棵决策树可能根据设备的温度、转速、扭矩等数据,逐步进行判断:
设备运行数据
|
扭矩是否 > 55 Nm?
/ \
是 否
| |
转速是否过低? 温度是否过高?
/ \ / \
是 否 是 否
| | | |
高风险 中风险 中风险 低风险
这类模型能够学习变量之间的非线性关系,但单棵决策树存在一个问题:容易受到训练样本变化的影响。
例如,当训练集增加或减少少量样本时,决策树可能选择完全不同的分裂节点,导致预测结果发生较大变化。
随机森林通过训练大量不同的决策树来解决这一问题。
1. Bootstrap:让每棵树看到不同的数据
假设训练集有 10,000 条记录。
随机森林为每棵树进行一次 Bootstrap 抽样:
- 从原始训练集中随机抽取 10,000 次。
- 每次抽取后将样本放回。
- 同一个样本可能被抽中多次。
- 一部分样本不会被当前决策树使用。
在这种抽样机制下,一棵树平均会使用原始训练集中约 63.2% 的不同样本。
剩余约 36.8% 的样本称为袋外样本(Out-of-Bag,OOB)。
因此,即使所有树来自同一个训练集,它们看到的数据也不完全一致。
2. 随机选择特征:进一步增加树之间的差异
随机森林还有第二种随机机制:每次节点分裂时,只考虑随机选出的一部分候选特征。
例如,数据中存在 100 个特征。
如果设置:
max_features="sqrt"
那么对于分类随机森林,每次寻找最佳分裂时,大致会考虑:
[
\sqrt{100}=10
]
个随机候选特征。
这样可以避免所有树总是优先选择相同的强特征。
3. 多棵树如何组合预测?
随机森林的整体结构如下:
原始训练数据
|
+------------+------------+
| | |
Bootstrap 1 Bootstrap 2 Bootstrap 3
| | |
决策树 1 决策树 2 决策树 3
| | |
+------------+------------+
|
集成预测
|
最终分类或回归值
对于分类任务,scikit-learn 的随机森林会汇总各棵树的类别概率,再进行平均。
对于回归任务,随机森林对各棵树的预测值取平均:
[
\hat y(x)=\frac{1}{T}\sum_{t=1}^{T}f_t(x)
]
其中:
- (T):决策树的数量。
- (f_t(x)):第 (t) 棵树的预测结果。
- (\hat y(x)):随机森林的最终预测结果。
通过集成大量相关性相对较低的决策树,随机森林通常能够降低预测方差。
这就是 Bagging(Bootstrap Aggregating)集成学习的基本思想。
二、案例一:工业设备故障检测
1. 业务背景
第一个案例使用 UCI Machine Learning Repository 的 AI4I 2020 Predictive Maintenance Dataset。
该数据集包含 10,000 条工业设备运行记录,用于研究机器学习在预测性维护中的应用。
数据集属于模拟工业生产数据,按照设备运行变量和故障生成规则构造,能够反映部分典型的设备故障规律。
其中:
- 正常记录:9,661 条。
- 故障记录:339 条。
- 故障样本占比:3.39%。
这意味着,设备故障识别是一个典型的类别不平衡问题。
2. 数据包含哪些特征?
| 字段 | 含义 | 单位 |
|---|---|---|
Type |
产品质量类型 | L、M、H |
Air temperature [K] |
环境温度 | K |
Process temperature [K] |
工艺温度 | K |
Rotational speed [rpm] |
设备转速 | rpm |
Torque [Nm] |
设备扭矩 | Nm |
Tool wear [min] |
刀具累计磨损时间 | min |
Machine failure |
是否发生设备故障 | 0 或 1 |
此外,数据中还提供五个故障类型标签:
TWF:刀具磨损故障。HDF:散热故障。PWF:动力故障。OSF:过载故障。RNF:随机故障。
这里有一个非常重要的建模细节。
Machine failure 是根据这些故障类型确定的总故障标签。
如果把 TWF、HDF 等字段直接放进模型的输入特征,模型实际上就提前知道了故障类型。
这会造成严重的数据泄漏。
因此,模型只使用运行过程中的温度、转速、扭矩和磨损等特征。
此外,当前数据标签表示该条运行记录是否发生故障。严格的提前预警系统还需要构造“未来一定时间内是否发生故障”的标签,并使用故障发生前的传感器数据训练。
本案例先解决设备故障状态识别,再讨论如何扩展为提前预警。
3. 获取数据
前往 UCI Machine Learning Repository,搜索:
AI4I 2020 Predictive Maintenance Dataset
也可以通过 Hugging Face 获取公开数据:
https://huggingface.co/datasets/TinchoARS/AI4I-2020-Predictive-Maintenance
将 CSV 文件保存为:
ai4i2020.csv
安装依赖:
pip install pandas numpy scikit-learn
4. 完整 Python 实践
本案例使用 RandomForestClassifier,并重点处理三个问题:
- 故障样本极少,需要考虑类别不平衡。
- 分类阈值需要根据验证集调整。
- 模型需要使用独立测试集评价。
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import (
roc_auc_score,
average_precision_score,
precision_recall_curve,
precision_score,
recall_score,
confusion_matrix,
classification_report
)
# 1. 读取数据
df = pd.read_csv("ai4i2020.csv")
# 2. 选择设备运行特征
features = [
"Type",
"Air temperature [K]",
"Process temperature [K]",
"Rotational speed [rpm]",
"Torque [Nm]",
"Tool wear [min]"
]
X = df[features].copy()
y = df["Machine failure"].astype(int)
print("样本数量:", len(df))
print("故障比例:", y.mean())
# 3. 划分训练、验证和测试集
X_dev, X_test, y_dev, y_test = train_test_split(
X,
y,
test_size=0.2,
stratify=y,
random_state=42
)
X_train, X_valid, y_train, y_valid = train_test_split(
X_dev,
y_dev,
test_size=0.25,
stratify=y_dev,
random_state=42
)
# 4. 类别特征编码
preprocessor = ColumnTransformer([
(
"type",
OneHotEncoder(handle_unknown="ignore"),
["Type"]
),
(
"numeric",
"passthrough",
[
"Air temperature [K]",
"Process temperature [K]",
"Rotational speed [rpm]",
"Torque [Nm]",
"Tool wear [min]"
]
)
])
# 5. 构建随机森林
rf = RandomForestClassifier(
n_estimators=300,
max_depth=12,
min_samples_leaf=3,
max_features="sqrt",
class_weight="balanced_subsample",
bootstrap=True,
n_jobs=-1,
random_state=42
)
model = Pipeline([
("preprocessor", preprocessor),
("random_forest", rf)
])
# 6. 模型训练
model.fit(X_train, y_train)
# 7. 验证集预测
valid_prob = model.predict_proba(X_valid)[:, 1]
# 8. 利用F2选择分类阈值
# F2比F1更加重视召回率
precision, recall, thresholds = precision_recall_curve(
y_valid,
valid_prob
)
f2 = (
5 * precision[:-1] * recall[:-1]
/ (
4 * precision[:-1]
+ recall[:-1]
+ 1e-12
)
)
best_threshold = thresholds[np.argmax(f2)]
print("最佳验证集阈值:", best_threshold)
# 9. 测试集独立评价
test_prob = model.predict_proba(X_test)[:, 1]
test_pred = (
test_prob >= best_threshold
).astype(int)
print(
"ROC-AUC:",
roc_auc_score(y_test, test_prob)
)
print(
"Average Precision:",
average_precision_score(y_test, test_prob)
)
print(
"Precision:",
precision_score(
y_test,
test_pred,
zero_division=0
)
)
print(
"Recall:",
recall_score(
y_test,
test_pred,
zero_division=0
)
)
print("混淆矩阵:")
print(confusion_matrix(y_test, test_pred))
print("分类报告:")
print(
classification_report(
y_test,
test_pred,
zero_division=0
)
)
5. 为什么使用 class_weight="balanced_subsample"?
这里涉及随机森林处理不平衡样本的重要机制。
假设训练集中:
- 正常样本:96.6%。
- 故障样本:3.4%。
如果不考虑类别不平衡,模型可能非常容易获得较高准确率,但对故障样本的识别能力有限。
balanced_subsample 会根据每棵树抽取的 Bootstrap 样本,调整不同类别的权重。
基本思想是:
[
w_c=\frac{N}{K\times N_c}
]
其中:
- (N):样本总量。
- (K):类别数量。
- (N_c):类别 (c) 的样本数量。
少数类将获得更大的训练权重。
这种做法可以增强模型对少数类的关注,但实际效果需要验证,而且权重调整可能影响预测概率的校准情况。
6. 为什么使用 F2 而不是只看 Accuracy?
假设有 10,000 条设备记录,其中 339 条是故障。
如果模型将全部记录都预测为正常:
[
Accuracy=\frac{9661}{10000}=96.61%
]
准确率看起来很高,但一个故障也没有识别出来。
因此,设备故障检测应该重点关注 Precision 和 Recall。
| 指标 | 含义 | 业务解释 |
|---|---|---|
| Precision | 被预测为故障的样本中,真正故障的比例 | 误报会消耗多少维修资源 |
| Recall | 实际故障中,被模型识别的比例 | 能识别多少真实故障 |
| AP | 综合评价精确率与召回率 | 适合稀少故障事件 |
| ROC-AUC | 区分正常与故障的能力 | 衡量总体风险排序能力 |
在部分场景中,漏掉一次重大设备故障可能造成高昂损失。
因此,可以优先关注 Recall。
F2 的定义为:
[
F_2=
\frac{5PR}{4P+R}
]
其中:
- (P):Precision。
- (R):Recall。
与 F1 相比,F2 更重视召回率。
但在真实工业生产中,F2 最大化也未必对应最佳维修策略。
如果一次误报就需要停机数小时,误报成本可能很高。
因此,最终阈值应该结合故障损失、检查成本和停机成本确定。
7. 如何查看重要特征?
可以使用 Permutation Importance(排列重要性)。
它的基本原理是:将某个特征随机打乱,观察模型预测性能下降多少。
在前面训练代码后继续添加:
from sklearn.inspection import permutation_importance
importance = permutation_importance(
model,
X_test,
y_test,
scoring="average_precision",
n_repeats=5,
random_state=42,
n_jobs=-1
)
result = pd.DataFrame({
"feature": X_test.columns,
"importance": importance.importances_mean
})
result = result.sort_values(
"importance",
ascending=False
)
print(result)
如果 Torque [Nm] 和 Tool wear [min] 的重要性较高,说明模型较依赖这些变量进行故障识别。
但需要注意,Permutation Importance 衡量的是模型预测对某个特征的依赖程度。
对于高度相关的特征,分别打乱其中一个变量可能只造成较小的性能下降,因为其他相关特征仍然保留了部分信息。
因此,特征重要性应该结合业务机制解释。
例如,设备的扭矩和转速可能共同影响机械功率,而刀具磨损与扭矩也可能共同影响设备负荷。
8. 如何扩展成真正的故障提前预警?
本案例预测的是当前记录对应的故障状态。
如果希望提前 24 小时发出故障警报,则需要修改样本定义。
例如:
时间 T
|
|-- 最近24小时传感器数据
|
|-- 温度变化趋势
|
|-- 扭矩波动
|
|-- 刀具磨损程度
|
v
随机森林模型
|
v
预测未来24小时是否发生故障
此时目标变量应该定义为:
[
Y_t=
\begin{cases}
1,&\text{未来24小时发生故障}\
0,&\text{未来24小时未发生故障}
\end{cases}
]
需要使用具有真实时间戳、设备编号、故障事件时间和足够观察期的数据构造训练样本。
同时,训练集和测试集最好按时间及设备分组设计,防止同一设备相邻时段的高度相似记录产生乐观的测试结果。
三、案例二:共享单车需求预测
1. 业务背景
第二个案例来自城市共享单车运营。
共享单车企业需要根据时间、天气和历史需求,预测不同时间段的租车数量,以支持车辆调度和运力安排。
例如:
- 工作日上午 8 点,地铁站附近可能出现大量骑行需求。
- 周末下午,公园附近可能有更多休闲骑行用户。
- 下雨、气温过低或过高时,租车需求可能下降。
- 节假日与普通工作日的用车规律也存在差异。
这些规律往往存在复杂的非线性关系。
因此,随机森林是一个适合尝试的回归模型。
2. 数据集介绍
本案例使用 UCI 的 Bike Sharing Dataset。
该数据集记录了美国华盛顿特区 Capital Bikeshare 系统在 2011—2012 年的租车情况。
其中,小时级文件 hour.csv 包含 17,379 条观测记录。
主要字段如下:
| 字段 | 含义 |
|---|---|
dteday |
日期 |
season |
季节 |
yr |
年份 |
mnth |
月份 |
hr |
小时 |
holiday |
是否节假日 |
weekday |
星期 |
workingday |
是否工作日 |
weathersit |
天气状况 |
temp |
标准化温度 |
atemp |
标准化体感温度 |
hum |
标准化湿度 |
windspeed |
标准化风速 |
casual |
非注册用户租车数量 |
registered |
注册用户租车数量 |
cnt |
总租车数量 |
我们希望预测:
[
Y_t=cnt_t
]
也就是某个小时的总租车数量。
这里必须排除两个字段:
casual
registered
因为:
[
cnt=casual+registered
]
如果将它们作为预测输入,模型就会直接看到目标变量的组成部分,产生数据泄漏。
因此,我们仅使用时间和天气等特征进行预测。
这个案例演示的是根据目标小时的日历信息和天气条件预测该小时的需求。历史数据中的天气是实际观测值;真实部署时,预测未来小时需要将这些天气字段替换成预测时点已经可获得的天气预报信息。
3. 获取数据
前往 UCI Machine Learning Repository,搜索:
Bike Sharing Dataset
下载并解压数据文件,获得:
hour.csv
Hugging Face 也提供公开数据版本:
https://huggingface.co/datasets/t22000t/bike-sharing-tabular
本实践使用 UCI 原始 hour.csv 字段结构。
4. 为什么不能随意随机划分训练集?
这是时间相关业务预测中非常重要的问题。
假设我们拥有 2011 年和 2012 年的全部共享单车记录。
如果随机抽取 80% 作为训练集、20% 作为测试集,那么模型可能使用 2012 年 12 月的数据训练,再去预测 2011 年 3 月。
这种划分方式能够用于某些静态数据分析,但无法很好地模拟真实业务中“利用过去数据预测未来需求”的过程。
因此,本案例按照时间排序:
2011年初 2012年末
|----------------------------------|
| | | |
| 训练集 | 验证集 | 测试集 |
| 70% | 15% | 15% |
先使用较早的数据训练,使用后续数据选择参数,再用最后一段时间的数据进行独立测试。
5. 完整 Python 实践
本案例使用 RandomForestRegressor。
同时,我们建立一个简单的基线模型:使用训练数据中各小时的平均租车量,预测未来相同小时的需求。
这样可以判断随机森林是否真正学到了超出简单时间规律的信息。
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import (
mean_absolute_error,
mean_squared_error,
r2_score
)
# 1. 加载数据
df = pd.read_csv("hour.csv")
# 2. 构造时间字段
df["datetime"] = (
pd.to_datetime(df["dteday"])
+ pd.to_timedelta(df["hr"], unit="h")
)
df = df.sort_values(
"datetime"
).reset_index(drop=True)
# 3. 特征选择
# 不使用casual和registered,避免泄漏
features = [
"season",
"yr",
"mnth",
"hr",
"holiday",
"weekday",
"workingday",
"weathersit",
"temp",
"atemp",
"hum",
"windspeed"
]
target = "cnt"
# 4. 按时间划分训练、验证、测试集
n = len(df)
train_end = int(n * 0.70)
valid_end = int(n * 0.85)
train = df.iloc[:train_end]
valid = df.iloc[train_end:valid_end]
test = df.iloc[valid_end:]
X_train = train[features]
y_train = train[target]
X_valid = valid[features]
y_valid = valid[target]
X_test = test[features]
y_test = test[target]
# 5. 比较不同随机森林参数
candidates = [
{
"max_depth": 12,
"min_samples_leaf": 5
},
{
"max_depth": 20,
"min_samples_leaf": 2
}
]
best_mae = float("inf")
best_params = None
for params in candidates:
model = RandomForestRegressor(
n_estimators=250,
max_features=0.8,
n_jobs=-1,
random_state=42,
**params
)
model.fit(X_train, y_train)
pred = model.predict(X_valid)
mae = mean_absolute_error(
y_valid,
pred
)
print(
"参数:", params,
"验证集MAE:", round(mae, 2)
)
if mae < best_mae:
best_mae = mae
best_params = params
print("最佳参数:", best_params)
# 6. 使用训练集+验证集重新训练
train_valid = df.iloc[:valid_end]
final_model = RandomForestRegressor(
n_estimators=250,
max_features=0.8,
n_jobs=-1,
random_state=42,
**best_params
)
final_model.fit(
train_valid[features],
train_valid[target]
)
# 7. 独立测试集预测
pred = final_model.predict(X_test)
# 租车数量不能为负数
pred = np.maximum(pred, 0)
# 8. 计算评价指标
mae = mean_absolute_error(y_test, pred)
rmse = np.sqrt(
mean_squared_error(y_test, pred)
)
r2 = r2_score(y_test, pred)
print("\nRandom Forest")
print("MAE:", round(mae, 2))
print("RMSE:", round(rmse, 2))
print("R²:", round(r2, 4))
# 9. 构造简单基线
# 使用历史同一小时的平均租车量
hour_mean = train_valid.groupby("hr")["cnt"].mean()
baseline_pred = test["hr"].map(hour_mean)
baseline_pred = baseline_pred.fillna(
train_valid["cnt"].mean()
)
baseline_mae = mean_absolute_error(
y_test,
baseline_pred
)
print(
"按小时均值预测 MAE:",
round(baseline_mae, 2)
)
# 10. 查看重要特征
importance = pd.Series(
final_model.feature_importances_,
index=features
).sort_values(ascending=False)
print("\n特征重要性:")
print(importance)
# 11. 导出测试集预测结果
result = test[["datetime", "cnt"]].copy()
result["predicted_cnt"] = pred
result["absolute_error"] = np.abs(
result["cnt"] - result["predicted_cnt"]
)
result.to_csv(
"bike_demand_predictions.csv",
index=False,
encoding="utf-8-sig"
)
print("预测结果已保存")
6. 如何评价预测效果?
本案例属于回归任务,重点使用三个指标。
| 指标 | 含义 | 判断方向 |
|---|---|---|
| MAE | 平均绝对误差 | 越低越好 |
| RMSE | 均方根误差 | 越低越好 |
| R² | 决定系数 | 越高通常越好 |
例如,假设测试集出现:
Random Forest
MAE: 45
RMSE: 70
R²: 0.83
按小时均值预测
MAE: 105
以上数值仅用于说明指标,属于假设结果。
它表示随机森林平均每小时预测误差约为 45 辆,而简单基线的平均误差约为 105 辆。
如果真实实验中出现类似的改善,说明模型学到了时间与天气变量之间更加复杂的关系。
例如:
- 工作日早上和周末早上的需求不同。
- 相同温度在不同季节可能对应不同需求。
- 下雨对工作日通勤和周末休闲骑行的影响可能不同。
这些都是随机森林可以通过决策树分裂自动捕捉的非线性关系。
7. 随机森林为什么适合这个问题?
假设我们希望建立一个简单的线性回归模型:
[
Demand=
\beta_0+
\beta_1Temperature+
\beta_2Hour+
\beta_3Workingday
]
它可以学习各变量的线性作用。
但真实租车需求可能更加复杂。
例如:
- 早上 8 点属于通勤高峰。
- 下午 14 点可能对应休闲骑行高峰。
- 晚上 23 点需求大幅下降。
- 同样的 25℃,工作日和周末的需求模式不同。
如果使用线性模型,需要进一步设计非线性变换、交互项或其他特征。
而随机森林可以通过树的分裂学习这些特征组合。
不过,随机森林对于训练数据范围之外的长期趋势外推能力较弱。
例如,当共享单车总需求由于城市人口增长而持续上升时,模型未必能准确预测远高于历史训练范围的需求水平。
此外,单纯使用时间和天气特征,也难以充分描述突发活动、道路封闭、大型演唱会等特殊事件。
因此,实际共享单车调度通常还需要加入历史需求、区域位置、节假日活动及其他运营数据。
四、随机森林中最关键的参数
随机森林的效果主要取决于树的数量、树的复杂度和随机采样机制。
以下是最值得掌握的参数。
| 参数 | 主要作用 | 实践建议 |
|---|---|---|
n_estimators |
决策树数量 | 可以从 100~300 开始 |
max_depth |
单棵树最大深度 | 控制模型复杂度 |
min_samples_split |
节点继续分裂所需的最少样本数 | 增大通常使树更保守 |
min_samples_leaf |
每个叶节点需要的最少样本数 | 对小样本和噪声数据很重要 |
max_features |
每次分裂考虑的候选特征数量 | 控制树之间的相似程度 |
bootstrap |
是否启用有放回抽样 | 经典随机森林通常设为 True |
max_samples |
每棵树抽样使用的样本规模 | 可以降低训练成本 |
class_weight |
分类时不同类别的权重 | 用于类别不平衡 |
oob_score |
是否计算袋外评价指标 | 可作为训练阶段的辅助诊断 |
n_jobs |
并行计算线程数 | -1 表示使用可用的全部处理器 |
其中最值得深入理解的是三个参数。
1. n_estimators:树越多越好吗?
树的数量通常会影响模型稳定性。
例如,可以分别训练:
n_estimators=50
n_estimators=100
n_estimators=300
n_estimators=500
观察验证集指标是否继续改善。
随着树的数量增加,随机森林的预测方差通常会逐渐稳定。
但增加树的数量也会提高训练时间、模型内存占用和推理成本。
因此,达到性能相对稳定的区间后,继续增加树的意义可能有限。
2. max_depth:如何控制过拟合?
如果每棵树都无限制地生长,它们可能学习大量局部细节。
例如,工业设备数据中的少量异常样本可能形成很深的分裂规则。
训练集表现可能很好,但测试集性能下降。
可以通过限制最大深度控制模型复杂度:
max_depth=8
也可以通过增大叶节点最少样本数:
min_samples_leaf=5
降低模型对局部噪声的敏感性。
3. max_features:为什么随机特征选择有意义?
这是随机森林与普通 Bagging 决策树的重要区别。
假设有两个高度相关的特征:
设备扭矩
设备机械功率
如果每棵树都可以选择全部特征,它们可能反复依赖同一个最有预测力的变量。
这样,不同树之间的预测结果会高度相似。
减少候选特征数量,可以增加树之间的差异。
但如果设置得太小,也可能让树经常无法利用真正有价值的特征,从而造成欠拟合。
因此,max_features 需要通过验证集或交叉验证进行调整。
五、一个实用技巧:OOB 袋外评估
随机森林拥有一个很有代表性的内部评估机制:Out-of-Bag。
由于每棵树都通过 Bootstrap 抽样训练,总有一部分样本没有被该树使用。
这些样本可以用于评价对应决策树的预测效果。
如果某条记录没有出现在第 1、5、8、12 棵树的训练样本中,那么就可以使用这几棵树对它进行预测,并组合得到该记录的袋外预测。
对于随机森林分类模型,可以这样启用:
rf = RandomForestClassifier(
n_estimators=300,
bootstrap=True,
oob_score=True,
n_jobs=-1,
random_state=42
)
rf.fit(X_train, y_train)
print("OOB Score:", rf.oob_score_)
默认情况下,分类模型的 OOB Score 使用 Accuracy。
对于严重不平衡的故障识别问题,仅查看 OOB Accuracy 可能仍然不够。可以利用 oob_decision_function_ 进一步计算其他指标,但需要注意是否存在未获得有效袋外预测的样本。
OOB 的优势是能够在训练过程中提供辅助泛化诊断。
但是,对于具有明显时间依赖、客户重复记录或设备分组相关性的数据,独立的时间外或组外测试仍然非常重要。
例如,共享单车需求具有时间相关性,因此不能仅依靠 OOB 判断模型未来几个月的预测能力。
六、随机森林与 XGBoost 的区别
由于随机森林和 XGBoost 都是基于决策树的集成学习方法,它们经常被放在一起比较。
但二者的训练思想存在明显差异。
| 维度 | Random Forest | XGBoost |
|---|---|---|
| 集成方式 | Bagging | Gradient Boosting |
| 树的训练关系 | 相对独立,适合并行 | 后续树依赖此前模型 |
| 主要思想 | 组合多棵树降低方差 | 逐轮优化整体损失 |
| 学习率 | 通常不需要 | 重要超参数 |
| 正则化机制 | 树复杂度、采样等 | 树复杂度、显式正则化、学习率等 |
| 调参复杂度 | 相对较低 | 通常更高 |
| 训练效率 | 容易并行 | 与迭代轮数和实现方式有关 |
| 典型优势 | 稳定、易于建立基线 | 对复杂结构化数据具有较强竞争力 |
可以用一个直观的例子理解。
随机森林像是让许多分析师分别独立研究同一个问题,然后综合他们的结果。
XGBoost 则像是让后面的分析师重点修正前面分析师预测不准确的部分。
因此:
随机森林主要通过集成降低模型预测的波动,XGBoost 则通过逐轮优化损失函数提高预测能力。
两种方法都能够处理非线性关系,也都可能取得很好的效果。
实际项目中,建议在相同的数据切分和评价指标下进行对照实验。
如果 XGBoost 只带来很小的指标提升,却显著增加了训练、调参和维护成本,那么随机森林仍然可能是更合适的选择。
七、真实项目中,随机森林最容易遇到哪些问题?
1. 特征泄漏
这是最常见、也最容易严重高估模型性能的问题。
工业设备案例中,不能把故障类型标签作为故障识别模型的输入。
共享单车案例中,不能把 casual 和 registered 作为预测总租车数量的特征。
更一般地说,建模时需要检查:
模型在真正作出预测的那个时点,究竟能够获得哪些信息?
只有预测时点已经可用的数据,才应当进入模型。
2. 类别不平衡
工业故障、交易欺诈等任务通常具有较低的正样本比例。
此时,Accuracy 往往无法有效反映模型识别少数类的能力。
应该重点关注 Precision、Recall、AP,以及不同风险阈值下的实际业务成本。
3. 预测时间发生变化
对于共享单车需求、订单量和金融风险等会随时间变化的任务,训练集和测试集应当尽可能模拟真实预测过程。
随机划分容易让训练数据与测试数据具有高度相似的时间背景,造成较乐观的验证结果。
实际项目可以考虑按时间划分训练集、验证集和测试集,或者进行滚动时间窗口验证。
4. 特征重要性的误读
随机森林可以输出:
model.feature_importances_
这个指标基于树节点不纯度的下降程度。
但它可能偏向取值较丰富的特征,也可能受到特征相关性的影响。
因此,重要性高通常表示模型在预测过程中较依赖该变量,并不意味着改变该变量一定会导致预测目标发生相应变化。
如果需要进一步解释模型,可以结合 Permutation Importance、部分依赖分析和 SHAP 等工具。
5. 回归外推能力不足
随机森林回归的预测值来自多棵树叶节点预测的平均。
因此,对于训练数据中从未出现过的极端高值或长期增长趋势,随机森林通常缺乏强外推能力。
在长期销量、房价或需求预测中,应该特别注意这一点。
如果业务存在明显的趋势性增长,可以进一步比较线性趋势模型、时间序列模型和梯度提升模型,或者加入更合理的趋势特征。
八、总结
随机森林通过 Bootstrap 有放回抽样和随机特征选择,构建多棵具有差异的决策树,再将它们的预测结果组合起来。
这种集成方式能够降低单棵决策树的预测方差,使随机森林成为结构化数据分析中非常实用的算法。
本文两个实践案例展示了随机森林的不同用途。
工业设备故障检测重点体现了随机森林在不平衡分类问题中的应用。通过类别权重、Precision、Recall、AP 和分类阈值选择,可以将模型评价与设备维护成本联系起来。
共享单车需求预测则展示了随机森林的回归能力。模型通过学习时间、天气和需求之间的非线性关系,预测未来不同时间段可能出现的租车数量。
两个案例也说明了一个重要问题:机器学习模型的实际效果,不仅取决于算法本身,还取决于数据是否符合预测时点、标签定义是否合理,以及验证方式是否贴近真实应用。
随机森林尤其适合用作结构化数据任务的基线模型。在进行复杂的神经网络或梯度提升模型开发之前,先训练一个设计合理的随机森林,往往能够帮助我们快速判断数据中是否存在有效的预测信息。
真正掌握随机森林,关键在于理解 Bootstrap、随机特征选择和多树集成如何共同影响泛化能力,并能够根据具体业务选择恰当的数据处理方式、评价指标与验证方案。
只有把这些环节做好,随机森林才能从一个机器学习算法,转化为能够在实际业务中发挥价值的预测工具。
- 点赞
- 收藏
- 关注作者
评论(0)