3.1 通用参数与Booster参数全解 通用参数管"用哪个引擎、几线程",Booster 参数管"树长什么样、正则收多紧"。 本节按公式出处逐个讲透树形与正则参数,用可控实验展示 gamma 与 maxdepth 的边际效应,最后给出一份参数速查表。 第 2 章的公式是地图,本章参数是方向盘。本节聚焦两个知识点:Booster 参数的公式身份、复杂度三闸门(gamma、lambda、minchildweight)的分工。 一、先分清两大类 通用参数数量少:booster 选引擎(gbtree、gblinear 或 dart——树、线性、 dropout 树三种),nthread 给线程数,device 选算力设备。绝大多数表格任务 booster 用默认的 gbtree 即可;
通用参数管"用哪个引擎、几线程",Booster 参数管"树长什么样、正则收多紧"。 本节按公式出处逐个讲透树形与正则参数,用可控实验展示 gamma 与 max_depth 的边际效应,最后给出一份参数速查表。
第 2 章的公式是地图,本章参数是方向盘。本节聚焦两个知识点:Booster 参数的公式身份、复杂度三闸门(gamma、lambda、min_child_weight)的分工。
通用参数数量少:booster 选引擎(gbtree、gblinear 或 dart——树、线性、 dropout 树三种),nthread 给线程数,device 选算力设备。绝大多数表格任务 booster 用默认的 gbtree 即可;dart 在小数据上偶有奇效但训练慢且不可用早停。真正的重头是 Booster 参数,每一个都能在公式里找到座位。
| 参数 | 公式身份 | 调大效果 | 典型范围 |
|---|---|---|---|
| eta | 每轮修正量乘的收缩系数 | 步子更小更保守 | 0.01–0.3 |
| gamma | 增益公式里减去的叶子税 | 分裂更难,树更简 | 0–5 |
| reg_lambda | 叶权分母里的 λ | 权重收缩更强 | 0.1–10 |
| reg_alpha | 叶权分子分母的 L1 项 | 稀疏化叶权 | 0–1 |
| max_depth | 树形搜索的深度上限 | 容量更大易过拟 | 3–10 |
| min_child_weight | 叶子 H 和的下限 | 小叶被禁,抗噪 | 1–10 |
| subsample | 每轮用多少比例样本 | 降到 1 以下增随机 | 0.5–1 |
| colsample_bytree | 每棵树用多少比例特征 | 降方差、提速度 | 0.5–1 |
eta(即 learning_rate)给每棵树的输出统一打折。第 1.2 节手工提升循环里 lr=0.3 的"只采纳三成修正"就是它。步长与步数是互补关系:eta 小则需要的树更多,但每步误差更可控,泛化常更好。实践中先用 0.1 左右把树形调好,最终上生产前把 eta 降到 0.02–0.05、树数放大三五倍,是稳定的最后一笔。配合早停(第 4 章详解),树数甚至不用手调。
gamma、reg_lambda、min_child_weight 都在压复杂度,但作用位置不同:gamma 在分裂瞬间收税(能不能分),lambda 在权重解上收缩(分了之后输出多大),min_child_weight 在叶子准入上设限(分出来的叶子够不够格)。用加州房价数据观察 gamma 的边际效应:
import numpy as np from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split import xgboost as xgb from sklearn.metrics import mean_squared_error housing = fetch_california_housing() Xtr, Xte, ytr, yte = train_test_split(housing.data, housing.target, test_size=0.2, random_state=0) for gamma in (0, 0.5, 2.0): m = xgb.XGBRegressor(n_estimators=200, max_depth=6, learning_rate=0.1, gamma=gamma, random_state=0) m.fit(Xtr, ytr) rmse = mean_squared_error(yte, m.predict(Xte)) ** 0.5 n_leaves = np.mean([t.count('leaf') for t in m.get_booster().get_dump()]) print(f"gamma={gamma:<4} 测试RMSE={rmse:.4f} 平均叶子数={n_leaves:.0f}") # 运行输出: # gamma=0 测试RMSE=0.4921 平均叶子数=58 # gamma=0.5 测试RMSE=0.4874 平均叶子数=44 # gamma=2.0 测试RMSE=0.4998 平均叶子数=31
gamma 从 0 到 0.5,平均叶子数从 58 降到 44,RMSE 反而略降——原来的部分分裂只在对付噪声;gamma 升到 2.0,剪过头,欠拟合出现。边际效应呈倒 U 形,中间有一段"剪掉的都是噪声"的免费午餐。

subsample 与 colsample_bytree 从随机森林借来:前者每轮只用部分样本,后者每棵树只用部分特征。都设 0.7–0.9 是常见起手式——既降方差又提速。注意它们与 gamma 类参数的联合效应是相乘的:同时收紧所有闸门容易直接把树憋死,症状是训练误差都压不下去。
⚠️ 常见坑:一上来就同时调十个参数。参数之间有交互,多维空间里网格搜索既贵又看不出因果。正确姿势是第 3.3 节的分层推进:一次只动一组,观察一组。
树形参数就位后,还差两个"方向性"设置:损失函数选谁、模型好坏由谁评判。下一节讲学习任务参数。
最后补一个排查小工具——当训练行为反常时,先把生效参数打印出来核对,很多"玄学问题"其实是参数没传进去:
# 打印实际生效的参数(排查参数未生效的利器) clf0 = xgb.XGBClassifier(max_depth=6) # 其余参数保持默认 config = clf0.get_params() for k in ('max_depth', 'min_child_weight', 'subsample', 'reg_lambda'): print(f"{k:18s} = {config.get(k)}") # 运行输出: # max_depth = 6 # min_child_weight = 1 # subsample = 1.0 # reg_lambda = 1.0
sklearn 风格接口里没写的参数全是默认值,这份清单能立刻暴露"以为调了其实没调"的低级错误。