用数据证明直觉:CTR 特征工程的完整实战复盘
🥇用数据证明直觉:CTR 特征工程的完整实战复盘
技术分享|2026-2-27|最后更新: 2026-2-27
type
Post
status
Published
date
Feb 27, 2026
slug
summary
tags
category
技术分享
icon
password
Status
我在 Criteo 广告点击数据上做了一组完整的特征工程实验。结果有些出乎意料:原始的 39 个特征,每一个单独拿出来做 IV(Information Value)评估,全部被判定为 "useless"——预测力不足。但经过系统的特征构造之后,AUC 提升了 3.42%,而且模型最终学到的 Top 9 重要特征全部是新构造出来的。
这篇文章我想记录的是一个完整的思考过程:怎么从一堆看起来"没用"的原始数据里,用数据分析工具一步步把有价值的信号挖出来。

起点:所有特征都"没用"?

先说数据背景。Criteo 是广告行业最常用的公开 CTR 数据集,包含 13 个数值特征(I1-I13)和 26 个类别特征(C1-C26),全部匿名化处理。我做了 RTB 场景的降采样处理,最终 CTR 约 5%,100 万条样本。
拿到数据第一件事,我用 IV/WoE 对每个特征做了预测力评估。IV 是金融风控领域常用的特征筛选指标,规则很简单:
结果是这样的:
排名
特征
IV
评级
1
I9
0.0165
useless
2
I5
0.0161
useless
3
C12
0.0157
useless
4
I1
0.0156
useless
5
I2
0.0140
useless
全军覆没。没有一个特征的 IV 超过 0.02 的 weak 门槛。
但这并不意味着这些特征真的没用。用同样的数据训练 XGBoost,AUC 能到 0.5892——远高于随机猜的 0.5。树模型靠的是特征组合而非单个特征。IV 衡量的是单一特征的线性区分能力,而现实中有预测力的往往是特征之间的交互。
不过 IV 作为快速筛选工具仍然有价值。我拿 IV 排名和 SHAP 重要性排名、XGBoost gain 排名做了交叉验证:
特征
IV 排名
SHAP 排名
XGBoost gain 排名
I9
1
1
3
I5
2
3
2
I1
4
2
1
I2
5
4
4
三种方法的 Top 4 完全重合,只是内部排序有差异。IV 的优势在于速度——毫秒级完成,SHAP 要秒级,适合大规模特征的第一轮初筛。

让数据告诉你该构造什么特征

传统做法是凭经验手工构造交叉特征,比如"时段 x 设备"、"广告主 x 广告位"。这种做法有两个问题:一是依赖领域经验,新人很难复制;二是容易遗漏真正有价值的组合。
我换了个思路:用 SHAP interaction values 直接量化特征之间的交互强度,看看哪些组合真的有信号。对 XGBoost baseline 模型做 interaction 分析后,得到这样的结果:
特征对
Mean Interaction
I5 x I9
0.005987
I1 x I9
0.005486
I2 x I5
0.005060
I2 x I9
0.004367
I1 x I5
0.004266
I1 x I2
0.004112
一个清晰的模式浮出来了:I1、I2、I5、I9 四个特征之间的两两交互值都远高于其他组合。如果这是真实广告数据,它们可能分别对应"广告主预算"、"用户活跃度"、"时段热度"、"广告位价值"之类的业务维度——匿名数据里看不到语义,但交互模式能帮我们猜到它们之间存在有意义的关联。
这就给出了明确的特征构造方向:优先构造这个四元组内部的两两交叉。

构造特征:四类方法,各有各的道理

基于 SHAP interaction 的分析结果,我构造了四类新特征,总共 20 个。

交叉特征:把交互显式化

取 SHAP interaction 排名前 5 的特征对,直接做乘积:
新特征
来源
IV
I5_x_I9
I5 * I9
0.0238
I1_x_I9
I1 * I9
0.0228
I1_x_I5
I1 * I5
0.0224
I2_x_I9
I2 * I9
0.0216
I2_x_I5
I2 * I5
0.0207
注意看 IV 值——交叉特征的 IV 全部 > 0.02,原始特征全部 < 0.02。单独看每个特征都"没用",但组合起来就跨过了 weak 的门槛。FM(Factorization Machine)的核心思想就是这个:真正有预测力的不是单个特征,而是特征之间的二阶交互。

时间特征:sin/cos 完胜二值标记

实验数据里有 hour(0-23),我用了两种编码方式:
新特征
编码方式
IV
hour_cos
cos(2π · hour/24)
0.0262
hour_sin
sin(2π · hour/24)
0.0129
is_peak
高峰时段二值标记
0.0000
is_night
夜间二值标记
0.0000
sin/cos 周期编码的 IV 远高于二值标记。hour_cos 的 IV 达到 0.0262,是所有新特征中最高的。而 is_peakis_night 的 IV 几乎为零。
原因很直接:sin/cos 保留了时间的连续性和周期性(23点和0点在数值上很远,但在 cos 编码下很近),而二值标记把连续信号硬压成了 0/1,丢失了绝大部分信息。
这个结论在广告场景中很实用。生产系统里通常把 hour 当类别特征做 Embedding,本质上也是在学一个连续表示。但如果你用的是树模型或者线性模型,sin/cos 编码是最简单有效的做法。

统计特征和分箱特征

统计特征是对 13 个数值特征做行级聚合(均值、标准差、最大值等)。效果一般,IV 大多在 0.01 左右。分箱特征是把数值特征离散化成分位数桶,帮助树模型找到更精确的分裂点。
这两类特征的价值不如交叉特征和时间特征,但胜在稳定——几乎在任何数据集上都能提供一些增量信息。

