3.2 学习任务参数与评估指标选择 objective 决定 g 与 h 怎么算(训练往哪优化),evalmetric 决定谁说了算(模型怎么比高低),两者可以不同但必须配套。 本节整理常见任务的目标与指标搭配,用不均衡数据的实验演示"指标错配"的后果,并给出自定义损失的最小样例。 上一节把树形参数讲完。还剩一组"方向性"参数:它们不改变树的形状逻辑,却决定整个训练朝哪个方向收敛、以及最终选哪一轮的模型——选错方向,树长得再好也是南辕北辙。 一、objective:g 与 h 的生产车间 第 2.2 节说过,objective 指定损失函数,损失函数决定每个样本的 g 与 h。
objective 决定 g 与 h 怎么算(训练往哪优化),eval_metric 决定谁说了算(模型怎么比高低),两者可以不同但必须配套。 本节整理常见任务的目标与指标搭配,用不均衡数据的实验演示"指标错配"的后果,并给出自定义损失的最小样例。
上一节把树形参数讲完。还剩一组"方向性"参数:它们不改变树的形状逻辑,却决定整个训练朝哪个方向收敛、以及最终选哪一轮的模型——选错方向,树长得再好也是南辕北辙。
第 2.2 节说过,objective 指定损失函数,损失函数决定每个样本的 g 与 h。常用搭配按任务划分:
| 任务 | objective | 损失本质 | 输出形态 |
|---|---|---|---|
| 回归 | reg:squarederror | 平方损失 | 连续值 |
| 回归抗噪 | reg:absoluteerror / huber | 绝对值或混合 | 连续值 |
| 二分类 | binary:logistic | 对数损失 | 概率 |
| 二分类输出 logit | binary:logitraw | 对数损失 | 未过 sigmoid 的值 |
| 多分类 | multi:softprob | 多类对数损失 | 各类概率 |
| 排序 | rank:pairwise 等 | 成对损失 | 分数 |
工程上最重要的纪律:objective 与输出解读必须一致。用 binary:logistic 训练后输出是概率,直接当"分数"排序没问题,但拿去做阈值 0.5 的硬分类前,最好先看概率分布——不均衡数据下 0.5 常常不是好阈值(本节第三部分实测)。
eval_metric 只影响"评估与早停",不改变训练方向,这给了组合空间:训练用 logloss(可导、光滑,适合优化),选型看 AUC(不受阈值影响、不均衡友好)。看一个错配实验——极度不均衡的人造数据上,同一模型在不同指标下的表现:
import numpy as np from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score, roc_auc_score, average_precision_score import xgboost as xgb # 造 3% 正例的不均衡数据 X, y = make_classification(n_samples=6000, weights=[0.97, 0.03], flip_y=0.02, random_state=1) Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, random_state=1) m = xgb.XGBClassifier(n_estimators=150, max_depth=4, eval_metric='logloss') m.fit(Xtr, ytr) proba = m.predict_proba(Xte)[:, 1] print(f"正例占比: {yte.mean():.3f}") print(f"准确率 accuracy : {accuracy_score(yte, proba > 0.5):.4f}") print(f"AUC : {roc_auc_score(yte, proba):.4f}") print(f"PR-AUC : {average_precision_score(yte, proba):.4f}") # 运行输出: # 正例占比: 0.031 # 准确率 accuracy : 0.9732 # AUC : 0.9318 # PR-AUC : 0.6407
准确率 0.973 看着漂亮,但全预测负例也有 0.969——准确率在这一刻几乎不携带模型信息;AUC 才暴露真实排序能力。指标错配不是学术洁癖,是会让团队在错误方向上自信满满的那种错误。早停也依赖 eval_metric:拿 accuracy 做早停,模型会在最粗暴的平台上停下,拿 logloss 或 auc 则能继续压榨。
XGBoost 允许用自定义目标函数替换内置 objective,只需给出梯度与二阶导数。一个实用例子——对高估惩罚两倍的非对称平方损失(比如库存场景:备多了浪费、断货更亏,两个方向代价不同):
import numpy as np import xgboost as xgb def asymmetric_squared(yhat, dtrain): y = dtrain.get_label() k = 2.0 # 高估方向的惩罚倍数 diff = yhat - y grad = np.where(diff > 0, k * diff, diff) # 一阶导 hess = np.where(diff > 0, k, 1.0) # 二阶导(该损失逐段为常数) return grad, hess rng = np.random.default_rng(3) X = rng.normal(size=(500, 4)) y = X[:, 0] * 1.5 + rng.normal(0, 0.3, 500) dtrain = xgb.DMatrix(X, label=y) booster = xgb.train({'tree_method': 'hist'}, dtrain, num_boost_round=80, obj=asymmetric_squared) pred = booster.predict(xgb.DMatrix(X)) over = np.mean(np.maximum(pred - y, 0)) under = np.mean(np.maximum(y - pred, 0)) print(f"平均高估 {over:.4f} 平均低估 {under:.4f}") # 运行输出: # 平均高估 0.1181 平均低估 0.3184
高估方向导数乘了 2,模型宁可多低估也不冒进——输出里低估量明显大于高估量,惩罚的方向性被忠实执行。注意二阶导数虽然这里逐段取常数(形式简单),但必须提供且量级要合理,否则第 2.2 节说的"曲率定价"会失真,训练变得又慢又差。
💡 关键直觉:选指标就选"和业务决策同构"的那个。阈值决策看精确率召回率,排序系统看 AUC 或 NDCG,回归报价看业务对称性。指标是模型与业务之间唯一的翻译官。
参数的含义与搭配都已明确,下一节把它们放进真实搜索流程:网格、随机、贝叶斯怎么选,调参顺序怎么排。