Bagging思想之随机森林算法介绍
1 课程概览
本课介绍随机森林算法,属于Bagging思想。随机森林采用决策树作为弱学习器,通过有放回随机抽样(行+列)训练多棵决策树,最终平权投票决定结果。重点讲解构建方法和API参数。
2 核心概念与定义
- 随机森林:基于Bagging思想的集成学习算法。
- 弱学习器:决策树(默认CART)。
- 有放回随机抽样:对行和列都随机抽样。
- 平权投票:每个弱学习器一票,多数表决。
- 自助法:Bootstrap,有放回抽样。
3 算法与模型详解
3.1 随机森林概述
所属思想:Bagging(并行执行)
Bagging三句话:
- 有放回的随机抽样
- 平权投票
- 并行执行
弱学习器:决策树(默认CART)
为什么用决策树:
- 多棵树组成森林
- 每个弱学习器都是决策树
3.2 随机森林构建方法
核心:自助法 + 特征子集
问题:如果用全部样本,样本一样、模型一样,结果必然一样,没有意义
解决方案:对行和列都随机抽样
步骤:
- 随机选取M条数据(行抽样)
- 随机选取K个特征(列抽样)
- 用M行×K列训练一棵决策树(默认CART)
- 重复1-3,构造N棵决策树(N一般为奇数)
- 平权投票,集成N个弱学习器的结果
3.3 行列双重抽样
行抽样:
- 从1000行中随机抽取600行(或700行等)
- 有放回抽样
列抽样:
- 从10列中随机抽取7列(或8列等)
- 保证列的角度也有交集和差异
双重抽样:
- 行:600行 × 10列
- 列:600行 × 7列
- 最终:600行 × 7列
目的:无论行还是列,既有交集又有差异
3.4 随机森林API
导入:
from sklearn.ensemble import RandomForestClassifier
重要参数:
n_estimators
- 含义:要几个决策树(模型数量)
- 默认:100
- 建议:奇数(避免投票平局)
criterion
- 含义:特征选择标准
- 取值:
gini(默认):CART树entropy:ID3/C4.5
max_depth
- 含义:树的最大深度
- 默认:None(尽可能生长)
- 建议:手动指定,防止过拟合
max_features
- 含义:每次随机抽样多少个特征(列)
- 取值:
auto/sqrt:开平方(如16列→4列)log2:求对数None:抽全部特征
- 默认:sqrt
bootstrap
- 含义:是否有放回抽样
- 默认:True
min_samples_split
- 含义:节点分裂所需最小样本数
- 示例:设置为5,节点样本数<5则不再分裂
min_samples_leaf
- 含义:叶子节点最小样本数
- 示例:设置为5,叶子节点样本数<5则与兄弟节点合并
min_impurity_split
- 含义:节点划分最小不纯度
- 说明:不纯度(基尼系数/均方差)小于阈值则不再划分
- 注意:新版API已改名
3.5 为什么N一般为奇数
原因:
- 避免投票平局
- 偶数可能出现票数相同
- 奇数保证一定能投出结果
偶数情况:
- 如果票数相同,用奥卡姆剃刀原则
- 底层也能解决,但建议用奇数
3.6 面试常问问题
Q1:为什么要随机抽样?
答:
- 随机森林属于Bagging思想
- 多个弱学习器并行执行,默认都是CART树
- 如果训练集都一样,结果也一样
- 有放回的随机抽样保证多个CART弱学习器训练集既有交集又有差异
- 结果更精准,更有说服力
Q2:为什么要有放回?
答:
- 无放回:每个树训练样本完全不同,没交集,有偏差
- 有放回:保证既有交集又有差异
- 投票结果更有说服力
4 代码示例
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import accuracy_score
from sklearn.datasets import make_classification
# 1. 生成数据
X, y = make_classification(
n_samples=1000,
n_features=16, # 16个特征
n_informative=8,
n_classes=2,
random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
print(f"训练集: {X_train.shape}")
print(f"测试集: {X_test.shape}")
print(f"特征数: {X.shape[1]}")
# 2. 随机森林基本使用
print("\n=== 随机森林基本使用 ===")
rf = RandomForestClassifier(
n_estimators=100, # 100棵决策树
criterion='gini', # CART树
max_depth=10, # 最大深度
max_features='sqrt', # 开平方:sqrt(16)=4个特征
bootstrap=True, # 有放回抽样
random_state=42
)
rf.fit(X_train, y_train)
rf_acc = accuracy_score(y_test, rf.predict(X_test))
print(f"准确率: {rf_acc:.4f}")
print(f"树的数量: {rf.n_estimators}")
print(f"使用的特征数: {rf.max_features}")
# 3. 模拟随机森林的行列抽样
print("\n=== 模拟行列抽样 ===")
np.random.seed(42)
n_samples = 1000
n_features = 16
for i in range(3):
# 行抽样(有放回)
row_idx = np.random.choice(n_samples, 600, replace=True)
# 列抽样(无放回)
col_idx = np.random.choice(n_features, 4, replace=False) # sqrt(16)=4
print(f"树{i+1}: 行={len(set(row_idx))}个唯一, 列={col_idx}")
# 4. 模拟投票决策
print("\n=== 模拟投票决策 ===")
# 训练5棵决策树
trees = []
for i in range(5):
# 行抽样
idx = np.random.choice(len(X_train), len(X_train), replace=True)
# 列抽样
col_idx = np.random.choice(X.shape[1], 4, replace=False)
dt = RandomForestClassifier(
n_estimators=1, # 单棵树
max_features=None,
random_state=i
)
dt.fit(X_train[idx][:, col_idx], y_train[idx])
trees.append((dt, col_idx))
# 各树预测
predictions = []
for dt, col_idx in trees:
pred = dt.predict(X_test[:, col_idx])
predictions.append(pred)
predictions = np.array(predictions)
print(f"5棵树的预测形状: {predictions.shape}")
# 投票
from scipy.stats import mode
final_pred = mode(predictions, axis=0)[0].ravel()
vote_acc = accuracy_score(y_test, final_pred)
print(f"投票准确率: {vote_acc:.4f}")
# 5. 网格搜索调参
print("\n=== 网格搜索调参 ===")
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [5, 10, 15],
'max_features': ['sqrt', 'log2']
}
rf = RandomForestClassifier(random_state=42)
grid_search = GridSearchCV(rf, param_grid, cv=3, n_jobs=-1)
grid_search.fit(X_train, y_train)
print(f"最佳参数: {grid_search.best_params_}")
print(f"最佳准确率: {grid_search.best_score_:.4f}")
# 6. 特征重要性
print("\n=== 特征重要性 ===")
rf_best = grid_search.best_estimator_
importances = rf_best.feature_importances_
for i, imp in enumerate(importances):
print(f"特征{i}: {imp:.4f}")
# 7. 参数说明
print("\n=== 随机森林参数说明 ===")
print("n_estimators: 决策树数量(默认100,建议奇数)")
print("criterion: 'gini'(CART)或'entropy'(ID3)")
print("max_depth: 最大深度(None=尽可能生长)")
print("max_features: 每次抽样的特征数")
print(" - 'sqrt': 开平方(默认)")
print(" - 'log2': 求对数")
print(" - None: 全部特征")
print("bootstrap: 是否有放回抽样(默认True)")
print("min_samples_split: 节点分裂最小样本数")
print("min_samples_leaf: 叶子节点最小样本数")
输出示例:
训练集: (800, 16)
测试集: (200, 16)
特征数: 16
=== 随机森林基本使用 ===
准确率: 0.9250
树的数量: 100
使用的特征数: sqrt
=== 模拟行列抽样 ===
树1: 行=472个唯一, 列=[ 3 12 1 14]
树2: 行=471个唯一, 列=[ 5 0 14 9]
树3: 行=473个唯一, 列=[ 2 6 11 8]
=== 模拟投票决策 ===
5棵树的预测形状: (5, 200)
投票准确率: 0.8900
=== 网格搜索调参 ===
最佳参数: {'max_depth': 15, 'max_features': 'sqrt', 'n_estimators': 200}
最佳准确率: 0.9250
=== 特征重要性 ===
特征0: 0.0234
特征1: 0.0156
...
5 重难点与易错提醒
- ❗重点:随机森林属于Bagging思想。
- ❗重点:弱学习器是决策树(默认CART)。
- ❗重点:行列双重抽样(有放回)。
- ❗重点:平权投票,多数表决。
- ❗重点:n_estimators建议奇数。
- ⚠️易错:只对行抽样,忘记列抽样。
- ⚠️易错:max_features理解错误(是列不是行)。
- 💡深入理解:行列双重抽样保证多样性。
6 课堂问答精选
Q: 随机森林为什么要随机抽样?
A: 随机森林属于Bagging思想,多个弱学习器并行执行,默认都是CART树。如果训练集都一样,结果也一样,没有意义。有放回的随机抽样保证多个CART弱学习器训练集既有交集又有差异,结果更精准,更有说服力。
Q: max_features参数有什么作用?
A: max_features指定每次随机抽样多少个特征(列):
- 'sqrt'(默认):开平方,如16个特征→4个特征
- 'log2':求对数
- None:全部特征 通过列抽样保证列的角度也有交集和差异。
7 本课小结
- 随机森林:Bagging思想,决策树作为弱学习器。
- 构建方法:行列双重抽样,训练N棵树,平权投票。
- API:RandomForestClassifier。
- 重要参数:n_estimators、max_depth、max_features、bootstrap。
- n_estimators建议奇数,避免投票平局。
8 延伸思考与实践
- 实践:用随机森林完成分类任务。
- 预习:随机森林泰坦尼克号案例。
- 思考:为什么行列都要抽样?