Day 9 :随机森林调参与时间序列交叉验证

Day 9 :随机森林调参与时间序列交叉验证

📋 目录

  1. 随机森林核心超参数详解
  2. 超参数对模型的影响
  3. 调参策略与顺序
  4. 时间序列交叉验证
  5. 网格搜索与随机搜索
  6. 学习曲线与验证曲线
  7. 过拟合诊断

第一部分:随机森林核心超参数详解(1.5小时理论)

1.1 超参数分类

类别 参数 作用 影响
模型复杂度 n_estimators 树的数量 越多越稳定
树结构 max_depth 最大深度 控制过拟合
分裂条件 min_samples_split 分裂最小样本数 正则化
叶节点 min_samples_leaf 叶节点最小样本数 平滑预测
特征选择 max_features 特征子集大小 增加多样性
采样 bootstrap 是否自助采样 通常为True

1.2 n_estimators(树的数量)

定义:随机森林中决策树的数量。

原理

  • 每棵树独立训练,通过投票/平均得到最终结果
  • 根据大数定律,树越多,方差越小

数学性质
Error=Bias2+Variance+Noise \text{Error} = \text{Bias}^2 + \text{Variance} + \text{Noise} Error=Bias2+Variance+Noise

增加 n_estimators 主要降低方差,不影响偏差。

参数影响

n_estimators 训练时间 预测时间 内存 性能
太少(<50) 不稳定
适中(100-200) 中等 中等 中等 良好
太多(>500) 边际收益递减

选择策略

python 复制代码
# 观察OOB误差随n_estimators的变化
oob_scores = []
for n in range(10, 301, 10):
    rf = RandomForestClassifier(n_estimators=n, oob_score=True)
    rf.fit(X_train, y_train)
    oob_scores.append(rf.oob_score_)
# 选择OOB误差收敛时的n_estimators

1.3 max_depth(最大深度)

定义:每棵树的最大深度。

原理

  • 深度越大,树越复杂,越容易过拟合
  • 限制深度是最直接的正则化方法

参数影响

max_depth 模型复杂度 过拟合风险 训练时间
小(3-5)
中(10-20) 中等
大(None)

选择策略

python 复制代码
# 观察不同深度下的训练/测试误差
depths = range(3, 31, 3)
train_scores, test_scores = [], []
for depth in depths:
    rf = RandomForestClassifier(max_depth=depth)
    rf.fit(X_train, y_train)
    train_scores.append(rf.score(X_train, y_train))
    test_scores.append(rf.score(X_test, y_test))
# 选择测试误差最低的深度

1.4 min_samples_split(分裂最小样本数)

定义:内部节点再划分所需的最小样本数。

原理

  • 值越大,树越难分裂,模型越简单
  • 防止模型学习局部噪声

参数影响

min_samples_split 分裂次数 树大小 过拟合风险
小(2)
中(5-20)
大(>50)

选择策略

python 复制代码
# 从小值开始,逐步增加
min_samples_split_values = [2, 5, 10, 20, 50, 100]

1.5 min_samples_leaf(叶节点最小样本数)

定义:叶节点所需的最小样本数。

原理

  • 与min_samples_split类似,但作用于叶节点
  • 确保每个叶节点有足够样本,避免过拟合

与min_samples_split的区别

参数 作用节点 对树的影响
min_samples_split 内部节点 限制分裂
min_samples_leaf 叶节点 限制叶节点大小

1.6 max_features(特征子集大小)

定义:每次分裂时随机选择的特征数量。

原理

  • 增加树的多样性
  • 防止强特征主导所有树

常用设置

任务 推荐值 说明
分类 sqrt(n_features) 默认值
回归 n_features/3 默认值
特征少 None 使用所有特征
高维数据 log2(n_features) 增加随机性

第二部分:超参数对模型的影响

2.1 单个参数的影响分析

