1. 随机森林到底在解决什么问题
1.1 先从单棵决策树的无奈说起
很多人学机器学习的第一棵树就是决策树,因为它实在太直观了。给出一堆特征,按照“是否大于某个阈值”不断切分,最后落到叶子节点上,一个分类或回归结果就出来了。我最早接触决策树的时候觉得这玩意儿简直是万能砖,什么场景都能砌一下。但用着用着就发现问题了:单棵决策树在训练集上表现得很漂亮,一到测试集就开始露怯,也就是典型的过拟合。决策树的生长过程非常贪婪,它会想方设法把样本分得越来越纯,一棵不剪枝的树甚至能长到每个叶子只有一个样本,训练集准确率接近100%,换个数据直接拉胯。那时候我为了压制这种过拟合,天天跟剪枝参数死磕,什么预剪枝、后剪枝、CCP代价复杂度剪枝试了一圈,效果有,但总是治标不治本。
后来想明白了一个道理:一棵树的过拟合,本质上是因为它只看到了一份数据的“局部真相”,而且这个局部里还带了不少噪声。要解决这个问题,光靠优化单棵树是不够的,得想办法让“很多棵树”一起做决定,并且每棵树最好还能“各看各的”,不要全都长成一个模子。这就是集成学习的出发点,也是随机森林登场的理由。
1.2 从Bagging到随机森林:一句大白话解释为什么“三个臭皮匠”真的有用
随机森林属于集成学习里最经典的Bagging(Bootstrap Aggregating,自助采样聚合)家族。Bagging的思路就是有放回地从原始数据集里随机抽样本,抽出若干个大小差不多的子集,每个子集训练一棵决策树,最后把这么多棵树的预测结果拿过来投票(分类)或者取平均(回归)。
那为什么这么简单的操作能在实际效果上把单棵树按在地上摩擦?这个可以稍微拆开说一点。假设我们有k棵结构差不多的决策树,每棵树的预测误差里都包含一部分“偏差”和一部分“方差”。单独看任何一棵树,它的方差都挺大的,因为树的切分点对训练数据里的细微变化非常敏感,训练集稍稍变一变,整棵树的形态可能就完全不一样了。Bagging做的事情,是同时训练出k个这样的高方差模型,然后把它们的预测平均起来。如果这些模型的误差是相互独立的,平均之后方差会降到原来的约1/k。但现实情况是树和树之间并没有完全独立,因为每棵树的训练子集都是从同一个原始数据集里抽样出来的,样本本身就高度重叠。所以方差削减的程度没有理论上的1/k那么理想,但依然相当可观。
随机森林在Bagging的基础上又加了一刀:不只是样本层面做随机抽样,特征层面也做随机抽样。每次切分的时候,并不是从全部特征里挑最优切分点,而是先随机抽出一部分特征,再在这部分特征里找最优切分。这一步的意义在于:如果数据集里有一两个特别强的特征,普通的Bagging会让几乎所有的树都在最顶上几层用这两个特征切分,造成树和树之间高度相似,集成效果大打折扣。而特征随机化之后,很多树在顶部就只能被迫选择那些没那么强的特征,等于逼着不同的树从不同的角度去“观察”数据。样本随机降低了每棵树之间的相关性,特征随机进一步压低这种相关性,两者一叠加,模型整体的稳定性就上来了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 随机森林为什么能work:两个随机性的深度拆解
2.1 样本层面的自助采样:一袋樱桃的比喻
关于自助采样,我最喜欢用“拿樱桃”来打比方。假设你面前有一大袋樱桃,每个樱桃都贴着一个标签,有些是甜的,有些是酸的。你想通过尝少量樱桃来判断整袋樱桃的整体品质,每次抓一把出来,尝完记录结果,然后把这一把放回去再抓。因为你每次都把樱桃放回去了,所以同一次抽样里完全可能连续抓到同一颗樱桃。自助采样就是这个流程,对每个子集,从原始数据n条样本里有放回地抽n次,组成一棵树的训练集。
这么干的直接结果就是:每个训练子集里大概会有多少不重复的样本?计算一下就是1减去(1-1/n)的n次方,当n足够大的时候,这个值趋近于1减去e的-1次方,约等于0.632。也就是说每棵树的训练集里大概会有63.2%的原始样本,剩下的36.8%是重复样本。有趣的是,那36.8%没被抽中的样本,恰好可以用来做这件事的天然验证集:把这棵树的预测结果往这些样本上套,看它猜得准不准。随机森林里有个非常实用的oob_score参数,就是利用这部分“袋外数据”来评估模型,完全不需要额外划分验证集。
我早期不知道这个机制的时候,总喜欢再从数据里切一块验证集出来,搞三层:训练、验证、测试。后来发现随机森林自带oob评分,一套交叉验证的活它自己就干完了。省下来的样本拿去喂模型不香吗?尤其在小数据集上,多一条样本都是宝贵的。
2.2 特征层面的随机子空间:为什么要逼每棵树“偏科”
如果说样本抽样解决的是“每棵树看哪些样本”的问题,那特征抽样解决的就是“每棵树看哪些维度”的问题。随机森林在每一个节点做切分前,会先从全量特征m个里面随机抽出一部分,常见的默认值是根号m(分类)或者m/3(回归)。然后只从抽出来的这堆特征里挑最优切分变量和最优切分点。没有这一层随机性的话,Bagging虽然已经让树和树之间有了差异,但正如前面说的,碰上强特征非常突出的数据集,很多树的顶部结构会雷同,森林就会变成“同一棵树复制了很多遍”,集成带来的方差削减优势直接被抹平。
特征随机性本质上就是在给每棵树“制造偏科”。单看某棵树,它因为没能用到某个全局最强的特征,可能在某些样本上犯错。但不同树偏的方向不一样,有的偏科在这几个特征,有的偏科在那几个特征。聚合的时候,这些偏科错误会被“委员会机制”中和掉。这就像让一群各有盲区的专家投票,每个专家都会犯错,但犯错的区域错开了,大多数时候正确的判断总能胜出。
2.3 两个随机结合之后,模型实际发生了什么变化
我很久以前在kaggle上用一份表格数据做实验,单棵决策树在测试集上的准确率大概是82%左右,加Bagging(也就是特征抽样比例设为1,只做样本抽样)之后能到88%到89%,再把max_features从默认值调低一点,也就是真正引入特征随机性之后,分数能再往上走两三个点,直接冲到91%上下。这个提升幅度不是玄学,是能稳定复现的。
更直观的感受是:模型输出的概率或预测值不会再那么“神经质”。单棵树换个随机种子,预测结果可能就差很多;随机森林对随机种子并不太敏感,就算调大n_estimators到几百棵,结果差不多就是稳定在那一个值附近。这种稳定性在业务落地中极其重要。你去跟老板汇报的时候,总不能每次跑一遍代码结果都不一样,那看起来就很不专业。随机森林这种“多树投票、方差小”的天然属性,让它成为很多线上策略模型的第一选择。
3. 实操中的参数配置与调优逻辑
3.1 先搞清楚哪些参数值得调,哪些是摆设
随机森林的超参数数量不算夸张,但每个参数背后的实际影响天差地别。我用scikit-learn的RandomForestClassifier举例,逐个聊一下我实际调参过程中的感受。
先说n_estimators,也就是树的数量。这个参数是“越多越好”的典型,因为更多树只会让预测更平滑,基本不会带来过拟合风险(单棵树的过拟合不会因为树多而叠加,因为每棵树都在用随机子集)。但代价是训练时间和推理时间线性增长。实际经验是:先设一个偏大的值比如500到1000,配合oob_score=True,训练完看一眼oob曲线(或者直接打印oob_score),如果分数在某个树数量之后基本不再上升,就用那个数量的树,没必要为了心理安慰硬撑1000棵。
再说max_depth和max_leaf_nodes。这两个都是控制单棵树复杂度的。随机森林本身已经通过样本和特征随机性缓解了过拟合,所以对单棵树的剪枝其实不如决策树那么敏感。但完全不限制树深的话,训练集足够大、特征足够多时,内存占用会很难看。我在一个百万级样本的业务数据上跑过一次不限制深度的随机森林,单棵树能长出几千个叶子节点,模型文件保存出来几百兆,后来还是老老实实用max_depth=15到20做了限制,效果几乎没掉,文件体积小了一个数量级。
最值得认真调的参数其实是max_features。这个参数控制每个节点参与竞争的特征数量,是随机森林“随机性”的关键来源。分类问题默认用sqrt(n_features),回归问题默认用n_features/3,但实际最优值跟数据的特征结构和相关性非常相关。如果特征之间有很强的冗余,max_features可以适当调大一点,因为就算选了多个冗余特征,它们提供的信息也不重复,树之间的差异性依然够。如果特征本身就少而且每个都很有用,max_features反而应该调小,让每棵树能看到的特征组合更随机一些。另外还有min_samples_split和min_samples_leaf,这两个参数控制叶子节点的最小样本量,对抑制过拟合有用,在噪声很大的数据上尤其明显。我的习惯是先粗调max_depth,再细调这两个样本量门槛,最后再回头细调max_features,迭代轮次会少一些。
3.2 如何科学地搜索最优参数,而不是全靠感觉
有不少人拿到随机森林第一件事就是开GridSearchCV,把所有参数一股脑放进去,搜索空间巨大,跑了个通宵还没结果。这其实是对计算资源的不尊重。我的流程通常分两轮:
第一轮先锁定n_estimators和max_features的大致方向。我会把max_depth和min_samples_leaf设成比较保守的值,避免出现极端情况,然后在几个候选的max_features值上各跑一次oob_score,画个曲线看看趋势。因为这俩参数很大程度上决定了森林的多样性和收敛行为,值得优先看。
第二轮再固定一个相对合理的n_estimators和max_features,去搜max_depth、min_samples_leaf、min_samples_split这几个跟“单棵树复杂度”相关的参数。因为这几个参数互相纠缠,适合放一起搜索。scikit-learn里推荐用RandomizedSearchCV而不是GridSearchCV,在高维参数空间里随机采样组合,效率更高。设置好n_iter比如60到100轮,基本能覆盖到性能不错的区域。
下面给一个基于随机森林分类的调参模板,代码用scikit-learn实现:
python复制from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint
rf = RandomForestClassifier(random_state=42, oob_score=True, n_jobs=-1)
param_dist = {
"n_estimators": randint(100, 600),
"max_features": ["sqrt", "log2", 0.3, 0.5, 0.7],
"max_depth": randint(5, 30),
"min_samples_split": randint(2, 20),
"min_samples_leaf": randint(1, 10),
"criterion": ["gini", "entropy"]
}
search = RandomizedSearchCV(
rf,
param_distributions=param_dist,
n_iter=80,
cv=5,
scoring="roc_auc",
random_state=42,
n_jobs=-1,
verbose=1
)
search.fit(X_train, y_train)
print(search.best_params_)
上面代码里有个小细节值得提:我把max_features直接给成了"sqrt"、"log2"以及0.3、0.5、0.7这些比例值,scikit-learn是允许用浮点数表示“占总特征数的比例”的。除了让搜索空间更丰富之外,也提醒自己别老是默认用sqrt,在很多特征场景下适当的比例值往往比sqrt要好。
3.3 使用OOB分数替代额外的验证集:一个省样本的好办法
我在走上文说到的调参流程时,核心评估指标一直用的是oob_score,而不去单独划分验证集。因为随机森林有一个天然优势:每棵树都有约36.8%的样本没参与它的训练,这些“袋外样本”相当于那棵树的私有测试集。把森林里所有样本的袋外预测汇总起来,得到的整体精度就是oob_score,这个过程相当于在做一种轻量级的交叉验证。
实际使用中,oob_score跟3折或者5折交叉验证的分数之间往往有很强的一致性,差距一般在一个百分点以内。既然两者相差不大,又何必多消耗训练时间去做交叉验证呢?在小样本场景,比如只有几千条样本的业务数据里,少划走20%的验证集意味着模型能看到更多数据,效果提升可能比调参带来的提升还明显。
不过oob_score也有它的边界。当数据严重不平衡时,oob_score反映的是整体准确率,可能会被多数类主导,这时候单纯看oob_score是不够的,要在评估维度里加上oob的AUC值或者F1。sklearn没有直接输出oob的AUC,但可以通过训练好的forest的oob_decision_function_属性手工去算:
python复制from sklearn.metrics import roc_auc_score
# oob_decision_function_形状为(n_samples, n_classes)
# 二分类时取正类的概率列
oob_proba = rf.oob_decision_function_
auc_oob = roc_auc_score(y_train, oob_proba[:, 1])
print(f"OOB AUC: {auc_oob:.4f}")
这个方法非常实用。不管是调参还是做特征筛选,OOB AUC都比OOB accuracy稳定得多,尤其在类别分布不那么均衡的场景里。
4. 特征重要性分析:如何读懂随机森林的“投票逻辑”
4.1 两种重要性指标的根本区别
随机森林一个特别吸引人的点在于,它训练完之后可以直接告诉你每个特征对预测的贡献有多大。但很多人不清楚的是,sklearn里默认算出来的feature_importances_是基于“不纯度减少”的。具体来说,在每棵树的每个节点做切分时,切分会带来基尼系数(分类)或均方误差(回归)的下降,这个下降值会累加到被选中的那个特征上,最后把所有树的结果汇总再归一化。
这个基于不纯度的指标有两个明显的坑。第一个坑是它偏向数值型特征和高基数类别特征,因为这类特征能提供更多潜在切分点,在贪心分裂搜索的时候更容易“捡到”一个看起来收益很大的切分点,自然累计的不纯度下降就多。第二个坑是当特征之间存在强相关性时,重要性会被“分摊”,两个高度相关的特征,重要性会被分散到两者头上,每个单独看起来都不算高,但合起来其实很重要。这种情况会让特征排序结果产生误导。
如果想得到更可靠的评估,Permutation Importance(排列重要性)是更好的选择。它的思路也很直接:把某个特征的取值随机打乱,破坏该特征与标签的关系,然后看模型性能下降多少。下降越多,说明模型对这个特征的依赖越强。这个指标天然不受特征类型和特征间相关性的干扰,因为它是直接在训练好的模型上做评估。缺点是计算量稍大,需要在验证集上做多次重复打乱才能稳定。
4.2 用随机森林跑一次特征重要性排序的实例
为了把这个问题落到实处,我写一段基于随机森林分类的特征重要性计算代码,包含默认重要性和permutation importance的对比。数据用sklearn自带的乳腺癌数据集,特征不多,方便展示:
python复制import numpy as np
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.inspection import permutation_importance
data = load_breast_cancer()
X, y = pd.DataFrame(data.data, columns=data.feature_names), data.target
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
rf = RandomForestClassifier(
n_estimators=300,
max_depth=8,
min_samples_leaf=3,
random_state=42,
n_jobs=-1
)
rf.fit(X_train, y_train)
# 默认重要性
imp_default = pd.Series(rf.feature_importances_, index=X.columns).sort_values(ascending=False)
# permutation importance
perm_result = permutation_importance(
rf, X_test, y_test, n_repeats=10, random_state=42, n_jobs=-1
)
imp_perm = pd.Series(perm_result.importances_mean, index=X.columns).sort_values(ascending=False)
print("Top5 default importance:")
print(imp_default.head())
print("\nTop5 permutation importance:")
print(imp_perm.head())
这里有一点想要特别建议:排列重要性一定要在测试集上做,不要在训练集上做。因为训练好的模型在训练集上的预测能力强,打乱特征后性能下降幅度可能被放大,导致重要性虚高。测试集上的估计更接近真实应用场景。
4.3 特征重要性分析在业务解释中的边界
特征重要性看着很科学,但千万别把它当成一种“因果推断”工具。比如一份学生压力因素数据里,模型显示“每周运动时长”的重要性很高,这只能说明运动时长这个特征与压力水平的关联性强,不能直接断言“增加运动就能降低压力”。中间可能有隐变量在起作用,比如睡眠质量同时影响运动意愿和压力水平。特征重要性告诉你的是关联模式,至于因果方向,需要用实验设计或者其他因果推断方法去回答。我见过不少产品经理拿着特征重要性的排序表直接写进方案里做因果论断,这种用法很容易踩坑。正确的做法是把它定位成“特征筛选和洞察辅助”,帮你在众多候选特征里找出值得深挖的方向。
5. 从分类到回归,再到真实业务场景实战
5.1 随机森林回归的关键差异
随机森林不只是能分类。RandomForestRegressor在回归任务上同样非常常用,比如预测房价、预测销量这类结构化数据的场景。回归场景下,叶子节点的输出不再是一个类别,而是落在该节点所有样本标签的平均值。森林的最终输出是这么多棵树预测结果的算术平均。和分类相比,回归森林在损失函数上用均方误差(MSE)或者绝对误差(MAE)来指导切分。sklearn默认用squared_error,也就是均方误差,对离群点比较敏感。如果你的标签噪声很大、离群点多,可以试试把criterion设成absolute_error,对异常值的鲁棒性会好一些。
回归任务的评估自然要看R2、MAE、RMSE这些指标。其中RMSE对预测偏差大的样本惩罚比较重,有时候随机森林在测试集上的RMSE表现得不好,但MAE看起来很漂亮,这是因为少数极端样本拖了后腿。需要根据业务语义决定更关注哪一个。
5.2 预测学生压力水平的一个实战演示
之前在很多场合看到“探索影响学生压力的主要因素”这个话题,我发现这非常适合用来演示随机森林在分类和回归之间的切换。假设现在有一份问卷数据,收集了学生的“每周学习时长”、“每周运动时长”、“每日睡眠时长”、“社交活动频率”、“饮食规律性”等特征,标签是“压力水平”,取值范围0到100。我们想知道这些因素里哪些对压力影响最大,并训练一个能预测压力的模型。
下面是用随机森林回归实现这个场景的示意代码,数据用随机模拟和构造的方式生成,但流程可以直接替换成真实数据:
python复制import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score, mean_absolute_error
rng = np.random.RandomState(42)
n = 2000
df = pd.DataFrame({
"study_hours": rng.uniform(1, 12, n),
"exercise_hours": rng.uniform(0, 7, n),
"sleep_hours": rng.uniform(4, 10, n),
"social_frequency": rng.uniform(0, 5, n),
"diet_regularity": rng.randint(1, 6, n),
"part_time_job": rng.choice([0, 1], n, p=[0.6, 0.4])
})
# 模拟压力水平的生成逻辑(只是为了演示,真实场景直接用真实标签)
stress = (
50
- 3.0 * df["exercise_hours"]
- 2.5 * df["sleep_hours"]
- 1.5 * df["social_frequency"]
- 1.0 * df["diet_regularity"]
+ 2.0 * df["study_hours"]
+ 3.0 * df["part_time_job"]
+ rng.normal(0, 5, n)
)
stress = np.clip(stress, 0, 100)
df["stress_level"] = stress
X = df.drop(columns=["stress_level"])
y = df["stress_level"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
rf_reg = RandomForestRegressor(
n_estimators=300,
max_depth=12,
min_samples_leaf=4,
max_features=0.6,
random_state=42,
n_jobs=-1,
oob_score=True
)
rf_reg.fit(X_train, y_train)
y_pred = rf_reg.predict(X_test)
print(f"R2: {r2_score(y_test, y_pred):.4f}")
print(f"MAE: {mean_absolute_error(y_test, y_pred):.4f}")
print(f"OOB R2: {rf_reg.oob_score_:.4f}")
把数据跑完之后,接着计算特征重要性,就会看到运动时长、睡眠时长、学习时长这些特征的排名。这里我要特别提醒一句:上面模拟数据的特征影响系数是我直接写死的,所以在重要性排序上能比较清晰地复现规律。但真实数据往往充满噪声和混淆变量,特征重要性的排序可能没有这么“清爽”,这是正常现象,不要因为排序不符合直觉就怀疑模型跑错了。
5.3 处理类别特征和缺失值时的经验之谈
随机森林在sklearn里的原生实现不支持类别字符串特征直接传入,要么用OrdinalEncoder编码成整数,要么用OneHotEncoder做独热编码。但要注意,如果你用随机森林处理高基数类别特征,比如用户ID、城市代码这种,基于不纯度的特征重要性会特别偏向它们,这一点我在前文已经提过。这种情况下可以给特征做目标编码(Target Encoding),也就是用类别对应的标签均值替代原始类别值,但目标编码容易引入标签泄露,需要配合交叉验证或者平滑系数使用,新手慎用。
缺失值方面,sklearn的随机森林支持在内部节点分裂时自动处理缺失值的分支(新版本支持缺失值,旧版本需要提前填充)。最省事的做法是用SimpleImputer填充均值/中位数/众数。如果你只想把缺失值本身作为一种信息保留下来,可以加一个“is_null”的布尔特征,这样模型有机会学到“缺失本身就是一种模式”。在业务数据里,某个字段缺失往往跟样本的特殊背景有关,加上这种特征经常能捡到一点效果提升。
6. 常见问题与排查技巧实录
6.1 始终不变的结果和意想不到的报错
随机森林的日常使用里,我碰到过几个典型问题,几乎每个接触过的人都会遇到。我整理成一个表,方便需要的时候直接对号入座。
| 问题现象 | 常见原因 | 解决办法 |
|---|---|---|
| 训练很慢,调参动辄几小时 | n_estimators过大且没有释放多核 | 设n_jobs=-1;在收敛后降低树的数量;对大数据用直方图方法(如HistGradientBoosting)替代 |
| 模型在训练集上几乎完美,测试集上一般 | 树过深、叶子节点样本太少 | 限制max_depth,提高min_samples_leaf;用OOB分数监督泛化表现 |
| 结果在不同机器/版本上不一致 | scikit-learn版本差异或缺少固定随机种子 | 记录random_state和版本号;复现实验时固定python环境 |
| 特征重要性排序很不稳定 | 样本太少、树太少、特征存在强共线性 | 增大n_estimators、用permutation importance、检查相关性矩阵 |
| 类别严重不平衡 | 默认的accuracy不再是好指标 | 用class_weight="balanced";按业务改用AUC、F1等指标 |
| 数据维度非常高时内存爆掉 | 每棵树存储开销大,预测也慢 | 限制max_depth和叶子数量;改用线性模型作为baseline;降维后再训练 |
6.2 关于类别不平衡和样本权重的实战心得
随机森林处理不平衡数据不像某些模型那么脆弱,但也别指望它无脑好用。scikit-learn里有个非常实用的参数叫class_weight,可以设成"balanced",它会根据类别频率自动给少数类样本更高的权重。除此之外,样本权重在业务场景中也很常用。比如预测学生高压力风险时,如果你更关心“高风险学生”这个少数类,可以额外给这些样本更大的业务权重,让模型更努力地拟合它们。代价是整体准确率可能下滑一点,但召回率会上升。到底怎么权衡,取决于业务端“漏报”和“误报”哪个代价更高,这个取舍不是模型替你决定的。
另外提一个很多人在做平衡采样时容易犯的错:用过采样(比如SMOTE)之前一定要先划分训练集和测试集,并且只在训练集上做。如果把全量数据过采样之后再划分,测试集里就会混入很多由训练样本插值生成的伪样本,导致测试集评估结果虚高,上线之后一测真实效果就现原形。
6.3 什么时候不该用随机森林
要承认,随机森林不是万能的。它在表格数据上确实能打,但在超高维稀疏数据(比如文本TF-IDF向量,动辄几十万维)上,它的表现通常不如线性模型之后再做非线性映射的方式,训练时间和内存开销也很感人。它也不擅长做小样本上的极致拟合,如果只有几百条样本,模型很容易被噪声带偏,这时候更推荐用带强正则化的模型。而在图像、文本序列这类强空间结构的数据上,随机森林的作用范围就更有限了。
遇到这些场景,我自己的原则是:先跑一个简单的线性模型或者逻辑回归当基线,再跑随机森林,假如随机森林的领先幅度不到两三个点,那我会认真考虑是不是应该选更简单、可解释性更好的方案。要知道在业务端,简单模型在调试、解释、上线、维护上省下来的成本,有时候比那几个点的精度值钱得多。
7. 从单模型到森林,再到更复杂的集成之路
随机森林学明白之后,再去碰其他集成方法会顺畅不少。Gradient Boosting(梯度提升)可以理解成和Bagging相反的思路,后者并行地训练一堆独立模型再融合,前者则是串行地训练一堆弱模型,每个新模型都在拟合前面所有模型的残差。随机森林的优点是训练快、好调参、不容易过拟合;梯度提升的优点是精度上限通常更高,尤其在海量数据和比赛场景里表现突出。如果是业务落地,我会优先考虑随机森林,先把baseline稳稳立住;如果追求最后一两个点的精度提升,再在随机森林的基础上去试XGBoost、LightGBM或者CatBoost。
从实践路径来看,随机森林还有一个很大的优势:它基本不需要特征归一化,数值型特征和类别型特征能同时喂进去,树模型对特征尺度天然不敏感。这意味着数据预处理可以做得很轻。也正是因为这一点,随机森林在快速验证一个项目可行性时,永远是我心里的第一梯队算法。
从初学到现在,我在随机森林上花的时间其实不算少,但它带给我的收益也很直接。把一个黑盒模型的名字玩明白并不难,难得是弄懂它为什么有效、什么时候失效、怎么通过参数调整让它更适合自己的数据。这篇文章没有搞那些特别炫酷的东西,我把这些年实操过程中真正影响结果的关键点和踩过的坑都写了出来。如果你正准备在你的数据上跑一把随机森林,不妨按照上面说的流程走一遍,先把OOB分数跑出来,再把特征重要性打出来看一眼,你对自己数据的理解,一定会比原来深一层。
