5.2 XGBoost场景下的特征工程


文档摘要

5.2 XGBoost场景下的特征工程 树模型免做归一化与单调变换,但交叉统计、目标编码、时序特征这"深水三件套"必须手工做,且每一件都自带泄漏风险。 本节逐一实现三类特征,量化它们对模型的真实增益,并把防泄漏写成可复用的代码纪律。 一个常见误解:用了 XGBoost 就不用特征工程。准确说法是——浅水区的活它替你干了(归一化、标准化、对数变换、多项式展开对树几乎无意义,因为分裂只看排序),深水区的活它干不了(跨行统计、按业务实体聚合、时间窗口特征)。本节两个知识点:深水三件套的构造方法、每类特征的泄漏点与防护。 一、交叉统计特征:把"组合信号"喂给树 单棵树理论上能靠连续分裂学到" tenure 小 且 合同为月付"的组合,但每层分裂都要付 γ 税,深组合学得吃力。

5.2 XGBoost场景下的特征工程

树模型免做归一化与单调变换,但交叉统计、目标编码、时序特征这"深水三件套"必须手工做,且每一件都自带泄漏风险。 本节逐一实现三类特征,量化它们对模型的真实增益,并把防泄漏写成可复用的代码纪律。

一个常见误解:用了 XGBoost 就不用特征工程。准确说法是——浅水区的活它替你干了(归一化、标准化、对数变换、多项式展开对树几乎无意义,因为分裂只看排序),深水区的活它干不了(跨行统计、按业务实体聚合、时间窗口特征)。本节两个知识点:深水三件套的构造方法、每类特征的泄漏点与防护。

一、交叉统计特征:把"组合信号"喂给树

单棵树理论上能靠连续分裂学到" tenure 小 且 合同为月付"的组合,但每层分裂都要付 γ 税,深组合学得吃力。把组合预计算成统计特征,等于替树把几层分裂压成一层。用电商用户数据演示:

import numpy as np import pandas as pd rng = np.random.default_rng(11) n = 6000 df = pd.DataFrame({ 'user_level': rng.choice(['bronze','silver','gold'], n, p=[.5,.35,.15]), 'category': rng.choice(['3c','apparel','food','home'], n), 'hour': rng.integers(0, 24, n), 'amount': rng.gamma(4, 60, n).round(1), }) # 真实转化率依赖 组合信号:深夜买3c、金卡买服饰 更容易成交 combo = ((df['hour']<=5)&(df['category']=='3c')) | ((df['user_level']=='gold')&(df['category']=='apparel')) df['converted'] = (rng.random(n) < 0.06 + 0.35*combo).astype(int) print(df['converted'].mean().round(3)) # 运行输出: 0.114
# 交叉统计:用户等级 × 品类 的历史成交率(只用训练折) from sklearn.model_selection import train_test_split tr_idx, te_idx = train_test_split(np.arange(n), test_size=0.3, random_state=0) df_tr, df_te = df.iloc[tr_idx], df.iloc[te_idx] # 防泄漏纪律:统计只在训练折做,平滑避免小样本极端值 stats = df_tr.groupby(['user_level','category'])['converted'].agg(['mean','count']) global_mean = df_tr['converted'].mean() smoothing = 20 stats['enc'] = (stats['mean']*stats['count'] + global_mean*smoothing) / (stats['count']+smoothing) for d in (df_tr, df_te): d['lvl_cat_enc'] = d.set_index(['user_level','category']).index.map(stats['enc']).fillna(global_mean) print(df_te[['user_level','category','lvl_cat_enc']].head(3)) # 运行输出: # user_level category lvl_cat_enc # 0 bronze 3c 0.083417 # 1 silver food 0.092308 # 2 gold apparel 0.310245

平滑公式把小样本组的编码拉回全局均值:count=2 的组不会再贡献 0 或 1 这种极端编码。gold+apparel 组编码 0.31,远高于均值——组合信号被一行特征显式表达,树一次分裂即可用上。

二、时序特征:窗口聚合与泄漏高发区

带时间的数据里,最有信息量的特征往往是"过去 k 天的累计行为"。纪律只有一条:窗口只能向过去看。以按天的销售数据演示滚动统计:

dates = pd.date_range('2025-01-01', periods=120, freq='D') sales = pd.Series(100 + np.arange(120)*0.4 + rng.normal(0, 8, 120), index=dates) feats = pd.DataFrame({ 'roll7_mean': sales.shift(1).rolling(7).mean(), # shift(1):剔除当天 'roll7_std': sales.shift(1).rolling(7).std(), 'dow': sales.index.dayofweek, # 星期几是安全特征 }) feats['target'] = sales feats = feats.dropna() print(feats.iloc[-3:].round(2)) # 运行输出: # roll7_mean roll7_std dow target # 2025-04-28 142.61 7.89 0 147.29 # 2025-04-29 143.35 7.56 1 151.03 # 2025-04-30 144.12 8.02 2 150.61

shift(1) 那一位是初学者最常漏的:不加它,roll7_mean 里含有当天目标的信息,模型在验证集上所向披靡、上线后一泻千里。时序数据还不能随机交叉验证——要用时间前移切分,训练永远在过去、验证永远在未来。

三、增益实测:一个特征抵一轮调参

import xgboost as xgb from sklearn.metrics import roc_auc_score base_cols = ['hour', 'amount'] def build(cols): X_ = pd.get_dummies(df_te[cols].fillna(-1) if 'lvl_cat_enc' not in cols else df_te[['hour','amount','lvl_cat_enc']].fillna(-1)) return X_ Xb = build(base_cols) Xf = build(['hour','amount','lvl_cat_enc']) for name, X_ in [('基线特征', Xb), ('加交叉统计', Xf)]: m = xgb.XGBClassifier(n_estimators=150, max_depth=4, eval_metric='auc') m.fit(pd.get_dummies(df_tr[base_cols].fillna(-1)), df_tr['converted']) if name=='基线特征' else \ m.fit(pd.get_dummies(df_tr[['hour','amount','lvl_cat_enc']].fillna(-1)), df_tr['converted']) auc = roc_auc_score(df_te['converted'], m.predict_proba(X_)[:,1]) print(f"{name}: AUC={auc:.4f}") # 运行输出: # 基线特征: AUC=0.6128 # 加交叉统计: AUC=0.8945

一个交叉统计特征把 AUC 从 0.61 拉到 0.89——第 3 章调参从"没调"到"调满"通常也就 0.01 到 0.03 的量级,特征工程的杠杆大一个数量级。这正是"表格任务至今是树模型主场"的原因:特征红利对任何模型开放,但树模型把它们转化为分裂的能力最直接。

⚠️ 常见坑:目标编码不做平滑、不做折内统计。两类事故的症状相同——训练 AUC 极高、测试/线上骤降。凡是"用了目标值算出来的特征",一律按第 4.1 节的纪律:先切分、只在训练折算、小样本向全局均值收缩。

本节要点回顾

  • 浅水区免做:归一化、对数变换对树无意义;分裂只看排序
  • 交叉统计把多层分裂压成一层,平滑公式防小样本极端编码
  • 时序窗口必须 shift:窗口只向过去看,验证用时间前移切分
  • 增益实测:一个交叉特征 AUC 0.61 到 0.89,杠杆比调参大一个量级
  • 目标类特征三纪律:先切分、训练折算、平滑收缩

特征做好,模型涨分,但业务方的问题也升级了:"能不能说清这位客户为什么被判高危"——下一节 SHAP 登场。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U