python 复制代码
# 参数影响矩阵
# 
#          欠拟合 ←──────────────→ 过拟合
# 
# n_estimators:   小 ←→ 大
# max_depth:      小 ←→ 大
# min_samples_split: 大 ←→ 小
# min_samples_leaf:  大 ←→ 小
# max_features:   小 ←→ 大

2.2 参数交互作用

参数组合 效果
max_depth小 + min_samples_split 强正则化,可能欠拟合
max_depth大 + min_samples_split 弱正则化,可能过拟合
n_estimators大 + max_depth 稳定且不易过拟合

2.3 偏差-方差权衡

text 复制代码
高偏差(欠拟合)          最优              高方差(过拟合)
     ←─────────────────────────────────────────→
     
增加 max_depth ────────────────────────────────→
增加 n_estimators ────────────────────────────→
增加 min_samples_split ←───────────────────────
增加 min_samples_leaf ←───────────────────────

第三部分:调参策略与顺序

3.1 推荐调参顺序

text 复制代码
第1步: 固定其他参数,确定 n_estimators
       └── 观察OOB误差收敛点

第2步: 调整 max_depth
       └── 找到最佳深度范围

第3步: 调整 min_samples_split 和 min_samples_leaf
       └── 进一步正则化

第4步: 调整 max_features
       └── 微调特征随机性

第5步: 联合调优
       └── 使用网格搜索/随机搜索

3.2 粗调 vs 精调

阶段 参数范围 步长 目的
粗调 大范围 找到最优区域
精调 小范围 找到最优值

3.3 调参注意事项

  1. 避免数据泄露:只用训练集调参
  2. 使用交叉验证:评估泛化能力
  3. 记录实验:保存每次调参结果
  4. 关注边际收益:性能提升不明显时停止

第四部分:时间序列交叉验证

4.1 为什么不能用普通K折?

普通K折的问题

  • 随机打乱数据,破坏时间顺序
  • 用未来数据训练,过去数据验证(前视偏差)
python 复制代码
# 错误做法 ❌
from sklearn.model_selection import KFold
kf = KFold(n_splits=5, shuffle=True)  # 随机打乱

4.2 TimeSeriesSplit原理

工作方式

text 复制代码
Fold 1: Train [0:100]  | Test [100:120]
Fold 2: Train [0:120]  | Test [120:140]
Fold 3: Train [0:140]  | Test [140:160]
Fold 4: Train [0:160]  | Test [160:180]
Fold 5: Train [0:180]  | Test [180:200]

特点

  • 训练集始终在测试集之前
  • 测试集不重叠
  • 训练集大小递增
python 复制代码
# 正确做法 ✅
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)

4.3 TimeSeriesSplit参数

参数 说明 默认值
n_splits 分割次数 5
max_train_size 最大训练集大小 None
test_size 测试集大小 None
gap 训练集和测试集之间的间隔 0

4.4 带间隔的TimeSeriesSplit

python 复制代码
# 防止数据泄露,设置间隔
tscv = TimeSeriesSplit(n_splits=5, gap=5)  # 训练集和测试集间隔5天

# 示意:
# Fold 1: Train [0:100]  | Gap [100:105] | Test [105:125]

4.5 时间序列交叉验证的变体

变体 描述 适用场景
滚动窗口 固定窗口大小 非平稳数据
扩展窗口 窗口不断增大 数据量小
带间隔 防止短期依赖 高频数据

第五部分:网格搜索与随机搜索

5.1 GridSearchCV(网格搜索)

原理:遍历所有参数组合,找到最佳参数。

python 复制代码
from sklearn.model_selection import GridSearchCV

param_grid = {
    'max_depth': [10, 20, 30],
    'min_samples_split': [2, 5, 10]
}
# 总共 3×3 = 9 种组合

grid_search = GridSearchCV(rf, param_grid, cv=5)
grid_search.fit(X_train, y_train)

