简介:《XGBoost with Python》英文原版经典教材PDF,面向具备一定机器学习基础、希望系统掌握梯度提升与XGBoost建模的读者。全书从提升方法与AdaBoost讲起,过渡到梯度提升框架及XGBoost的并行化、效率优势,并结合scikit-learn接口演示糖尿病发病预测的回归任务,覆盖数据准备、标签编码与独热编码、缺失值处理、模型训练与预测、MSE/MAE等指标评估、树深度与学习率及正则化调参,还讲解特征重要性计算与可视化,帮助读者理解模型行为并处理较大规模数据。资源包共1个PDF文件,大小约1.19MB,轻量便于查阅。目前已有76人学习下载,适合作为XGBoost入门到实践的案头参考。
1. 这份 XGBoost with Python 英文 PDF:先跑通 5 个接口,再谈调参
很多人把这份 XGBoost with Python 英文原版 PDF 当成参数手册,翻到调参章节就抄 max_depth 和 learning_rate,结果第一个模型都跑不起来。它真正值钱的地方,是把梯度提升、scikit-learn 接口、数据准备、早停、特征重要性和调参实验串成了一条能逐章复现的路线,而不是给你一堆孤立公式。
如果你已经会用 scikit-learn 跑通一个二分类模型,但面对 XGBoost 的 n_estimators、subsample、colsample_bytree 不知道先动哪个,这份教材适合当案头手册。它不要求你先啃完泰勒展开,而是让你先把模型跑起来,再从日志和验证曲线里反推参数该往哪边走。
新手按“第一个模型、数据准备、评估、早停、特征重要性”的顺序敲一遍,熟手直接翻避坑和调参实验设计,各取所需。下面按这份 PDF 的目录骨架,把它拆成能直接抄作业的代码和参数判断。
2. 从 AdaBoost 到 XGBoost:把梯度提升拆成可执行的 4 个对象
2.1 为什么先理解残差,再碰参数
梯度提升的起点不是 XGBoost,而是 AdaBoost 那一类提升思想:用多个弱学习器串起来,后一个模型盯着前一个模型没做好的地方。AdaBoost 通过调整样本权重让错分样本更受关注,梯度提升则把这件事推广成“拟合负梯度”,在平方损失下就是拟合残差。你不需要手推公式,但要知道每一轮新树都在修正上一轮的预测误差,所以树的数量、每棵树的大小和学习率才会互相牵制。
XGBoost 在这个骨架上做了两件对工程落地很关键的事:一是用二阶导信息近似目标函数,二是把正则项写进目标里,让树的叶子数和叶子权重受到约束。落到参数上,max_depth、min_child_weight、gamma、reg_alpha、reg_lambda都不是玄学开关,而是在控制“每棵树能长多复杂”和“叶子权重能有多大”。先理解这层关系,后面调参才不会把n_estimators和learning_rate拆开乱试。
选 XGBoost 而不是单棵决策树,理由也很直接:单棵树容易过拟合,随机森林靠 bagging 降方差,梯度提升靠逐步修正偏差,XGBoost 又把并行分裂查找、缺失值默认分支、早停和特征重要性做进了同一个接口。对表格数据来说,这套组合在中小规模数据上通常比单棵树稳,比深度学习省事。教材把梯度提升放在 XGBoost 之前讲,就是为了让你知道后面那些参数不是凭空冒出来的。
2.2 用 scikit-learn 接口搭第一个二分类模型
教材用糖尿病发病预测数据做第一个示例,这里用一份同结构的 CSV 复现:前若干列是特征,最后一列是 0/1 标签。先不要调参,先把数据切分、模型定义、训练、预测概率和 AUC 这条链路跑通。代码里保留了教材强调的几个基础参数,但把随机种子和线程数显式写出来,方便你复现和排查。
import pandas as pd from sklearn.model_selection import train_test_split from sklearn.metrics import roc_auc_score from xgboost import XGBClassifier # 读取数据:最后一列是 0/1 标签,前面是特征 data = pd.read_csv("data/diabetes.csv") X = data.iloc[:, :-1] y = data.iloc[:, -1] # 分层切分,保证训练集和测试集的正负样本比例接近 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # 先给一组保守参数,不要一上来就网格搜索 model = XGBClassifier( n_estimators=200, max_depth=3, learning_rate=0.05, subsample=0.9, colsample_bytree=0.8, objective="binary:logistic", eval_metric="auc", n_jobs=4, random_state=42, ) model.fit(X_train, y_train) # 输出正类概率,再用 AUC 看排序能力 proba = model.predict_proba(X_test)[:, 1] print("AUC:", roc_auc_score(y_test, proba))这段代码把教材第 4 章的第一个模型压缩成了一条可执行链路:读取 CSV、分层切分、定义XGBClassifier、训练、输出概率、算 AUC。分层切分是为了避免小数据集里正负样本比例在训练集和测试集之间偏得太厉害;predict_proba取第二列是因为二分类输出两列,第二列才是正类概率。AUC 不依赖具体阈值,适合先看模型排序能力,再决定阈值怎么定。
参数上,n_estimators=200表示最多建 200 棵树,max_depth=3把每棵树限制在较浅的深度,learning_rate=0.05让每棵树只贡献一小步,三者一起构成“多棵小树慢慢修”的组合。subsample=0.9和colsample_bytree=0.8是行采样与列采样,作用是给模型加一点随机性,降低过拟合;objective="binary:logistic"指定二分类逻辑回归目标,eval_metric="auc"让训练日志按 AUC 记录。n_jobs=4控制线程数,第一次跑不建议直接设成-1,先确认机器内存和线程行为。
提示:第一次跑通前不要把
n_jobs设成-1,先设 2 或 4,确认内存和线程行为。
2.3 训练日志里该盯哪几个数
模型跑通只是第一步,教材在第 10 章前后反复强调监控训练过程,原因是 XGBoost 的验证集曲线能告诉你模型是在继续学还是在过拟合。训练时传入eval_set,把训练集和验证集都放进去,再看evals_result_里每一轮的 AUC。不要只看最后一行,要看曲线有没有在验证集上提前走平甚至往下掉。
# 重新训练一次,记录训练集和验证集的 AUC model.fit( X_train, y_train, eval_set=[(X_train, "train"), (X_test, "valid")], verbose=False, ) results = model.evals_result_ train_auc = results["validation_0"]["auc"] valid_auc = results["validation_1"]["auc"] print("last train auc:", train_auc[-1]) print("last valid auc:", valid_auc[-1])这段代码把训练集和验证集的 AUC 分别存到validation_0和validation_1,顺序取决于eval_set里传入的顺序。verbose=False是为了不让每一轮日志刷屏,真正要看的时候再打开。训练集 AUC 和验证集 AUC 差距小,说明模型还没明显过拟合;训练集继续涨、验证集走平或下降,就该考虑早停、减小树深或提高正则化。
参数说明里要特别注意eval_set的顺序:第一个元素对应validation_0,第二个对应validation_1,写反了曲线解释就会颠倒。evals_result_里的指标名来自eval_metric,如果改成logloss,这里就要取logloss。教材在这一块给的是排查思路,不是固定答案:先看曲线形态,再决定动max_depth还是动n_estimators,不要一上来就同时改五个参数。
3. 数据准备与评估:标签编码、One-Hot、缺失值和交叉验证的落地顺序
3.1 类别特征的三条处理路线与选择依据
教材第 5 章把数据准备单独拎出来,是因为 XGBoost 虽然能处理不少脏数据,但类别特征处理错了,后面的调参全是白费。常见路线有三条:标签编码把字符串类别映射成整数,One-Hot 把类别展开成 0/1 列,或者用支持类别特征的接口直接传类别列。标签编码适合有序类别,比如“低、中、高”;无序类别用标签编码会让树模型误以为 0 和 2 之间有大小关系,切分点会变得没意义。
One-Hot 适合类别数不多的无序特征,缺点是维度会膨胀,类别一多就容易稀疏。教材在标签编码和 One-Hot 之间做了对比,落到工程上,我一般先看类别基数:十几个类别以内用 One-Hot 省心,几百个类别以上先考虑目标编码或让模型原生处理类别,不要硬展开。下面这段代码用ColumnTransformer把类别列和数值列分开处理,避免手工拼列时把列顺序弄乱。
from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.preprocessing import OneHotEncoder from xgboost import XGBClassifier # 示例列名,按你的 CSV 实际列名替换 cat_cols = ["color", "region"] num_cols = [c for c in X.columns if c not in cat_cols] preprocess = ColumnTransformer( transformers=[ ("cat", OneHotEncoder(handle_unknown="ignore"), cat_cols), ("num", "passthrough", num_cols), ] ) pipe = Pipeline([ ("prep", preprocess), ("model", XGBClassifier( n_estimators=300, max_depth=3, learning_rate=0.05, eval_metric="auc", random_state=42, )), ]) pipe.fit(X_train, y_train)这段代码把 One-Hot 和模型装进同一个 Pipeline,训练时先做列变换再进 XGBoost,预测时同一套变换会自动复用。handle_unknown="ignore"是为了让测试集出现训练集没见过的类别时不报错,而是整列置 0;num_cols用passthrough原样传给模型,避免数值列被意外标准化或丢弃。Pipeline 的好处是交叉验证和线上推理不会出现“训练时做了 One-Hot、预测时忘了做”的翻车。
参数上,OneHotEncoder的handle_unknown建议设成ignore,尤其是类别会随时间变化的业务数据;cat_cols和num_cols一定要根据实际列名生成,不要手写死列表。XGBClassifier这边先保持浅树和小学习率,把数据准备跑通再谈调参。教材里讲标签编码和 One-Hot 的章节并不长,但这两步在真实项目里最容易埋雷,后面模型效果差,很多时候不是 XGBoost 不行,而是类别列被当成了连续数值。
3.2 缺失值不是先填再训,而是先让 XGBoost 自己走默认分支
教材第 5 章专门讲了缺失值支持,这一点和很多人的直觉相反:XGBoost 在分裂时会把缺失值分到一个默认分支,训练时学这个方向怎么走,所以不要无脑fillna(0)。填 0 会把“缺失”变成“真实观测到的 0”,如果 0 在业务上有意义,模型就会把缺失样本和真实 0 样本混在一起。正确做法是先确认缺失标记,比如 CSV 里用?或空字符串表示缺失,先替换成NaN,再让 XGBoost 自己处理。
import numpy as np # 有些 CSV 用 ? 表示缺失,先统一成 NaN X_train = X_train.replace("?", np.nan) X_test = X_test.replace("?", np.nan) model = XGBClassifier( n_estimators=300, max_depth=4, learning_rate=0.05, missing=np.nan, eval_metric="auc", random_state=42, ) model.fit(X_train, y_train)这段代码先把?替换成NaN,再在模型里显式写missing=np.nan。missing参数告诉 XGBoost 哪个值算缺失,默认就是NaN,但显式写出来能避免数据里用了其他标记时被当成普通数值。替换操作要在训练集和测试集上分别做,不能只处理训练集,否则预测时缺失标记还会以字符串形式混进去。
参数说明里,missing不是“填缺失值”的参数,而是“识别缺失值”的参数;真正的填充策略要看你是否想保留缺失信息。如果业务上缺失本身有含义,比如“未检测”不等于“检测结果为 0”,就保留NaN。如果数据源用-1表示缺失,要么先替换成NaN,要么把missing=-1传给模型,但不要既填-1又设missing=np.nan,那样模型会把-1当成真实数值。
3.3 用分层 K 折把评估做扎实
教材第 6 章讲了训练集/测试集评估和 k 折交叉验证两种方式,实际项目里小数据集更依赖交叉验证。分层 K 折能保证每一折的正负样本比例接近整体分布,比普通 K 折更适合分类任务。这里要注意并行层级:外层交叉验证如果设n_jobs=-1,模型内部又设n_jobs=-1,线程会互相抢资源,内存小的机器容易卡死。稳妥做法是外层n_jobs=1,让模型内部并行,或者反过来只开一层。
from sklearn.model_selection import StratifiedKFold, cross_val_score cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score( model, X, y, cv=cv, scoring="roc_auc", n_jobs=1, ) print("fold auc:", scores) print("mean auc:", scores.mean(), "std:", scores.std())这段代码用 5 折分层交叉验证算 AUC,shuffle=True和random_state=42保证折分可复现。cross_val_score返回每一折的分数,均值和标准差一起看:均值高但标准差大,说明模型对数据划分敏感,可能是样本太少或某些折里类别比例特殊。n_jobs=1是刻意限制外层并行,避免和模型内部的线程叠加。
参数上,n_splits=5是常用起点,数据量小可以到 10,数据量大可以降到 3 或 5 控制时间。scoring="roc_auc"适合二分类排序评估,如果是类别很不平衡的业务,可以再加average_precision一起看。交叉验证不是为了刷一个更好看的分数,而是为了看模型在不同划分下稳不稳;如果某几折 AUC 明显掉队,先回去查数据准备和缺失值处理,而不是继续调max_depth。
4. 训练控制与模型持久化:早停、学习曲线、单树可视化和 pickle/joblib
4.1 早停到底停在哪一轮
教材第 10 章讲早停,核心是别让模型在验证集已经变差之后还继续建树。做法是把n_estimators设得足够大,再给一个early_stopping_rounds,验证集指标连续多少轮不提升就停。这里有一个版本差异坑:旧接口把early_stopping_rounds放在fit()里,新接口放在XGBClassifier构造函数里,写错位置会直接报TypeError。下面用构造函数写法,训练时只传eval_set。
model = XGBClassifier( n_estimators=1000, max_depth=4, learning_rate=0.05, objective="binary:logistic", eval_metric="auc", early_stopping_rounds=50, random_state=42, ) model.fit( X_train, y_train, eval_set=[(X_test, "valid")], verbose=False, ) print("best iteration:", model.best_iteration) print("best score:", model.best_score)这段代码把最大树数设到 1000,但靠早停决定实际用多少棵。early_stopping_rounds=50表示验证集 AUC 连续 50 轮没有提升就停,best_iteration是停止后表现最好的那一轮。注意eval_set里只放了验证集,所以日志键是validation_0;如果同时放训练集和验证集,键会变成validation_0和validation_1,别取错。
参数上,early_stopping_rounds不要设得太小,太小会因为验证集波动提前停;也不要设得太大,太大就失去早停意义。数据量小、验证集噪声大时,可以适当放大到 50 到 100;数据量大、每轮训练慢时,可以缩小到 20 到 30。n_estimators设成 1000 只是上限,不代表一定建 1000 棵树,早停会截断。教材在这一块强调“监控训练性能”,本质就是让你用验证集曲线决定什么时候收手。
注意:
early_stopping_rounds在不同接口里的位置不一样,写错会直接报TypeError。
4.2 学习曲线怎么看欠拟合与过拟合
学习曲线不是画着好看,而是用来判断下一步该加模型容量还是加正则。训练集和验证集 AUC 都低,说明欠拟合,优先加深树或增加树数;训练集高、验证集低且差距拉大,说明过拟合,优先减小max_depth、提高min_child_weight、加大reg_lambda或降低学习率。下面这段代码把早停模型的验证集曲线画出来,横轴是 boosting round,纵轴是 AUC。
import matplotlib.pyplot as plt results = model.evals_result_ valid_auc = results["validation_0"]["auc"] plt.plot(valid_auc, label="valid auc") plt.xlabel("boosting round") plt.ylabel("auc") plt.legend() plt.show()这段代码从evals_result_里取验证集 AUC,逐轮画线。如果曲线在前几十轮快速上升,后面走平,说明模型已经学得差不多;如果曲线在中后段开始抖动或下降,说明继续建树只会加重过拟合。validation_0对应eval_set里第一个数据集,这里只传了验证集,所以取这个键。要看训练集曲线,就在eval_set里再加一组训练数据,然后分别取validation_0和validation_1。
参数上,横轴不是树的数量本身,而是 boosting round,早停后实际模型只保留到best_iteration附近。纵轴指标取决于eval_metric,换成logloss时曲线方向会反过来,越低越好。学习曲线要和早停一起看:早停告诉你停在哪,学习曲线告诉你为什么停。教材在第 10 章把这两件事放在一起讲,就是为了避免只看一个数字就下结论。
4.3 单棵树可视化与模型保存加载
教材第 7 章讲单棵树可视化,目的是让你看清模型到底在哪些特征上切分、切分点大概在哪。单棵树不是整个模型,但能帮你判断特征缩放、类别编码和缺失值处理有没有出大问题。可视化依赖 graphviz,没有装的话先补上系统依赖和 Python 包,否则plot_tree会报错。下面代码画第一棵树,num_trees=0表示第一棵,rankdir="LR"让树从左到右排,读起来比上下排舒服。
from xgboost import plot_tree import matplotlib.pyplot as plt plot_tree(model, num_trees=0, rankdir="LR") plt.show()这段代码只画单棵树,num_trees=0是索引,想画第二棵就改成 1。rankdir="LR"控制布局方向,树深较大时从左到右更省屏幕空间。单棵树可视化适合排查“模型是不是在某个明显不该切的特征上切了”,但不适合拿来解释整个集成模型;整体解释要看特征重要性,而不是盯着一棵树。
模型持久化在教材第 8 章讲了 pickle 和 joblib 两种方式。pickle 是 Python 标准库,joblib 对 NumPy 数组更友好,两种都能用。工程上我更倾向 XGBoost 自带的save_model/load_model,因为它保存的是模型结构而不是 Python 对象,换环境加载时兼容性更好。下面两段代码分别演示 joblib 和原生保存,按团队规范选一种,不要混着用。
import joblib joblib.dump(model, "model.joblib") loaded = joblib.load("model.joblib") print(loaded.predict_proba(X_test)[:5])model.save_model("model.json") from xgboost import XGBClassifier loaded = XGBClassifier() loaded.load_model("model.json") print(loaded.predict_proba(X_test)[:5])第一段用 joblib 把整个模型对象序列化,适合本地快速保存和加载;第二段用 XGBoost 原生格式保存模型结构和参数,跨语言、跨环境加载更稳。参数上,model.joblib和model.json都是文件名,路径要提前存在;load_model之后不需要重新fit,但预测前的特征列顺序必须和训练时一致。教材讲 pickle 和 joblib 时强调“保存训练好的模型”,真正落地时还要记录特征列顺序、类别编码映射和缺失值标记,否则模型加载成功但预测结果对不上。
5. 避坑:XGBoost 调参和工程化里最容易翻车的 5 个点
5.1 数据与接口层的 3 个坑
第一个坑是类别编码把无序类别当成了有序数值。现象是模型训练能跑,但特征重要性里某个类别列排得异常高,验证集效果却一般。原因在于标签编码把“红、绿、蓝”映射成 0、1、2,树模型会在 0 和 2 之间找切分点,强行制造了不存在的顺序。解决方式是看类别是否有序:有序类别可以用标签编码,无序类别用 One-Hot 或模型原生类别支持,别为了省内存把无序类别压成整数。
第二个坑是缺失值先填 0 再训练。现象是 AUC 比保留NaN时低一截,尤其是缺失比例高的列。原因是 XGBoost 本来会把缺失值分到默认分支,填 0 之后缺失样本和真实 0 样本混在一起,模型学到的切分方向被污染。解决方式是先把?、空字符串等缺失标记统一成NaN,再让模型自己处理;如果业务上缺失有含义,就保留缺失标记,不要为了“看起来干净”把缺失信息抹掉。
第三个坑是训练集和测试集的特征列顺序不一致。现象是离线评估正常,上线后预测概率离谱,甚至报列名不匹配。原因是手工拼特征时训练用了一列顺序,预测用了另一列顺序,树模型按列索引切分,顺序错了整个模型就错位。解决方式是用 Pipeline 把预处理和模型绑在一起,或者保存训练时的feature_names,预测前按同一顺序重排。下面这个检查很土但有效,训练前和预测前各跑一次。
# 训练前记录特征列顺序 feature_names = list(X_train.columns) print(feature_names) # 预测前按训练顺序重排,缺列直接报错 X_test = X_test[feature_names]这段代码把训练列顺序存下来,预测前用列表索引重排。X_test[feature_names]如果遇到训练集有、测试集没有的列会直接抛KeyError,比静默错位好。参数上,feature_names要保存到模型配置文件或 Pipeline 里,不要只留在 notebook 变量里。教材在数据准备章节讲列处理,落地时这一步比调参更值得花时间。
| 现象 | 原因 | 解决 |
|---|---|---|
| 类别列重要性异常高,验证集效果差 | 无序类别被标签编码成有序整数 | 无序类别改 One-Hot 或原生类别支持 |
| 填 0 后 AUC 下降 | 缺失信息被当成真实 0 | 保留NaN,用missing识别缺失 |
| 上线后预测概率离谱 | 训练和预测特征列顺序不一致 | 保存feature_names,预测前重排 |
5.2 训练与调参层的 2 个坑
第四个坑是early_stopping_rounds放错位置。现象是训练直接报TypeError: fit() got an unexpected keyword argument 'early_stopping_rounds',或者早停完全不生效。原因是不同接口版本对早停参数的位置要求不一样,旧写法放在fit(),新写法放在构造函数。解决方式是先看当前接口签名,统一用构造函数写法,训练时只传eval_set;如果团队里有人用旧接口,就在代码里写清楚注释,别让两种写法混在一个文件里。
第五个坑是并行层级叠加导致内存爆掉或训练变慢。现象是交叉验证时机器风扇狂转、内存直线上升,甚至进程被系统杀掉。原因是外层cross_val_score(n_jobs=-1)和模型内部n_jobs=-1同时开满线程,每个折都在抢资源。解决方式是只开一层并行:外层交叉验证设n_jobs=1,让模型内部并行;或者外层并行、模型内部设n_jobs=1。数据量大时还要看dmatrix的内存占用,别把subsample当成省内存开关。
# 只开一层并行:外层交叉验证串行,模型内部并行 model = XGBClassifier( n_estimators=500, max_depth=4, learning_rate=0.05, n_jobs=4, eval_metric="auc", random_state=42, ) scores = cross_val_score( model, X, y, cv=StratifiedKFold(n_splits=5, shuffle=True, random_state=42), scoring="roc_auc", n_jobs=1, )这段代码把模型内部n_jobs设成 4,外层交叉验证n_jobs=1,避免线程叠加。参数上,n_jobs=4不是越大越好,要看 CPU 核数和内存带宽;如果每折数据量很大,模型内部并行也会占内存,可以降到 2。教材讲多线程支持时强调“并行能加速”,但工程上要先保证不把机器跑崩,再谈加速。调参也是一样,先固定并行策略,再动学习率和树深,否则实验结果会被资源竞争搅乱。
6. 特征重要性与调参顺序:把“玄学调参”变成可验证的实验
6.1 特征重要性怎么用才不误导
教材第 9 章讲特征重要性,常见的三种口径是weight、gain和cover。weight统计特征被用来分裂的次数,gain统计分裂带来的平均增益,cover统计被分裂覆盖的样本数。默认口径在不同接口里可能不同,做特征筛选时我一般显式指定importance_type="gain",因为它更接近“这个特征对目标函数的贡献”,而不是“被用了多少次”。下面代码输出按 gain 排序的前 15 个特征。
import pandas as pd model = XGBClassifier( n_estimators=500, max_depth=4, learning_rate=0.05, importance_type="gain", eval_metric="auc", random_state=42, ) model.fit(X_train, y_train) rank = pd.Series(model.feature_importances_, index=X_train.columns) print(rank.sort_values(ascending=False).head(15))这段代码把importance_type固定为gain,再用 Pandas Series 对齐列名排序。feature_importances_的长度和训练列数一致,索引必须是训练列名,否则排序结果对不上。参数上,gain适合看贡献,weight适合看使用频率,cover适合看影响样本范围;三个口径结论不一致时,不要只拿一个口径做删列决策。
特征重要性只能当线索,不能当因果结论。两个高度相关的特征,模型可能只选其中一个分裂,另一个重要性被压低,但你删掉被压低那个未必安全。稳妥做法是先用 gain 排序,再对高相关特征做聚类或分组,按组做前向选择,每删一批就跑一次交叉验证。教材在这一块给的是可视化方法,落地时要加一道验证,不然容易把有用特征误删。
6.2 调参顺序:先树数量与深度,再学习率,再采样
调参不要一上来就网格搜索全部参数,先按影响面排序。教材第 14 到 16 章的顺序很实用:先定树的数量和大小,再定学习率和树数的组合,最后看行采样和列采样。树深和树数决定模型容量,学习率决定每棵树的贡献步长,采样决定随机性和泛化。下面这张表是我常用的起步顺序,不是固定答案,但能避免在无关参数上浪费实验。
| 顺序 | 参数 | 起步值 | 观察点 |
|---|---|---|---|
| 1 | max_depth | 3 到 6 | 验证集曲线是否过早过拟合 |
| 2 | n_estimators | 200 到 1000 | 配合早停看best_iteration |
| 3 | learning_rate | 0.05 到 0.1 | 小学习率通常要更多树 |
| 4 | subsample | 0.7 到 1.0 | 行采样降方差,太小会欠拟合 |
| 5 | colsample_bytree | 0.6 到 1.0 | 列采样降相关,太小会丢信息 |
我自己的血泪经验是:先把max_depth和n_estimators用早停跑出一个稳定区间,再动learning_rate,最后才碰subsample和colsample_bytree。每次只改一组参数,固定随机种子,记录验证集均值和标准差;如果两组参数分数差不多,选更简单的那组。从那以后我每次开新数据集都强制先跑一遍“浅树 + 早停 + 交叉验证”的基线,再谈调参,不然很容易在玄学搜索里绕半天。希望这份拆解帮到你。
本文还有配套的精品资源,点击获取