筛选和验证:特征工程的闭环

构造了 20 个新特征后,不是全部丢进模型就完事了。我用了一个简单的 IV 筛选 pipeline:
  1. 对每个新特征计算 IV
  1. IV >= 0.01 的保留
  1. 被筛掉的:is_peakis_nightnum_nonzeronum_max_rationum_min —— 5 个
  1. 保留 15 个新特征
然后做端到端对比:
实验
特征数
AUC
LogLoss
Baseline(原始特征)
23
0.5672
0.1923
Enhanced(原始 + 新特征)
38
0.5866
0.1916
仅新特征
15
0.5862
0.1916
几个值得注意的点:
AUC 提升了 +0.0194(+3.42%)。在 CTR 预估领域,这个幅度不算小。线上系统里 0.01 的 AUC 提升就值得上一次 AB 实验。
仅用 15 个构造特征就几乎等同于全部 38 个特征的效果(0.5862 vs 0.5866)。新特征已经捕获了原始特征的大部分信息,甚至更紧凑。
但最让我意外的是增强模型中特征重要性的排名:
排名
特征
XGBoost Gain
来源
1
hour_cos
62.85
[新] 时间
2
I5_x_I9
36.51
[新] 交叉
3
I9_bin10
33.87
[新] 分箱
4
I1_x_I9
30.07
[新] 交叉
5
hour_sin
26.31
[新] 时间
...
...
...
...
10
I1
17.27
原始
Top 9 全是新构造的特征。原始特征从第 10 名才开始出现。模型很诚实:显式构造的交叉、分箱、时间特征比原始特征提供了更直接的预测信号。

回到真实世界:生产环境的特征工程长什么样

实验做完,我拿这套流程和自己负责的 RTB 生产系统做了对比。教科书和生产之间的差距,比我预想的大得多。

手动交叉 vs FM 自动交叉

实验里我手动构造 I5 * I9 这种乘积特征,在树模型上效果不错。但在生产系统里,FM 层已经在做这件事了——而且是全量的:
FM 对所有特征对做二阶交互,复杂度 O(kn),不需要你挑选哪些特征对值得交叉。所以在用了 FM 的生产系统里,手动构造交叉特征的价值被大幅压缩。

Embedding vs Label Encoding

实验中一个让我印象深刻的发现:26 个类别特征(C1-C26)做 Label Encoding 后喂给 XGBoost,AUC 只有 0.5061——跟随机猜差不多。
原因是 hash 过的类别值对树模型来说就是无序整数。"C3=42" 和 "C3=99137",树只知道 42 < 99137,但这个大小关系没有任何语义。
生产系统用 Embedding 解决这个问题:每个类别值映射到一个可学习的向量空间,语义相近的广告主、创意会有相近的向量表示。深度 CTR 模型超越树模型的核心不是网络更深,而是表示能力更强。
方法
本次实验
生产系统
交叉特征
手动乘积 (I5 * I9)
FM 自动全量二阶交叉
时间编码
sin/cos
hour 作为类别 Embedding
统计特征
行级 sum/max/std
历史 CTR/曝光/点击统计
分箱
quantile binning
Go 端 BucketEncoder
特征选择
IV + SHAP
业务经验 + 线上 AB

生产中特征工程的真正战场

对比下来,我发现生产环境中特征工程的重点根本不在手动交叉这些事上。FM 已经覆盖了大部分,真正花精力的地方是另外几件事。
历史统计特征的时间窗口设计。生产系统的 StatisticsProcessor 提供了过去 7 天的 CTR、过去 30 天的曝光数、上一次点击的时间间隔这类特征。Criteo 数据集里完全没有这些,但在真实广告系统中这才是最有预测力的信号。
Training-Serving 一致性。生产系统中特征处理分两端:Go 端的 FeatureProcessor 负责 Serving 时的实时特征提取,Python 端的 feature_processor.py 负责训练时的批处理。两端对同一个特征的处理逻辑必须完全一致——分桶边界、缺失值填充、归一化参数,差一点点就会引入 Training-Serving Skew,直接吃掉离线 AUC 的提升。这个问题不性感,但杀伤力巨大。
新特征的线上验证。离线 AUC 提升不等于线上收入提升。一个新特征从提出到上线可能要跑两周的 AB 测试。

回头看

回顾整个实验,有几件事我自己印象比较深。
IV/WoE 做特征初筛很快。毫秒级完成,结果和 SHAP、XGBoost gain 高度一致。特征多的时候先用 IV 过一遍,省掉很多时间。
SHAP interaction 比拍脑袋靠谱。实验里它指出的 I1/I2/I5/I9 四元组,构造出来的交叉特征 IV 全部突破了 useless 的门槛。如果没有这个分析,我大概率不会刚好猜中这几个组合。
时间特征别用二值标记。cos 编码的 IV 是 0.0262,is_peak 和 is_night 是 0.0000。差距太明显了。
特征工程对树模型价值大,对深度模型价值小。本次实验在 XGBoost 上拿到 +3.42% AUC,但换成 DeepFM(FM 层自动做交叉),增量会小得多。用什么模型,决定了你的特征工程应该花在哪。
最后一个:特征不是越多越好。消融实验里,用 SHAP 排名前 10 的特征训练出来的模型 AUC 反而比全部 85 个特征高。冗余特征是噪声。
深入解析 Codex 智能体循环面向业务的agent后端架构设计
Loading...