You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带权重与base_margin的XGBRegressor如何实现样本外验证?

带样本权重与base_margin的XGBoost交叉验证/样本外验证实现方案

首先纠正两个常见认知偏差:

  • GridSearchCV等sklearn交叉验证工具不需要你在模型初始化阶段传入sample_weight、base_margin,这类属于fit()方法的参数可以在调用交叉验证的fit时统一传入,框架会自动按折索引拆分对应的数据片段,不会错位。
  • 1.3.0及以上版本的XGBoost sklearn接口已经支持给验证集单独传权重和base_margin,不需要切换原生接口就能实现eval_set的独立参数配置。

方案1:基于sklearn网格搜索/交叉验证工具快速实现

适合旧GLM模型固定、不需要随交叉验证折重训的场景,传入的权重、base_margin数组长度只要和全量输入X、y长度完全对齐即可,sklearn会自动按拆分索引匹配每折的训练、验证子集对应数值。

from sklearn.model_selection import GridSearchCV
import xgboost as xgb

# 初始化模型,不需要在该阶段传入权重、base_margin
xgb_model = xgb.XGBRegressor(
    n_estimators=50,
    learning_rate=0.01,
    n_jobs=4,
    eval_metric="poisson-nloglik",
    objective="count:poisson" # 泊松回归必须显式指定目标函数,默认平方误差和评估指标不匹配
)

# 参数搜索网格
param_grid = {
    "max_depth": [3, 5, 7],
    "max_leaves": [20, 50, 100]
}

grid = GridSearchCV(
    estimator=xgb_model,
    param_grid=param_grid,
    cv=5,
    scoring="neg_mean_poisson_deviance"
)

# 传入全量数据对应的权重、base_margin数组
grid.fit(
    X=X_all,
    y=y_all,
    sample_weight=_ALL_WEIGHT,
    base_margin=_ALL_BASE_MARGIN
)

重要提醒:如果你的base_margin来自旧GLM模型的输出,禁止用全量数据训练GLM后输出所有样本的预测值当base_margin做交叉验证,会造成严重数据泄露。这种场景请用下面的手动实现CV方案。


方案2:手动实现K折交叉验证(严格规避数据泄露)

如果你需要在每折交叉验证中单独训练旧GLM模型、生成对应折的base_margin(避免泄露),可以手动切分折逐次训练,同时支持自定义每折验证集的参数传入:

from sklearn.model_selection import KFold
import numpy as np

kf = KFold(n_splits=5, shuffle=True, random_state=42)
cv_scores = []

for train_idx, val_idx in kf.split(X_all):
    # 拆分当前折的训练、验证子集
    X_tr, X_val = X_all.iloc[train_idx], X_all.iloc[val_idx]
    y_tr, y_val = y_all.iloc[train_idx], y_all.iloc[val_idx]
    w_tr, w_val = _ALL_WEIGHT[train_idx], _ALL_WEIGHT[val_idx]

    # 若需规避泄露:在该位置只用当前折训练集数据训练GLM,再输出两个子集的base_margin
    # 示例:glm.fit(X_tr, y_tr, sample_weight=w_tr)
    # bm_tr = glm.predict(X_tr)
    # bm_val = glm.predict(X_val)
    # 若旧GLM为固定离线模型,直接按索引取对应margin即可
    bm_tr = _ALL_BASE_MARGIN[train_idx]
    bm_val = _ALL_BASE_MARGIN[val_idx]

    model = xgb.XGBRegressor(
        n_estimators=50,
        max_depth=100,
        max_leaves=100,
        learning_rate=0.01,
        n_jobs=4,
        eval_metric="poisson-nloglik",
        objective="count:poisson"
    )
    model.fit(
        X_tr, y_tr,
        sample_weight=w_tr,
        base_margin=bm_tr,
        eval_set=[(X_val, y_val)],
        # 按eval_set的顺序传入对应验证集的权重、margin即可
        sample_weight_eval_set=[w_val],
        base_margin_eval_set=[bm_val],
        verbose=False
    )

    # 计算当前折验证集指标
    val_pred = model.predict(X_val, base_margin=bm_val)
    fold_score = poisson_nloglik(y_val, val_pred, sample_weight=w_val)
    cv_scores.append(fold_score)

print(f"交叉验证平均泊松负对数似然: {np.mean(cv_scores):.4f}, 标准差: {np.std(cv_scores):.4f}")

方案3:XGBoost原生DMatrix接口实现(最灵活)

如果习惯用XGBoost原生训练接口,可以为每个数据集单独创建DMatrix对象,绑定对应权重、base_margin,训练时会自动读取,不需要额外传参:

import xgboost as xgb

# 训练集绑定对应权重、margin
dtrain = xgb.DMatrix(
    X_train,
    label=y_train,
    weight=_WEIGHT_TRAIN,
    base_margin=_BASE_MARGIN_TRAIN
)
# 验证集单独绑定自己的权重、margin
dval = xgb.DMatrix(
    X_val,
    label=y_val,
    weight=_WEIGHT_VAL,
    base_margin=_BASE_MARGIN_VAL
)

params = {
    "max_depth": 100,
    "max_leaves": 100,
    "eta": 0.01,
    "objective": "count:poisson",
    "eval_metric": "poisson-nloglik",
    "nthread": 4
}

# 训练时直接传入验证集即可,自动读取绑定的权重和margin
model = xgb.train(
    params,
    dtrain,
    num_boost_round=50,
    evals=[(dtrain, "train"), (dval, "val")]
)

原有代码的注意事项

  • 你同时传入了n_estimators=25和nrounds=50,sklearn接口中nrounds是无效参数,实际生效的轮次是n_estimators,重复传参会导致参数不符合预期。
  • max_depth=100的设置树深过大,搭配小学习率非常容易过拟合,建议把树深、叶子节点数作为交叉验证的重点调优参数。

内容的提问来源于stack exchange,提问作者Berthrand Eros

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 01:30:57