优点 :保证找到最优组合
缺点:计算量大(组合爆炸)

5.2 RandomizedSearchCV(随机搜索)

原理:随机采样参数组合,而非遍历。

python 复制代码
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint

param_dist = {
    'max_depth': randint(10, 50),
    'min_samples_split': randint(2, 20)
}
# 可以指定n_iter控制搜索次数

random_search = RandomizedSearchCV(rf, param_dist, n_iter=20, cv=5)

优点 :效率高,适合大参数空间
缺点:可能错过最优组合

5.3 搜索策略选择

场景 推荐方法
参数少(<10种组合) GridSearchCV
参数多(>50种组合) RandomizedSearchCV
参数连续分布 RandomizedSearchCV
追求最优 GridSearchCV + 分步搜索

5.4 分步搜索策略

python 复制代码
# 第1步:粗调,大范围
param_grid1 = {'max_depth': [5, 10, 20, 30, None]}

# 第2步:精调,小范围
param_grid2 = {'max_depth': [10, 12, 14, 16, 18, 20]}

# 第3步:联合调优
param_grid3 = {
    'max_depth': [12, 14, 16],
    'min_samples_split': [2, 4, 6, 8]
}

第六部分:学习曲线与验证曲线

6.1 学习曲线(Learning Curve)

定义:展示模型性能随训练集大小的变化。

作用

  • 判断欠拟合/过拟合
  • 判断增加数据的收益

解读

text 复制代码
训练误差高,验证误差高 → 欠拟合
训练误差低,验证误差高 → 过拟合
两者都低且接近 → 良好拟合

6.2 验证曲线(Validation Curve)

定义:展示模型性能随单个参数的变化。

作用

  • 找到参数的最佳范围
  • 观察参数对过拟合的影响
python 复制代码
from sklearn.model_selection import validation_curve

train_scores, test_scores = validation_curve(
    rf, X_train, y_train,
    param_name='max_depth',
    param_range=[3, 5, 10, 15, 20, 30],
    cv=5
)

6.3 学习曲线 vs 验证曲线

曲线类型 X轴 目的
学习曲线 训练集大小 判断数据需求
验证曲线 参数值 判断参数影响

第七部分:过拟合诊断

7.1 诊断方法

方法 指标 过拟合信号
训练vs测试误差 准确率差 训练>>测试
OOB评分 OOB vs 测试 OOB低于测试
学习曲线 误差差距 差距随数据增大
验证曲线 参数影响 复杂度过高

7.2 过拟合应对策略

  1. 增加正则化
    • 减小max_depth
    • 增大min_samples_split
    • 增大min_samples_leaf
  2. 增加数据
    • 收集更多历史数据
    • 数据增强
  3. 减少特征
    • 特征选择
    • 降维(PCA)
  4. 降低模型复杂度
    • 减少n_estimators(边际效应)
    • 减小max_features
相关推荐
️是782 小时前
信息奥赛一本通—编程启蒙(3371:【例64.2】 生日相同)
开发语言·c++·算法
ZPC82102 小时前
ROS2 快过UDP的方法
python·算法·机器人
周末也要写八哥2 小时前
最长递增子序列典型应用题目详解
数据结构·算法
昆曲之源_娄江河畔2 小时前
婴儿版训练GPT
python·gpt·机器学习·大模型训练
不会写DN3 小时前
为什么map查找时间复杂度是O(1)?
算法·哈希算法·散列表
始三角龙3 小时前
LeetCode hoot 100 -- 找到字符串中的所有字母异位词
算法·leetcode·职场和发展
zhengyquan3 小时前
特斯拉无方向盘Cybercab落地,自动驾驶商业化再提速!
人工智能·机器学习·自动驾驶
abant23 小时前
leetcode 45 跳跃问题2 很难的贪心
算法·leetcode·职场和发展
小糯米6013 小时前
C语言指针3
c语言·数据结构·算法