集成学习之随机森林
一棵决策树容易"死记硬背"过拟合,那就种一片森林让它们集体投票------这就是随机森林的核心哲学。本文将从"为什么需要随机森林"出发,结合两个实战案例(垃圾邮件识别、银行贷款违约预测)逐行解析代码逻辑,详细讲解参数调优方法,并对比随机森林与决策树的差异。
📚 目录
- 一、从决策树的痛点说起:为什么需要随机森林?
- 二、集成学习的两大流派
- 三、随机森林算法详解
- 四、代码实战一:垃圾邮件识别
- 五、代码实战二:银行贷款违约预测(含样本不平衡处理)
- 六、算法逻辑深度解析(基于代码)
- 七、参数调优详解(重点)
- 八、特征重要性的意义
- 九、随机森林的意义
- 十、随机森林的优缺点
- [十一、随机森林 vs 决策树](#十一、随机森林 vs 决策树)
- 十二、总结
一、从决策树的痛点说起:为什么需要随机森林?
在学习决策树时我们已经知道:一棵没有约束的决策树会不断生长,直到把每个训练样本都"完美"分对为止。这就像一个学生把考试题原题死记硬背下来,遇到新题就傻眼了------这就是过拟合(Overfitting)。
那有没有办法让模型既保留决策树"可解释、易实现"的优点,又不容易过拟合呢?思路很朴素:
"三个臭皮匠,顶个诸葛亮" ------ 既然一棵树容易犯错,那就训练很多棵树,让它们集体决策。
这就是集成学习(Ensemble Learning) 的思想:组合多个"弱学习器",得到一个更强的"强学习器"。
二、集成学习的两大流派
在进入随机森林之前,先厘清集成学习的两大流派,避免混淆:
| 流派 | 代表算法 | 核心思想 | 类比 |
|---|---|---|---|
| Bagging(装袋法) | 随机森林 | 多个模型并行训练,相互独立,最终投票/平均 | 多个学生独立做题,最后对答案取多数 |
| Boosting(提升法) | GBDT、XGBoost、AdaBoost | 模型串行训练,后一个模型纠正前一个的错误 | 学生做完题对答案,下个学生专攻上个人做错的题 |
随机森林(Random Forest)属于 Bagging 流派,它的"弱学习器"就是决策树。
三、随机森林算法详解
3.1 核心思想
随机森林 = Bagging + 决策树 + 特征随机化。
它通过**两个"随机"**来保证每棵树都不一样,从而让森林"既聪明又多样":
- 样本随机(Row Random) :每棵树从训练集中有放回地 抽取相同数量的样本(Bootstrap 抽样)。这样每棵树看到的样本不一样,有的样本会被重复抽中,有的样本一次都抽不到(约 36.8% 的样本不会被抽中,称为袋外数据 OOB)。
- 特征随机(Column Random) :每次节点分裂时,不是从所有特征里选最优分裂点,而是先随机抽一部分特征,再在这部分特征里选最优。
💡 通俗类比:假设你要判断一封邮件是不是垃圾邮件。
- 如果只问一个人(一棵树),他可能只盯着"免费"这个词就下结论,太片面。
- 随机森林的做法是:找 100 个人,每个人只看邮件的一部分内容(特征随机)和一部分案例(样本随机),最后举手表决。这样结论更稳健。
3.2 工作流程
训练阶段:
输入:训练集 D,决策树数量 T,每次分裂随机选的特征数 m
1. for i = 1 to T:
2. 从 D 中有放回抽样得到子集 D_i(Bootstrap)
3. 用 D_i 训练一棵决策树 T_i,每次分裂只从 m 个随机特征中选最优
4. 决策树完全生长(不剪枝,靠随机性防过拟合)
输出:森林 {T_1, T_2, ..., T_T}
预测阶段:
- 分类任务 :所有树各自预测一个类别,多数投票(Majority Voting) 决定最终结果。
- 回归任务 :所有树预测值的平均值作为最终结果。
3.3 袋外数据(OOB)------免费的验证集
由于 Bootstrap 是有放回抽样,每棵树大约有 36.8% 的训练样本没有被抽到,这部分叫袋外数据(Out-Of-Bag, OOB)。
这相当于每棵树自带了一个"没见过的验证集",可以直接用它评估模型,不用额外划分验证集 。sklearn 中设置 oob_score=True 即可启用。
3.4 算法结构示意图
下图直观展示了随机森林的训练与预测流程:训练阶段通过 Bootstrap 抽样生成多棵相互独立的决策树,预测阶段对所有树的输出进行投票(分类)或平均(回归)。

3.5 RandomForestClassifier API 参数详解
sklearn 中随机森林分类器的完整签名:
python
from sklearn.ensemble import RandomForestClassifier
RandomForestClassifier(
n_estimators=100, # 森林中决策树的数量
criterion='gini', # 分裂质量的度量标准
max_depth=None, # 决策树最大深度
min_samples_split=2, # 节点继续分裂所需的最小样本数
min_samples_leaf=1, # 叶子节点最少样本数
min_weight_fraction_leaf=0.0, # 叶子节点最小权重和占比
max_features='auto', # 每次分裂随机选用的特征数
max_leaf_nodes=None, # 叶子节点最大数量
min_impurity_decrease=0.0, # 分裂引起的最小不纯度下降值
bootstrap=True, # 是否进行 Bootstrap 有放回抽样
oob_score=False, # 是否使用袋外数据评估
n_jobs=None, # 并行训练的 CPU 核数
random_state=None, # 随机种子
verbose=0, # 日志冗余度
warm_start=False, # 是否在已有模型基础上继续训练
class_weight=None, # 类别权重(处理样本不平衡)
ccp_alpha=0.0, # 最小代价复杂度剪枝参数
)
按作用分组介绍:
① 森林规模与随机性
| 参数 | 说明 |
|---|---|
n_estimators |
决策树数量。越多越稳定,但收益递减且训练变慢。通常 100~500 足够,本文代码用 50/100。 |
bootstrap |
是否 Bootstrap 抽样,默认 True。若设 False,则每棵树用全部样本训练(失去样本随机性,不推荐)。 |
random_state |
随机种子,固定后结果可复现。调参时务必固定,否则无法判断参数效果。 |
n_jobs |
并行训练核数,-1 表示用全部 CPU 核。案例二用了 n_jobs=-1 加速。 |
② 特征随机化
| 参数 | 说明 |
|---|---|
max_features |
每次分裂随机选用的特征数。可取值: • 'auto'/'sqrt':√总特征数(分类默认) • 'log2':log₂(总特征数) • 浮点数 0.8:总特征数 × 0.8(本文代码用法) • 整数 5:固定 5 个 • None:用全部特征(失去特征随机性) 越小,树之间差异越大,越抗过拟合,但单棵树准确率下降。 |
③ 树的结构(控制过拟合的核心,本文重点调优对象)
| 参数 | 说明 |
|---|---|
criterion |
分裂标准,'gini'(基尼系数,默认)或 'entropy'(信息熵)。两者效果通常接近。 |
max_depth |
树的最大深度。越大越容易过拟合,None 表示不限。本文调优区间 5,10,15,20。 |
min_samples_split |
节点继续分裂所需最小样本数。越大越保守,本文调优区间 2,3,4,5。 |
min_samples_leaf |
叶子节点最少样本数。越大越平滑,本文调优区间 1,2,3,4,5。 |
max_leaf_nodes |
叶子节点最大数量,None 表示不限。可替代 max_depth 控制复杂度。 |
min_impurity_decrease |
分裂必须带来的最小不纯度下降,否则不分裂。值越大越保守。 |
ccp_alpha |
最小代价复杂度剪枝参数(后剪枝)。值越大剪枝越狠,0 表示不剪枝。 |
④ 样本权重与评估
| 参数 | 说明 |
|---|---|
class_weight |
类别权重,处理样本不平衡。可取 'balanced'(自动按频率反比加权)、字典 {0:1, 1:5}、None。案例二用的是下采样,也可改用此参数。 |
min_weight_fraction_leaf |
叶子节点最小权重和占比,作用类似 min_samples_leaf 但基于权重。 |
oob_score |
是否用袋外数据评估,默认 False。设 True 可通过 rf.oob_score_ 查看袋外准确率,省去划分验证集。 |
warm_start |
是否在已有模型上继续添加树,适合逐步增大 n_estimators 观察 OOB 曲线。 |
💡 调参优先级建议:
- 先固定
random_state,设n_estimators足够大(如 100)。- 重点调
max_depth、min_samples_split、min_samples_leaf三个结构参数(本文做法)。- 视情况调整
max_features、class_weight、oob_score。- 最后微调
n_estimators至性能稳定(再增大无提升)。
四、代码实战一:垃圾邮件识别
数据集 spambase.csv 包含 4601 封邮件,57 个特征(单词频率、字符频率、大写字母统计等),标签 label:1=垃圾邮件,0=正常邮件。
4.1 数据加载与划分
python
import pandas as pd
from sklearn.model_selection import train_test_split
df = pd.read_csv("/Classical_Machine_Learning/data/spambase.csv")
x = df.iloc[:, :-1] # 前 57 列为特征
y = df.iloc[:, -1] # 最后一列为标签
xtrain, xtest, ytrain, ytest = train_test_split(x, y, test_size=0.2, random_state=42)
test_size=0.2:80% 训练,20% 测试。random_state=42:固定随机种子,保证结果可复现。
4.2 网格搜索 + 交叉验证调参
这是本文的核心,重点讲解:
python
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
# 待调优的三组参数
max_depth = [5, 10, 15, 20] # 决策树最大深度
min_samples_split = [2, 3, 4, 5] # 节点继续分裂所需的最小样本数
min_samples_leaf = [1, 2, 3, 4, 5] # 叶子节点最少样本数
best_score = 0
i = 1
for depth in max_depth:
for split in min_samples_split:
for leaf in min_samples_leaf:
rf1 = RandomForestClassifier(
n_estimators=100, # 森林里决策树的数量
max_features=0.8, # 每次分裂随机选 80% 特征
max_depth=depth,
min_samples_split=split,
min_samples_leaf=leaf,
random_state=42,
)
# 8 折交叉验证,评价指标用 recall(召回率)
score = cross_val_score(rf1, xtrain, ytrain, cv=8, scoring='recall')
mean_score = score.mean()
if mean_score > best_score:
best_score = mean_score
best_depth = depth
best_split = split
best_leaf = leaf
print(f"第{i}条:Depth={depth},Split={split},Leaf={leaf} -> Recall: {mean_score:.4f}")
i += 1
print(f"Best_depth={best_depth},Best_split={best_split},Best_leaf={best_leaf},Best Score={best_score:.4f}")
运行结果(节选):
第1条:Depth=5,Split=2,Leaf=1 -> Recall: 0.8341
第2条:Depth=5,Split=2,Leaf=2 -> Recall: 0.8262
...
第80条:Depth=20,Split=5,Leaf=5 -> Recall: 0.8805
Best_depth=20,Best_split=2,Best_leaf=1,Best Score=0.8856
共 4×4×5=80 组参数组合,代码遍历所有组合,记录使召回率最高的一组。
4.3 用最优参数训练最终模型
python
rf = RandomForestClassifier(
n_estimators=100,
max_features=0.8,
max_depth=best_depth,
min_samples_split=best_split,
min_samples_leaf=best_leaf,
random_state=42,
)
rf.fit(xtrain, ytrain)
train_predicted = rf.predict(xtrain)
test_predicted = rf.predict(xtest)
from sklearn import metrics
print("训练集测试结果:")
print(metrics.classification_report(ytrain, train_predicted, digits=9))
print("测试集训练结果:")
print(metrics.classification_report(ytest, test_predicted, digits=9))
运行结果示例:
训练集测试结果:
precision recall f1-score support
0 0.998930 0.997322 0.998125 2611
1 0.995885 0.998354 0.997118 1069
accuracy 0.997798 3680
macro avg 0.997408 0.997838 0.997621 3680
测试集训练结果:
precision recall f1-score support
0 0.948000 0.970109 0.958923 549
1 0.945098 0.904478 0.924340 335
accuracy 0.946798 884
可以看到训练集准确率 99.8%,测试集 94.7%,存在轻微过拟合(正常现象),但泛化能力已相当不错。
4.4 特征重要性可视化
随机森林有个非常有用的副产品------特征重要性(Feature Importance),它可以告诉我们哪些特征对分类贡献最大:
python
import matplotlib.pyplot as plt
importances = rf.feature_importances_ # 模型自带的特征重要性属性
im = pd.DataFrame(importances, columns=['importances'])
clos = df.columns.tolist()[:-1] # 去掉标签列
im['clos'] = clos
im = im.sort_values(by='importances', ascending=False)[:10] # 取前 10 重要特征
index = range(len(im))
plt.yticks(index, im.clos)
plt.barh(index, im['importances'])
plt.show()
输出图形(横向条形图,越靠上越重要):
Char_freq_! ████████████████████ 0.15
Word_freq_free ███████████████ 0.11
Word_freq_remove ██████████ 0.07
Capital_run_length_longest ███████ 0.06
Word_freq_$ ██████ 0.05
...(共 10 条)
这说明邮件中感叹号
!、free、remove、$等出现频率越高,越可能是垃圾邮件------非常符合直觉。
五、代码实战二:银行贷款违约预测(含样本不平衡处理)
第二个案例 银行贷款(随机森林).py 使用信用卡交易数据 creditcard.csv 检测欺诈交易。它的难点在于:正常交易(Class=0)远多于欺诈交易(Class=1),正负样本比例悬殊。
5.1 数据预处理与标准化
python
import pandas as pd
from sklearn.preprocessing import StandardScaler
data = pd.read_csv(r".../creditcard.csv")
scaler = StandardScaler()
data['Amount'] = scaler.fit_transform(data[['Amount']]) # 对金额列做 Z 标准化
data = data.drop(['Time'], axis=1) # 删除无用的 Time 列
- Z 标准化:把数据转成均值 0、方差 1 的分布,消除量纲影响。
- Amount 列原本数值范围很大(0 ~ 几万),标准化后与其他特征处于同一量级,避免大数值特征"压制"小数值特征。
5.2 下采样解决样本不平衡
python
from sklearn.model_selection import train_test_split
x_whole = data.drop(['Class'], axis=1)
y_whole = data['Class']
x_train_w, x_test_w, y_train_w, y_test_w = train_test_split(
x_whole, y_whole, test_size=0.3, random_state=42)
x_train_w['Class'] = y_train_w
data_train = x_train_w
# 下采样:从多数类(正常)中抽取与少数类(欺诈)相同数量的样本
positive_eg = data_train[data_train['Class'] == 0]
negative_eg = data_train[data_train['Class'] == 1]
positive_eg = positive_eg.sample(len(negative_eg)) # 随机抽样使两类数量相等
data_c = pd.concat([positive_eg, negative_eg])
x_whole = data_c.drop('Class', axis=1)
y_whole = data_c.Class
x_train_w, x_test_w, y_train_w, y_test_w = train_test_split(
x_whole, y_whole, test_size=0.3, random_state=1000)
💡 为什么要下采样?
假设欺诈样本只占 0.5%,如果直接训练,模型只要全部预测为"正常"就能达到 99.5% 准确率------但完全没识别出欺诈,毫无意义。下采样让两类样本数量相等,强迫模型认真学习少数类的特征。
5.3 调参与训练(带进度条)
逻辑与案例一相同,这里用 tqdm 增加了进度条,且评价指标仍是 recall:
python
from tqdm import tqdm
max_depth = [5, 10, 15]
min_samples_split = [2, 3, 4, 5]
min_samples_leaf = [1, 2, 3, 4, 5]
best_score = 0
total = len(max_depth) * len(min_samples_split) * len(min_samples_leaf)
with tqdm(total=total, desc="随机森林调参") as pbar:
for depth in max_depth:
for split in min_samples_split:
for leaf in min_samples_leaf:
rf1 = RandomForestClassifier(
n_estimators=50,
max_features=0.8,
max_depth=depth,
min_samples_split=split,
min_samples_leaf=leaf,
random_state=42,
)
score = cross_val_score(rf1, x_train_w, y_train_w,
cv=8, scoring='recall', n_jobs=-1)
mean_score = score.mean()
if mean_score > best_score:
best_score = mean_score
best_depth = depth
best_split = split
best_leaf = leaf
pbar.set_postfix({"最佳Recall": f"{best_score:.4f}"})
pbar.update(1)
print(f"Best_depth={best_depth},Best_split={best_split},Best_leaf={best_leaf},Best Score={best_score:.4f}")
运行结果示例:
随机森林调参: 100%|████████████████| 60/60 [02:13<00:00, 2.22s/it, 最佳Recall=0.9123]
Best_depth=10,Best_split=2,Best_leaf=1,Best Score=0.9123
后续 fit + predict + classification_report 与案例一一致,此处不再赘述。
关键差异点 :本例
n_jobs=-1启用多核并行加速(cross_val_score的参数),在大数据集上能显著缩短调参时间。
六、算法逻辑深度解析(基于代码)
6.1 整体执行流程
原始数据 → 划分训练/测试集 → 网格搜索+交叉验证找最优参数
→ 用最优参数训练最终模型 → 训练集/测试集评估 → 特征重要性可视化
6.2 交叉验证(Cross Validation)的作用
代码中 cross_val_score(rf1, xtrain, ytrain, cv=8, scoring='recall') 是关键:
cv=8:把训练集分成 8 份,每次用 7 份训练、1 份验证,轮换 8 次,取平均。- 这样能避免"参数碰巧在某一划分下表现好"的偶然性,评估更稳健。
- 注意:交叉验证只在训练集 上做,测试集全程不参与调参,保证评估的客观性。
6.3 投票机制
RandomForestClassifier 内部预测时,100 棵树各自给出一个类别预测,最终取多数票。例如 100 棵树中 73 棵判定为垃圾邮件,27 棵判定为正常,则最终预测为垃圾邮件。这种"集体决策"比单棵树稳定得多。
七、参数调优详解(重点)
7.1 需要调优的参数一览
RandomForestClassifier 的参数可分为三类:
| 类别 | 参数 | 代码中的值 | 作用 | 是否需调优 |
|---|---|---|---|---|
| 森林规模 | n_estimators |
50 / 100 | 树的数量,越多越稳定但越慢 | ⭐ 一般设大点(100~500),不必精细调 |
| 特征随机 | max_features |
0.8 | 每次分裂随机选 80% 特征 | ⭐ 可调,分类常用 √总特征数 |
| 树的结构 | max_depth |
5,10,15,20 | 树的最大深度,控制过拟合 | ⭐⭐⭐ 必调 |
min_samples_split |
2,3,4,5 | 节点继续分裂所需最小样本数 | ⭐⭐⭐ 必调 | |
min_samples_leaf |
1,2,3,4,5 | 叶子节点最少样本数 | ⭐⭐⭐ 必调 | |
| 随机性 | random_state |
42 | 固定随机种子,保证可复现 | 不调(固定即可) |
💡 记忆口诀:
max_depth:树能长多"高"------越大越容易过拟合。min_samples_split:节点要"分裂"至少需要多少样本------越大越保守。min_samples_leaf:叶子要"结出"至少多少样本------越大越平滑。三个参数都越大越保守、越小越激进,调参时在"欠拟合"和"过拟合"之间找平衡。
7.2 看什么"率"?------评价指标选择
代码中 scoring='recall'(召回率),这是有讲究的:
| 指标 | 含义 | 通俗解释 | 适用场景 |
|---|---|---|---|
| accuracy(准确率) | 预测正确的比例 | 100 题做对几道 | 样本均衡时 |
| precision(精确率) | 预测为正的里真正为正的比例 | 报警的里有几个真坏蛋 | 误报代价高时(如垃圾邮件误判正常邮件) |
| recall(召回率) | 真正为正的被找出来的比例 | 坏蛋有没有被抓全 | 漏报代价高时(如欺诈检测、疾病筛查) |
| f1 | precision 和 recall 的调和平均 | 综合平衡 | 两者都重要时 |
为什么两个案例都用 recall?
垃圾邮件:宁可把正常邮件误判为垃圾邮件(误报),也不能让垃圾邮件漏网(漏报)------但其实这里用 f1 更平衡,作者选 recall 强调"不漏检"。
银行欺诈 :欺诈交易漏掉一笔可能损失巨大,必须高召回------漏报代价远高于误报代价,所以 recall 是核心指标。
💡 记忆口诀:T/F = 判断对错(True/False = 对/错)
P/N = 预测类别(Positive/Negative = 预测为正/负)
Recall = TP / (TP + FN) = 真正的正类中,被正确预测出来的比例 = "坏蛋有没有抓全"
7.3 调参方法:网格搜索 + 交叉验证
代码采用最直观的网格搜索(Grid Search):
- 列出候选参数:3~4 个关键参数各列几个候选值。
- 穷举所有组合:三重 for 循环遍历每个组合(案例一 80 组,案例二 60 组)。
- 交叉验证评估:每组参数用 8 折交叉验证算平均 recall。
- 选最优组合:记录 recall 最高的参数组合。
进阶方法(代码未用,但值得了解):
-
GridSearchCV:sklearn 封装好的网格搜索,写法更简洁:pythonfrom sklearn.model_selection import GridSearchCV param_grid = { 'max_depth': [5, 10, 15, 20], 'min_samples_split': [2, 3, 4, 5], 'min_samples_leaf': [1, 2, 3, 4, 5], } grid = GridSearchCV(RandomForestClassifier(n_estimators=100, random_state=42), param_grid, cv=8, scoring='recall', n_jobs=-1) grid.fit(xtrain, ytrain) print(grid.best_params_) -
RandomizedSearchCV:参数组合太多时,随机抽样搜索,效率更高。 -
调参顺序经验 :先调
n_estimators(让它足够大),再调max_depth,最后微调min_samples_split和min_samples_leaf。
八、特征重要性的意义
代码末尾用 rf.feature_importances_ 绘制了特征重要性条形图,这一步看似简单,实则价值巨大:
- 特征筛选:剔除重要性极低特征,降维提速、减少噪声。
- 业务洞察:告诉业务方"哪些因素最影响结果",如银行可重点关注 Amount、某些交易模式。
- 可解释性:随机森林虽是"黑箱"集成模型,但特征重要性提供了部分解释能力。
⚠️ 注意 :sklearn 的特征重要性基于"不纯度下降(MDI)",对高基数(取值多)特征有偏好。若要更可靠的重要性,可用
permutation_importance(排列重要性)。
九、随机森林的意义
随机森林在机器学习发展史上地位重要,原因在于:
- "开箱即用"的强基线:无需复杂调参,默认参数就能拿到不错的成绩,是工业界首选的 baseline 模型之一。
- 兼顾准确与稳健:通过双重随机性有效抑制过拟合,泛化能力强。
- 推动集成学习普及:它是 Bagging 思想的集大成者,理解了随机森林,再学 GBDT/XGBoost 会顺畅很多。
- 提供特征解释:弥补了深度学习等黑箱模型的可解释性短板。
十、随机森林的优缺点
10.1 优点
| 优点 | 说明 |
|---|---|
| ✅ 准确率高 | 多树投票,通常优于单棵决策树 |
| ✅ 抗过拟合 | 双重随机性 + 集成,泛化能力强 |
| ✅ 处理高维数据 | 特征随机选择,无需特征选择也能跑 |
| ✅ 可评估特征重要性 | 自带 feature_importances_ |
| ✅ 对缺失值/异常值鲁棒 | 比逻辑回归、SVM 更"皮实" |
| ✅ 并行训练 | 各树独立,可多核并行(n_jobs=-1) |
| ✅ 不需太多特征工程 | 不需要标准化、归一化(但本案例标准化是为了统一量纲,非必须) |
10.2 缺点
| 缺点 | 说明 |
|---|---|
| ❌ 可解释性差 | 比单棵决策树难解释,无法画出清晰的判断路径 |
| ❌ 预测速度较慢 | 树多时预测需遍历所有树 |
| ❌ 内存占用大 | 需存储所有树 |
| ❌ 对极稀疏特征(如文本 one-hot)效果一般 | 不如线性模型 |
| ❌ 外推能力差 | 不能预测训练集范围外的值(回归任务) |
十一、随机森林 vs 决策树
| 对比维度 | 决策树 | 随机森林 |
|---|---|---|
| 模型结构 | 单棵树 | 多棵树集成 |
| 训练方式 | 直接训练 | Bootstrap 抽样 + 多树并行训练 |
| 预测方式 | 单树输出 | 多树投票/平均 |
| 过拟合风险 | 高(易"死记硬背") | 低(随机性 + 集成抑制) |
| 准确率 | 一般 | 通常更高 |
| 稳定性 | 数据微小变化可能导致树结构剧变 | 稳定,对噪声鲁棒 |
| 可解释性 | 强(可画出决策路径) | 弱(但有特征重要性) |
| 训练速度 | 快 | 慢(树多) |
| 预测速度 | 快 | 慢 |
| 特征选择 | 用全部特征 | 每次分裂只用部分随机特征 |
| 超参数 | 少(depth、split、leaf) | 多(再加 n_estimators、max_features) |
💡 一句话总结:决策树是"单兵作战",随机森林是"集团军作战"------牺牲了部分可解释性和速度,换来更高的准确率和稳定性。
十二、总结
本文从决策树过拟合的痛点出发,系统讲解了随机森林算法:
- 思想:Bagging + 决策树 + 双重随机性(样本随机 + 特征随机),多树投票抑制过拟合。
- 实战:两个案例(垃圾邮件、银行欺诈)展示了完整流程:数据划分 → 网格搜索+交叉验证调参 → 训练评估 → 特征重要性可视化。
- 调优 :核心调
max_depth、min_samples_split、min_samples_leaf三个结构参数;评价指标根据业务选择------漏报代价大选 recall,误报代价大选 precision,二者兼顾选 f1。 - 优劣:准确稳健、抗过拟合、易用;但可解释性差、预测慢、内存大。
- 对比决策树:用"集成"换"稳定与准确",用"随机"换"多样性",是决策树的自然进化。
学习建议:理解随机森林后,下一步可对比学习 Boosting 流派的 GBDT、XGBoost、LightGBM,体会"并行投票"与"串行纠错"的本质差异,这会极大加深对集成学习的理解。