← Back to list

From Gradient-Boosted Trees to Deep Recommenders: Practical Lessons from Migrating a Production Customer Support Recommender

判别式推荐 Intuit
Abstract 7 │ Reading 7 │ Rating —
2026-08-25
Sonia Sharma, Jeyendran Balakrishnan, Shreya Rajpal, Swapnil Parekh, Nagaraj Janardhana, Andrew Mattarella-Micke
Intuit
Intuit 复盘了把一套支撑约 3.6 亿美元营收组合的线上客服会话推荐系统从 CatBoost 多分类迁移到 pairwise-binary 深度推荐器的全过程:在'线上质量不得回退'的硬约束下,用固定 16709 正例 contact 群体、对齐已部署 CatBoost 基线(CL 0.4985 / ER 0.5310 / GZ 0.5676)的 micro-F1 评估器逐步验证——显式/隐式×硬/软的负样本分类学与 K 扫描(发现 PR-AUC 改善不传导到 contact 级指标)、50:50 批次组成与 InfoNCE 对比损失被证明可加而非冗余(CL 0.4981 / ER 0.5510 / GZ 0.5354)、36 配置 double-descent 网格显示 72× 更小的 17.2M 模型加 15% 标签噪声注入是唯一打败 CatBoost CL 的配置(0.5003)而单纯训 200 epoch 无恢复(空结果),最终 nomic-embed 逐 utterance 嵌入上的 attention pooling 成为首个直接超越 CatBoost 的方案(ER 0.5630 / GZ 0.5943,CL 仅差 1.2pp),two-tower 侧则用零初始化残差修正路径加 DIN 式候选条件 target attention 拿到最佳 GZ 0.6195;服务侧实测文本特征化是两系统最贵的一步,正确攒批带来 19×/34× 吞吐(17→323/575 req/s)但仍远不及 CatBoost 的 9954 req/s,INT8 量化仅 +11% 吞吐且 ER/PR-AUC 掉约 2 点故不采纳;论文自我定位为生产迁移案例研究而非 benchmark 结果,且新模型尚无线上 A/B。
评分原因
摘要评分:Intuit 生产环境的真实迁移案例:所有实验都对齐线上已部署的 CatBoost 基线、跑在支撑约 3.6 亿美元营收组合的客服推荐系统数据上,还给出单请求/批量 124-641ms 的服务成本与 INT8 量化取舍,工业细节稀缺可贵;但技术本身(pairwise 二分类改写、负采样、对比损失、two-tower/DeepFM、注意力池化)都是成熟手段,且新模型尚无线上 A/B,故 7 分。
精读评分:实验纪律与工程细节极扎实(固定 16709 正例群体评估器对齐线上 CatBoost、36 配置 double-descent 网格、25 个 two-tower 变体且要求复现、完整服务成本剖面、200-epoch 空结果与 INT8 不采纳都如实报告),但所有技术(pairwise-binary 重构、负采样、InfoNCE、DeepFM/two-tower、DIN target attention、attention pooling)均为成熟手段的组合,无方法新颖性,且新模型尚无线上 A/B、全部实验仅基于内部生产数据无公开 benchmark,故 7 分。
feature-interaction contrastive-ssl transformer negative-feedback inference-serving industrial

From Gradient-Boosted Trees to Deep Recommenders:一次生产客服推荐系统迁移的实战复盘

研究动机与背景

商品目录变化的速度超过了模型能适应的速度

Intuit 这篇论文写的不是一个新方法,而是一次真实发生在生产环境里的模型迁移:把一套线上运行的客服会话推荐系统,从"按商品做多分类的梯度提升树(CatBoost)"换成"pairwise 二分类的深度推荐器"。

迁移的起点是商品目录形态的变化。服务型业务正在从"独立定价的 SKU"转向"服务时刻动态组装的捆绑包 + 折扣组合"。论文强调这不只是规模问题,而是推荐问题形状本身的改变:一个 bundle 的身份是组合性的(由哪些 SKU、配哪种折扣构成),而不是原子性的。

被替换的旧系统是一个 per-product 的 GBDT 多分类器,它是为慢速变化的目录设计的。作者列出了三条结构性局限:

  1. 问题形式化(Problem formulation)。"在商品上做多分类"假定了一个固定的标签集合;新增一个 bundle 意味着新增一个类别,并要重训标签空间本身。改成 pairwise-binary 形式 $(\text{contact}, \text{product}) \rightarrow \text{match}/\text{no-match}$ 就绕开了这一点:一个 bundle 或新 SKU 只是一个新的 item 侧特征向量,而不是一个新的输出单元。
  2. 多模态信号。旧模型只在表格化、聚合后的特征上打分,无法原生消费实时会话转录文本。深度推荐器可以通过 attention pooling 直接以转录表示为条件——它学的是逐 chunk 的相关性权重,而不是对整段会话做均匀池化。
  3. 架构与目标函数的灵活性。一旦问题变成 pairwise,就可以在共享的 user/item 嵌入空间上用对比目标;也可以在 two-tower、DeepFM 等架构族之间按延迟/表达力权衡取舍。

迁移的代价

论文很坦诚地列出了三项具体成本:

  • 调参面积暴涨:超参数比提升树模型多一个数量级;
  • 监督信号更稀疏、更不平衡:正例率约 4.5%,154 个类别字段、约 2k 个稠密特征;
  • 单请求推理成本更高:每个 (contact, product) 对都要走一遍 embedding 查表 + DNN 前向,而不是一次提升树遍历。

硬约束:推荐质量不能回退

这是理解全文最关键的一句话。这次迁移不是绿地研究,而是在一条硬性的非回归约束下做的。这套客服推荐器往下游喂给一个话术增强系统 DynaPitch(同团队另一篇工作),DynaPitch 的质量直接依赖它推荐的质量——这里一旦回退,不是一个孤立的指标掉点,而会传播到下游系统的输出。

更进一步,这个推荐器是多个生态增长与留存计划的骨干,喂给的升级推荐支撑着约 3.6 亿美元量级的营收组合。所以本文写作时对齐的目标是"保住质量",而不只是"提升质量"。因此每一个实验都在同一份固定正例 contact 群体上、用同一套指标定义、对着已部署的 CatBoost 基线报告,让"我们有没有回退"在每一步都有一个无歧义的答案。

问题设置与评估方法论

实时推荐设置

客服会话是随进程增量打分的,推荐在通话中的某个时刻被服务出去。生产系统把转录文本切成 chunk_num 索引的、每块恰好 20 条 utterance 的片段,每块打分一次。Case-Load 和 Early-Reco 就是第 0 块和第 1 块("通话开始"是对 chunk 0 的直觉描述,不是一个挂钟时间断言)。选 20 条 utterance 是在"上下文足够打分"和"服务时每块调用成本低"之间的折中。

数据与切分

全文把一次客服会话称为一个 contact。

项 设置
训练/验证池 2025-01 ~ 2026-03 的 contact
切分方式 在 contactid 级别按 75/25 随机切分(seed 42),保证同一 contact 的所有 chunk 行落在同一分区
测试集 时序 out-of-time holdout:2026-04 ~ 2026-06,训练/验证阶段完全未见
规模 训练 903,829 行 / 验证 302,122 行 / 测试 360,022 行(切块与负采样之后)
商品数 9 个(计划扩张到低百量级)

注意:本文所有数据均为 Intuit 生产内部数据,不含任何公开学术数据集。

两级指标

  • Chunk / pair 级:扁平的 (contact-chunk, product) 行上的 log-loss、ROC-AUC、PR-AUC(average precision),都在验证集上拟合;
  • Contact 级:每个 contact 在通话中一个定义好的时刻产出一个决策。

三个业务指标:CL / ER / GZ

表 1:Contact 级业务指标定义

指标 选取方式 回答什么问题
CL(Case-Load) 第一块(chunk_num = 0) 最早可能给出的推荐的质量
ER(Early-Reco) 第二块(chunk_num = 1) 稍晚一点、信号多一些时的质量
GZ(Ground-Zero) 每个 contact 上权重最大的那一块 推荐最可能真正触发的那一刻的质量

三者都是在同一份固定正例 contact 群体(当前数据快照下 16,709 个 contact,与评估旧 CatBoost 模型时完全一致)上计算的 micro-F1,因此本文每一个数字都可以和已部署基线直接比较,不存在群体错配。

贯穿全文的基线参考值(Legacy CatBoost):CL 0.4985 / ER 0.5310 / GZ 0.5676。

基线设置:负样本分类学与 CatBoost/DeepFM 基线

负样本的分类学

负样本的选择是这个问题上最主导的建模杠杆——严重的 pair 级不平衡和不可见的未转化候选,会把模型每个 contact 的 top-1 选择扭向过度代表的商品。论文在两个层次上定义了一套统一词汇:

第一层:显式(explicit)vs 隐式(implicit)——按"我们怎么知道这个候选是负的"划分。

  • 显式:客户给了明确回答——他们对这个具体商品说了"不";这是 product_explicit_negative 流水线机制。
  • 隐式:从未给出过明确拒绝,我们只知道这个 contact 在这个候选上没有转化。

第二层:硬(hard)vs 软(soft)——在显式与隐式各自内部,按负信号的强度划分。

  • 显式侧:硬 = 明确、即时的"不";软 = 更弱或更含糊的非接受。
  • 隐式侧:硬 = 候选来自一个"该 contact 什么都没转化"的 chunk——在没有明确拒绝的前提下最强的"无信号",实现为 overall_negative 与 product_random_negative 机制;软 = 候选与另一个该 contact 确实转化了的商品同处一个 chunk,因此它的未转化是更弱的证据(可能只是"没被选中"而非"被主动拒绝"),实现为 implicit_negative 机制。

还有一个独立于上述行级分类学的概念:纯负 contact(pure-negative contact)——整个 contact 全程什么都没转化。这是 contact 的群体级属性,与描述单行的显式/隐式/硬/软是两个维度。

在 contact 级,采样起点大致是 1/3 正 : 2/3 负(保留每个正例 contact,负例 contact 采样到大致匹配,先取 explicit-hard 再取 explicit-soft)——这是一个刻意平衡、而非群体代表性的样本。但训练行是 contact-chunk 而不是 contact:负例 contact 比正例更长(它们不会因转化而结束,会持续产生 chunk),且我们还为每个正例 contact-chunk 额外构造隐式负样本,于是一个按 1/3 正例构建的 contact 级样本,落到 chunk 级只有 ≈4.1% 正例。

Chunk 级 vs contact 级的采样粒度

隐式硬负样本(overall_negative 与 product_random_negative)可以在两种粒度上采样:每个 contact 采一次(同一套负样本盖在它的每个 chunk 上),或每个 chunk 独立采。这个结论对 CatBoost 与 DeepFM 同样成立,因为它关心的是训练行怎么构造,而不是谁来消费。在匹配的 $K=2$ 下,按 chunk 独立采样在两个可比切面上都更好(CL 0.437 vs 0.407;ER 0.530 vs 0.525;contact 级那次运行没评 GZ),本文其余部分均采用这一约定。

建立基线

范围说明——全文都是 pairwise-binary 形式。本文不与更早的多分类形式做对比。这里研究的两个模型(Legacy CatBoost 与 DeepFM 候选)已经跑在同一套 pairwise-binary 形式上、同一份数据上:每行是一个 (contact-chunk, candidate product) 对,带二值 match/no-match 标签,负样本行按上面的分类学构造。§1.1 里关于多分类的讨论只是背景动机,不是本文报告结果的对比对象。

文本特征。 CatBoost 用单一 TF-IDF 向量器表示 chunk 文本(最多 3500 维、1–3 gram、去停用词、词项文档频率在 10–75% 之间)。DeepFM 的 with-text 配置对同一段文本做 TF-IDF,再用 truncated SVD 压到约 40 个成分,拼进表格稠密向量(1980 → 2020 维),而不是单独一个学习出来的文本塔;更丰富的文本表示留到 §8。

表 2:基线结果(pairwise-binary F1)

Configuration CL ER GZ
Legacy (CatBoost), with text 0.4985 0.5310 0.5676
DeepFM, no text 0.3426 0.4362 0.4293
DeepFM, with text 0.4678 0.5283 0.5158

分析:这张表定下了全文的基调——朴素迁移过去的 DeepFM 在三个指标上全面落后于已部署的 CatBoost;而且"有没有文本"是最大的单一落差来源(CL 0.3426 → 0.4678,+12.5pp)。文本信号从一开始就是这个问题的主线。

DeepFM 架构。 全文使用的 DeepFM 骨干是一个共享 DNN 塔 (2048, 1024, 512, 256),学习率 $3\times10^{-4}$,weight decay $10^{-4}$,10 个 warm-up epoch。表格特征分两条路径喂进 factorization-machine(FM)层:一条是约 1980 个连续特征的稠密投影;一条是分箱后嵌入的稀疏路径(按互信息选 top-100 类别特征、50 个分位数箱、嵌入维度 32)。

CatBoost 基线配置:depth 10、1000 iterations、学习率 0.0406、$\ell_2$ leaf 正则 8.51、scale_pos_weight 1.003,通过 25 次评估的贝叶斯搜索选出。

共享的负采样底座:表 2 的两个基线共享同一套负采样配置——overall_negative_n = 1 与 product_random_negative_k = 2,均在 contact 粒度上,再配一个二维行权重方案:每个负样本行的最终权重 = 一个按负样本类型索引的权重(overall_negative、product_explicit_negative、product_random_negative 各有自己的权重)× 一个按商品索引的权重(9 个商品各有自己的权重,因为有些商品更罕见、需要更多强调)。是两张独立查找表相乘,而不是一行一个扁平权重。这是本文每个模型训练与评估共同的底座。

架构对比:DeepFM vs Two-Tower

在叠加任何额外特征工程、attention pooling、对比目标之前,作者先问一个更窄的问题:把文本处理各自保持在本架构的标准机制上,哪个基础架构更强?

  • Two-tower:用分离的 user/context 编码器与 item 编码器,顶上做点积或学习出的相似度——很适合 retrieval 式服务,但对显式特征交互的建模较弱。
  • DeepFM:在共享特征空间上联合 FM、DNN 与 wide 组件——交互建模更丰富,但在推理时不太适合最近邻式服务。

作者特别澄清:two-tower 的 retrieval 式服务优势是为"大到无法穷举打分的物品目录"设计的,而这不是我们当前面对的约束(今天 9 个商品,扩张后大概率也就低百量级,完全在 DeepFM 穷举打分的射程内)。所以那个优势是潜在的、还没变成承重结构;下面的对比测的是"在我们的数据、我们当前的规模上哪个架构赢",不是"哪个通用更好"。

表 3:DeepFM vs two-tower(都带文本)

Architecture CL ER GZ Top-1
DeepFM (with text) 0.4678 0.5283 0.5158 —
TT-base: residual (with text) 0.4575 0.5537 0.6152 0.5822

分析:DeepFM 在通话开始及附近(CL)胜出,而 two-tower 在观察到更多会话之后(ER、GZ)更强。作者据此判断:在本文非回归约束下最要紧的那个区间——"最早可能给出的推荐,此时还没有任何转录信号可依"——DeepFM 是更好的基础架构,因此把它选为全文主架构;two-tower 这条线在 §8.2 单独按它自己的方式推进。

负采样:一次彻底的调查

结构化负采样

除了改变"采多少个随机隐式负样本",作者还测试了一个结构化、基于规则的采样器:通过观测到的客户与商品数据里可解释的模式来选隐式负样本,而不是均匀随机抽——这样采样决策就可以独立于神经模型被检视。在 $K=2$ 与 $K=4$ 两点上与随机采样对比。

表 4:随机 vs 结构化负采样,两个 $K$ 值

$K$ Sampling CL ER GZ Val PR-AUC Test PR-AUC
2 Random 0.4649 0.4924 0.4797 0.6845 0.5669
2 Structured 0.4572 0.4987 0.4836 0.7137 0.5527
4 Random 0.4857 0.5084 0.4982 0.5904 0.5244
4 Structured 0.4495 0.5069 0.4900 0.6722 0.6293

分析:结构化采样在若干设置下改善了 pairwise 排序行为,但这些收益没有一致地传导到 contact 级指标。在 $K=4$ 上,test PR-AUC 大涨(0.5244 → 0.6293),CL 却反而下跌(0.4857 → 0.4495);在 $K=2$ 上它改善了 validation PR-AUC,但 ER/GZ 只有微小移动,CL 与 test PR-AUC 没有收益。作者给出的机制解释很值得记:PR-AUC 度量的是正候选与负候选之间的 pairwise 区分度,而 CL/ER/GZ 度量的是"对一个正例 contact,正确的商品有没有真的浮到最上面"——更有针对性的负样本可以锐化 pairwise 分离,却不改善最终的推荐决策。

隐式随机负样本数量 $K$ 的扫描

表 5 隔离出"每个正例 contact-chunk 采多少个隐式负样本"这一个变量,固定架构与训练目标,在排序质量与训练集规模之间找平衡点。为了让扫描跑得快,每个配置只在 50% 训练数据子采样上训练。

表 5:隐式随机负样本 $K = 0, \ldots, 7$(50% 训练子采样) 超参:DNN (2048, 1024, 512, 256),dropout 0.50/0.60,LR $3\times10^{-4}$,weight decay $10^{-4}$,10 个 warm-up epoch,batch 512。

$K$ CL ER GZ Val PR-AUC Test PR-AUC
0 0.4715 0.5072 0.4901 0.7727 0.5832
1 0.4706 0.5119 0.4932 0.7250 0.5810
2 0.4649 0.4924 0.4797 0.6845 0.5669
3 0.4680 0.5021 0.4906 0.6548 0.5614
4 0.4857 0.5084 0.4982 0.5904 0.5244
5 0.4405 0.5100 0.4994 0.6274 0.5832
6 0.4677 0.5132 0.4978 0.5834 0.5883
7 0.4629 0.5007 0.4897 0.5762 0.5805

分析:这次扫描有一个很干净的观察——validation 与 test PR-AUC 之间的间隙随 $K$ 增大单调收窄(从 $K=0$ 的 0.19 收到 $K=6$–$7$ 时的近乎为零),但这并没有转化为我们真正在乎的指标上的可靠改善:CL、ER、GZ 随 $K$ 非单调移动,且不跟随那个正在收窄的 val-test 间隙。据此作者选择 $K \in [2, 4]$ 作为后续全文使用的范围。

这是一个典型的"代理指标改善 ≠ 目标指标改善"的实战案例:泛化间隙收窄看起来很像"过拟合被治好了",但业务指标不买账。

对比学习与 BCE 联合训练

动机与目标函数

数据里正向转化信号相对稀少,作者希望模型显式地学到它们并把它们与有意义的负样本对比,同时仍然照顾到数量大得多的负样本。因此 BCE 与对比损失联合使用:BCE 抓住整体转化分布,对比学习锐化模型区分正信号与竞争负样本的能力。

对每个包含真实正例商品 $p$ 的 contact-chunk $u$,构造一个对比组:

$$G_u = \{p, n_1, \ldots, n_K\} \tag{1}$$

其中 $u$ 是 contact-chunk,$p$ 是与真实正向转化关联的商品,$n_j$ 是第 $j$ 个隐式负样本商品,$K$ 是从有效非正例商品中采样的隐式负样本数。在最优配置中 $K = 2$ 且隐式负样本随机采样。

minibatch 中所有正、负样本都参与 BCE 目标:

$$\mathcal{L}_{\mathrm{BCE}} = -\frac{1}{N}\sum_{i=1}^{N}\left[y_i \log \hat{y}_i + (1-y_i)\log(1-\hat{y}_i)\right] \tag{2}$$

其中 $N$ 是 minibatch 里的样本总数,$y_i \in \{0,1\}$ 是样本 $i$ 的真实转化标签,$\hat{y}_i$ 是对应的预测概率。正例商品 $y_i = 1$;隐式负样本与来自纯负 contact 的样本 $y_i = 0$。

除 BCE 外,对至少含一个真实正例商品的组计算一个 user–item 对比目标。令 $\mathbf{z}_u$ 表示掩掉商品特有输入特征之后由共享 DNN 产出的 contact-chunk 表示——这个掩码阻止 contact 表示直接观察到当前正在打分的是哪个候选商品(这是让对比目标有意义的关键设计)。令 $\mathbf{z}_p$ 表示商品 $p$ 的表示,由它的商品文本嵌入投影到与 $\mathbf{z}_u$ 同一潜空间得到。

contact-chunk 与候选商品的兼容性用余弦相似度度量:

$$s(u, p) = \cos(\mathbf{z}_u, \mathbf{z}_p) \tag{3}$$

对正例商品 $p$ 及其 $K$ 个隐式负样本 $\{n_1, \ldots, n_K\}$,user–item 对比损失是一个 InfoNCE 式目标:

$$\mathcal{L}_{\mathrm{CL}} = -\log \frac{\exp\!\left(s(u,p)/\tau\right)}{\exp\!\left(s(u,p)/\tau\right) + \sum_{j=1}^{K}\exp\!\left(s(u,n_j)/\tau\right)} \tag{4}$$

其中 $\tau$ 是对比温度,控制相对相似度分布的锐度。实验中固定 $\tau = 1.0$。

纯负 contact 不含正例锚点,因此不贡献 $\mathcal{L}_{\mathrm{CL}}$,但它们仍通过 BCE 参与训练。对比损失权重固定为 1.0,合并训练目标为:

$$\mathcal{L} = \mathcal{L}_{\mathrm{BCE}} + \mathcal{L}_{\mathrm{CL}} \tag{5}$$

批次组成(Batch Composition)

训练数据天然大约是 1/3 含正例、2/3 负例。在标准 BCE + 随机分批下,minibatch 期望上就反映这个 1/3–2/3 分布。在 BCE–对比混合设置下,作者额外控制正对比组与纯负单元在 minibatch 中的组成。

令 $r$ 表示批次构造时正对比组的目标比例:一个采样单元以概率 $r$ 是正对比组,以概率 $1-r$ 是纯负单元。作者考察 $r = \frac{1}{3}$(约等于数据的自然组成)与 $r = \frac{1}{2}$(50:50,提升正例组在优化中的代表性)。

这个比例指的是采样单元的组成,而不是正/负 BCE 标签的数量:当 $K=2$ 时,一个正例组贡献 1 正 2 负共 3 个样本,三者都进 BCE,而这个组整体进对比目标;一个纯负单元只进 BCE。

表 6(左):$K=3$、仅 BCE、无对比项下的组平衡分批

Config. CL ER GZ
Random ($r = 1/3$) 0.4680 0.5021 0.4906
50:50 ($r = 1/2$) 0.4781 0.5441 0.5286

表 6(右):仅对比损失 vs BCE 分批 + 对比损失组合,两个隐式负样本数

$K$ Setting CL ER GZ
2 CL only 0.4636 0.4606 0.4635
2 BCE+CL 0.4963 0.5433 0.5279
4 CL only 0.4661 0.5455 0.5048
4 BCE+CL 0.4981 0.5510 0.5354

分析:这一组实验的结论非常干净,而且是本文最有实操价值的发现之一。

  1. 只控制 minibatch 组成——不加对比项、不改架构——就已经改善了所有指标(ER 0.5021 → 0.5441,GZ 0.4906 → 0.5286)。这是一个独立、可加的杠杆,而不是对比目标收益的一部分被错误归因。
  2. 但它也不是对比目标的替代品:在 50:50 分批之上再加对比项($K=4$)达到 CL 0.4981 / ER 0.5510 / GZ 0.5354,在每个 contact 级指标上都优于只做分批。
  3. 只用对比损失(CL only)会明显变差,尤其 $K=2$ 时 ER 掉到 0.4606。

最强的 contact 级行为来自把有信息量的负样本、批次组成、对比目标三者结合,而不是任何单独一项。

Double Descent:刻画容量/正则区间

动机

DeepFM 的带文本基线模型(147.8M 参数)表现出教科书式的插值起始过拟合:训练损失单调下降,validation PR-AUC 在第 5 个 epoch 早早见顶(0.752)然后衰减——正是这个症状让实践者不信任深度模型在稀疏表格数据上的表现、退回树模型。

作者不接受这是天花板,而是问:这会不会只是一条 double-descent 风险曲线的左半支,而它的右半支同时优于 DeepFM 基线和它要替换的 CatBoost? 本节是一个平行的、更详细的研究(作者同时投给另一个 venue)的结果子集。

实验设计

四个条件臂(isolating specific mechanisms),与一个 9 点的 DNN 宽度/深度容量阶梯(648K 到数千万参数)做叉乘,架构族(DeepFM)与数据固定:

表 7:Double-descent 条件臂

Arm Regularization Early stop Label noise
clean_prod production on (patience 15) 0%
nostop production off 0%
lowreg none off 0%
noisy none off 15%

noisy 臂对应 Nakkiran 等人最锐利的插值峰配置。4 臂 × 9 容量点 = 36 个配置。

结果

表 8:Double-descent 容量扫描,蒸馏到与迁移相关的对比

Configuration DNN shape (params) CL ER GZ Test PR-AUC
Legacy (CatBoost) — 0.4985 0.5310 0.5676 —
DeepFM baseline, 40ep (2048,1024,512,256), 147.8M 0.4678 0.5283 0.5158 0.6561
Best size (lowreg), 40ep (2048,1024,512,256), 17.2M† 0.4772 0.5508 0.5244 0.6921
Best noise (noisy), 40ep (2048,1024,512,256), 17.2M† 0.5003 0.5528 0.5368 0.6365
200ep check (noisy) (4096,2048), 37.1M, 200ep 0.4800 0.5544 0.5222 0.6504

† 与 DeepFM 基线的 DNN 塔形状相同,但去掉了那个约 130M 参数的稠密投影层(DeepFM 基线用它路由表格特征)——所以这是一个真正更小的模型,不是同一个模型换个训法;它的总参数量比 DeepFM 基线小 72 倍。

分析:

  • 头条发现:完整网格里 36 个配置中几乎每一个都在 test PR-AUC 与 CL F1 上打败了 DeepFM 基线模型,而参数量只是它的一小部分——容量本身并不是 DeepFM 基线排序质量的来源。
  • 模型尺寸与正则选择是两个独立的杠杆:固定同样的 17.2M 参数 DNN 形状,只把"低正则"换成"噪声注入",CL 就从 0.4772 移动到 0.5003——这是整个网格中唯一一个打败 CatBoost 自身 CL(0.4985)的配置,并且它的 ER 也清过了 CatBoost(0.5528 vs 0.5310)。
  • 诚实报告:这个配置的 GZ 落后 CatBoost(0.5368 vs 0.5676),所以没有任何配置能在三个 contact 指标上同时清过 CatBoost——作者明确写"we report this honestly rather than rounding up"。
  • 噪声注入是设计杠杆,不只是诊断工具:标签噪声在小容量下作为隐式正则改善而非损害召回,这是一个可用的旋钮,作者把它连同更小的模型尺寸一起,采纳为下一次生产迭代的设计杠杆。

更长训练时程能否恢复?一个空结果

上面的容量扫描在固定 epoch 预算下改变模型大小。作为独立检查,作者问:如果固定容量、单纯训练更久,经典的 epoch-wise double descent 恢复会不会出现?

他们在 (4096, 2048) 形状(37.07M 参数)上把 noisy 臂训了 200 个 epoch——比本文任何其它 run 长一个数量级。

结果:validation PR-AUC 只有一个早期峰(epoch 16,0.7428),随后在整个 200-epoch 预算里单调衰减(epoch 199 降到 0.2902),任何位置都没有出现 dip → rise → dip 的恢复;test PR-AUC 0.6504,CL 0.4800,ER 0.5544,GZ 0.5222。

作者把这作为一个真正的空结果报告,而不是一句用来搪塞的 caveat:在这个容量点上,训练更久本身不是恢复插值起始质量损失的杠杆——本文观察到的恢复(表 8)来自容量与正则选择,而不是训练时长。

以转录文本为条件:跨架构的注意力机制

动机

会话 chunk 的信息量并不相等,因此固定的、内容无关的池化会随长度增长稀释或丢弃信号。基于注意力的池化通过学习逐 chunk 的相关性权重修正这一点。因为跑一次完整 DeepFM 迭代很贵,作者先在更小更便宜的 two-tower 架构上做轻量探索;一旦确认"在 chunk 序列上做注意力"是最高杠杆的那个变量,就把它搬进 DeepFM。

论文的 Figure 1 用一张图并置了两种注意力机制。原文该图为矢量绘制、无法从 PDF 中抽出位图,此处按原图语义重绘:

graph TD
    subgraph A["(a) DeepFM:共享 query 的 attention pooling"]
        A1["u₁"] --> AS["scorer → masked softmax"]
        A2["u₂"] --> AS
        A3["u₃"] --> AS
        A4["u₄"] --> AS
        AQ["shared learned query"] --> AS
        AS --> AP["pooled c_u"]
        AP --> AD["DNN tower input<br/>(与表格特征拼接)"]
    end
    subgraph B["(b) Two-tower:候选条件化的 target attention"]
        B1["u₁"] --> BS["scorer"]
        B2["u₂"] --> BS
        B3["u₃"] --> BS
        B4["u₄"] --> BS
        BQ["query = [item p embed. ;<br/>target-enc. feats]"] --> BS
        BS --> BD["Δ_p(transcript)"]
        BD --> BR["对零初始化 base logit<br/>的加性修正"]
    end

Figure 1(重绘):两种机制都用一个学习出的、逐 chunk 的相关性权重替代固定池化,作用在同一条 per-utterance 嵌入序列 $(u_1, \ldots, u_4)$ 上。(a) DeepFM 使用一个共享 query,产出单个 pooled 向量,只喂给 DNN 塔;(b) two-tower 的 query 以具体候选商品 $p$ 为条件,因此不同候选会对同一通电话给出不同的注意力,产出的是对 cold-safe base logit 的加性修正。

Two-Tower:残差 base 路径 + target-attention 修正

架构:cold-safe 的 base 路径 + 零初始化的修正路径

设计约束正是本文硬性非回归要求的来源:模型必须在通话开始、还没有任何转录信号时保持可信,同时在转录信号到来后激进地利用它。作者用结构而不是仅靠损失加权来满足它:

  • Base 路径(cold-safe):标准 two-tower 打分器——一个覆盖表格、行为、target-encoded 特征的 user/context 塔,一个覆盖候选商品表示的 item 塔,融合成 base logit。这条路径不接收任何转录序列输入,因此它在 chunk_num = 0 时行为也有定义。
  • 修正路径(转录感知、零初始化):第二个 head 读会话 chunk 序列,输出一个加性修正:

$$\mathrm{logit} = \mathrm{logit}_{\mathrm{base}} + \Delta(\mathrm{transcript}) \tag{6}$$

修正 head 的最后一层零初始化,因此初始化时模型精确等于它的 cold-safe base——转录路径只能在训练中挣得自己的影响力。后续每一项架构增补都被要求保持这个契约(用一个单元测试验证:初始化时 $\mathrm{logit} \equiv \mathrm{logit}_{\mathrm{base}}$)。这是一个值得直接借鉴的工程范式。

  • 修正路径中的 target attention:影响最大的增补——DIN 式 target attention,其中在转录 chunk 上做注意力的 query 是候选商品的学习嵌入与该行 per-product target-encoded 特征的拼接。于是每个候选商品都会注意到同一通电话的不同部分——这正是 pairwise-binary 打分的天然契合点:同一个上下文要为每个候选各打一次分。
  • 面向冷启的损失塑形:两个同时施加的杠杆——(i) 冷样本权重再平衡,把 chunk_num = 0 的行按下式上调权重

$$w_{\mathrm{cold}} = 1 + \alpha(r - 1), \quad \alpha = 0.5 \tag{7}$$

其中 $r$ 是 warm-to-cold 的行数比;(ii) 只在冷样本行上对 base logit 计算一个辅助损失($\lambda = 0.5$),直接训练 base 路径,而不是让修正路径吸走全部梯度。

消融战役:什么真正移动了指标

整个战役训练了 25 个变体,论文只报告里程碑。每一行后继配置都要复现之后才被采纳,因为这份数据上的 run-to-run 种子噪声在 CL/Top-1 上约 1pp。两个影响最大的实验:

  1. 修正路径中的 target attention:Top-1 +3.3pp。加入 DIN 式注意力 head 把 Top-1 从 0.5822 抬到 0.6152——整个战役中最大的单杠杆收益,在三次独立运行中的两次复现成功,同时 ER 保持不变。不以候选为条件的池化(均匀或按时近加权,即他们的 v14 base)会留下这块空间,因为一通客服电话通常讨论好几个需求,单个 pooled 摘要会把它们糊在一起。
  2. 冷启损失杠杆:找回通话开始时的质量。激进的转录建模倾向于侵蚀通话开始(CL)的质量——梯度集中到修正路径。上面两个冷启杠杆把 CL 恢复到与 base 架构持平(0.4546 vs 0.4575),同时保留注意力带来的收益,产生最佳平衡配置 TT-full(战役中最好的 ER 与 GZ)。只做全局重加权是不够的。

TT-attn 与 TT-base 的差别是 target-attention head 加冷启杠杆;TT-full 额外在 58% chunk-stride 子采样上训练。这条线在有转录信号之后决定性地打败旧模型(TT-full:ER +2.8pp,GZ +5.2pp),只在通话开始(CL −4.4pp,此时无转录可用)落后。

DeepFM:Attention Pooling

把上面确认的杠杆直接搬进 DeepFM,实现为一个作用在同一条 per-utterance 嵌入序列上的 attention-pooling 层。

Per-utterance 嵌入存储。稠密文本表示用 nomic-embed-text-v1.5(768 维)按 utterance 计算一次而不是按 chunk_num 计算一次,缓存在每个 contact 的一个按时间排序的扁平存储里;一行 (contact, chunk_num) 读取该存储的一个前缀切片。这一个设计消除了朴素重嵌入在这份数据上会带来的 6.19× 冗余嵌入调用。

池化层。给定某个 chunk 上可用的变长 per-utterance 嵌入序列,一个单一学习 query 的注意力头为每条 utterance 打分——打分器是两层 MLP($768 \rightarrow 128$、tanh、$128 \rightarrow 1$),跨位置共享——再用 masked softmax 组合,padding 位置在 softmax 前被打成 $-\infty$。尚无任何 utterance 可用的 contact(chunk_num = 0,转录还不存在)被显式赋一个零向量,而不是对一个空的/全被 mask 的序列做 softmax(那会产生未定义的 NaN 注意力权重)。得到的 pooled 向量只拼接到 DNN 塔的输入上,与表格特征并列;它不进入 FM 或线性组件。

集成。池化器被包在它所替代的 TF-IDF 文本投影器已经使用的同一个调用接口后面,因此周边训练与评估代码无需任何结构性改动——只有文本表示模块本身变了。这是一条很实用的工程纪律。

结果

表 9:架构与文本表示对比,固定群体 CL/ER/GZ 评估器

Configuration CL ER GZ
Legacy model (CatBoost) 0.4985 0.5310 0.5676
TT-base: residual two-tower 0.4575 0.5537 0.6152
TT-attn: + target attention 0.4472 0.5537 0.6113
TT-full: + cold levers, stride 0.4546 0.5585 0.6195
Tabular-only (DeepFM) 0.3426 0.4362 0.4293
DeepFM + TF-IDF/SVD + dense embeddings 0.4678 0.5283 0.5158
DeepFM + Attention pooling (ours) 0.4867 0.5630 0.5943

分析:

  • DeepFM 的 tabular-only vs text-enabled 对比是"文本信号确实重要"的有力证据,它填掉了到 CatBoost 基线(0.4985 / 0.5310 / 0.5676)大部分的 CL/ER/GZ 差距。
  • Attention pooling 走得更远:它是本文第一个在 ER 与 GZ 上直接打败 CatBoost 的配置(0.5630 vs 0.5310;0.5943 vs 0.5676),并且在 CL 上只差 1.2pp。作者称这是本文最强的结果,且与 §7 的损失/正则图景一致。

综合

跨两种架构,深度模型只在 ER 与 GZ 上直接打败旧 CatBoost 模型——那是两个"实时转录确实存在"的评估点——而在 CL 上没有,因为那里还不存在转录(填这个缺口是负采样/正则的杠杆,而非架构的杠杆,见 §7)。

  • Two-tower 通过候选条件化的 chunk 序列注意力拿下 ER/GZ,但在通话开始处只恢复到与自己的 cold-safe base 接近持平;
  • DeepFM 通过同样的底层机制作用在一个结构上不同的基础架构上拿下 ER/GZ,并在 CL 上更接近 CatBoost(差 1.2pp)。

这个 ER/GZ 效应通过两种不同的注意力机制、在两种不同的架构上出现了两次——这正是作者把"以转录文本为条件"(而不是"DeepFM 还是 two-tower")当作证成这次迁移的因素的理由。两个架构互不支配:TT-full 在 GZ 上领先(0.6195 vs DeepFM attention-pooling 配置的 0.5943,比 CatBoost 高 5.2pp)——而 GZ 正是"推荐最可能真正触发"那一刻的指标——作者把这归因于 two-tower 的 target-attention 修正路径,它以候选商品为条件做转录池化,因此恰好在转录最长时收益最大。

推理时的挑战与优化

从提升树集成换到深度推荐器,改变的是服务时时间花在哪里。作者在同一台机器上端到端 benchmark 了两套系统的真实推理路径(只测 base model,不含 calibration),用的是生产 CatBoost artifact 与表 9 的 attention-pooling checkpoint。

两套系统在模型跑起来之前都要付一笔文本特征化成本——而 CatBoost 的那笔大得多。 CatBoost 的模型调用本身很便宜(2.4ms),它 641ms 端到端成本里几乎全是 TF-IDF 向量化。DeepFM 付的是同类成本,但规模小得多,通过一个小 transformer(nomic-embed)文本嵌入完成。

表 10:DeepFM 单请求分步成本(平均,冷请求)

Step CPU GPU % of CPU total
Text embedding (nomic-embed) 59.8ms 34.3ms 48%
Tabular featurization 12.5ms 22.6ms 10%
DeepFM forward pass 52.3ms 56.4ms 42%
Total (end to end) 124.5ms 113.3ms 100%

分析:在 CPU 上,文本嵌入实际是 DeepFM 两笔成本中较大的那笔——文本特征化是两套系统上请求里最贵的单一部分,比任何一个模型自己的打分逻辑都贵。

在无批处理的单请求上,DeepFM 端到端已经比 CatBoost 快(124ms/113ms vs 641ms)——但对一个同时处理很多请求的生产系统来说这是错误的比较维度,而正是在那个维度上局面反转。

批处理才是真正的杠杆

树集成的 predict() 调用本来就能在一遍里几乎免费地为很多行打分,因此 CatBoost 的吞吐几乎随 batch size 线性扩展(batch 64 下 9,954 req/s)。神经网络只有在服务代码真的把请求攒批时才拿到同样的好处——朴素地把每个请求丢到自己的线程上在负载下反而更糟(超过 4 个并发请求后吞吐下降,因为内部并行与线程并行抢同一批核心)。正确攒批后,DeepFM 的吞吐从 batch 1 的 17 req/s 升到 CPU 323、GPU 575(batch 64)——同样的权重上 19× / 34×。

这填掉了大部分而非全部差距:CatBoost 单行成本本质上更便宜。批处理把 DeepFM 从"不可用"带到"生产负载下可用",但不会让 DeepFM 比 CatBoost 更便宜。

批处理降低的是每请求成本,而不是任一单请求的延迟——更大的 batch 让单请求延迟更差而不是更好。一个请求必须等到攒够别的请求填满一个 batch,或者等待计时器到期,才会被打分。

表 11:单请求与 batch-64 的服务成本(无 coalescing) (CL/ER/GZ/PR-AUC 是模型权重的属性,与服务设置无关)

Model Setup P50 Thpt. CL ER GZ PR-AUC
CatBoost, fp32 batch 64 — 9954/s 0.4985 0.5310 0.5676 —
CatBoost, fp32 single req. 641ms — 0.4985 0.5310 0.5676 —
DeepFM, fp32 CPU, single req. 124ms 323/s ᵃ 0.4799 0.5550 0.5918 0.7431
DeepFM, fp32 GPU, single req. 113ms 575/s ᵃ 0.4799 0.5550 0.5918 0.7431
DeepFM, INT8 CPU, single req. 74ms 591/s ᵃ 0.4788 0.5342 0.5989 0.7217

ᵃ batch-64 吞吐上限(req/s),不是真实到达流量下 coalescing 服务器的测量值——见表 12。

表 12:DeepFM 在请求 coalescing 服务器下(最多持有请求 8ms,或攒满 64 个),持续 Poisson 到达流量

Setup P50 P95 Throughput
CPU, coalesced, 150 req/s 288–377ms 455–747ms 82–103 req/s
GPU, coalesced, 150 req/s 116–121ms 168–365ms 99 req/s
GPU, coalesced, 500 req/s 477ms 648ms 201 req/s

分析:在这个中等到达率下,CPU coalescing 服务器的典型延迟(288–377ms)比不攒批、来一个打一个(124ms)还差——因为在等待窗口内到达的请求不够填满一个 batch,请求大多只是坐着等超时。批处理只有在到达量高到能快速填满 batch 时才划算;最大等待时长这个旋钮是在最坏延迟与 batch 大小之间做交换,而作者明确表示"在没有生产流量数据前我们没有默认推荐值"。这是一条非常克制、很有工程诚意的结论。

量化:一个温和的、依赖指标的杠杆,不是免费午餐

INT8 动态量化(32-bit → 8-bit 权重,不重训)把模型文件缩小 75%、把 batch-64 吞吐提高 11%(表 11)——真实但远不及批处理的 19–34×——同时不均匀地移动质量:CL 与 GZ 基本不变甚至略好,但 Early-Reco F1 与 Test PR-AUC 都掉约 2 个点。鉴于收益温和而代价真实,作者默认不采纳;若要用,应对着部署最在乎的那个指标来权衡。

对生产意味着什么

DeepFM 在一个实时系统里以可用的成本与速度是可服务的,但只有在批处理到位的前提下——而大多数推理框架不会自动做这件事;这个成本相对于表 9 的推荐质量收益是值得的。更小或蒸馏过的文本编码器是一个有前景但未测试的杠杆,因为文本嵌入占了 DeepFM CPU 成本的近一半(表 10);GPU 只在吞吐上必需,而不是为了装下模型(batch 64 下峰值 VRAM 仅 1.4GB)。

核心贡献总结

  1. pairwise-binary 问题重构:让任意 item/context 特征都能进模型,无需固定标签空间——这是为"给训练中从未见过的商品打分"(新 SKU、快速增长目录)准备的结构性前提。
  2. 一套负样本分类学与彻底的负采样调查:显式/隐式 × 硬/软的二维词汇,加上纯负 contact 这个群体级概念;chunk 粒度采样优于 contact 粒度;结构化采样能锐化 PR-AUC 却不必然改善 contact 级决策;$K \in [2,4]$ 是实用区间。
  3. 批次组成 + InfoNCE 对比目标:证明二者可加而非冗余——单独控制 minibatch 组成已经是独立杠杆,但叠加对比项后每个 contact 级指标都更好。
  4. Double-descent 刻画:36 配置网格证明 147.8M 参数基线的排序质量不来自容量;72× 更小的模型 + 噪声注入是整个网格里唯一打败 CatBoost CL 的配置;并给出"训练更久不能恢复"的空结果。
  5. 跨架构的注意力池化:DeepFM 共享 query 池化 + two-tower DIN 式候选条件 target attention,是本文唯一直接打败 CatBoost(ER/GZ)而非只是缩小差距的干预。
  6. cold-safe 零初始化残差契约:$\mathrm{logit} = \mathrm{logit}_{\mathrm{base}} + \Delta(\mathrm{transcript})$ 加单元测试保证初始化时等于 base,是把"新信号路径不能伤害既有质量"写进架构而非损失权重的范式。
  7. 推理时优化的完整成本表:文本特征化是两套系统最贵的一步;批处理是 19–34× 的真正杠杆但会恶化单请求延迟;INT8 量化收益温和且指标依赖,默认不采纳。

讨论与局限性

值得借鉴的设计

  • 固定群体评估器:所有实验都在同一批 16,709 个正例 contact 上、用同一套 micro-F1 定义评估,让"有没有回退"在每一步都有无歧义答案。对任何有非回归约束的线上迁移,这是最该先建的基础设施。
  • 零初始化 + 单元测试的契约式架构:把安全性写进结构而非损失权重,并用测试固化,避免后续每一次架构增补悄悄破坏冷启行为。
  • 复现门槛纪律:因为 run-to-run 种子噪声在 CL/Top-1 上约 1pp,每个被采纳的里程碑都要求复现。25 个 two-tower 变体中只报告里程碑,也避免了"挑最好那次 run"的陷阱。
  • 对代理指标的怀疑:val-test PR-AUC 间隙单调收窄却不带动 CL/ER/GZ,结构化负采样提升 PR-AUC 却掉 CL——这两处都被明确写成"代理指标改善不等于目标指标改善"。
  • 空结果与不利结果被如实报告:200-epoch 无恢复、没有任何配置能三指标同时清过 CatBoost、INT8 不采纳、coalescing 在中等流量下反而更慢——这在工业论文里相当少见。

局限与争议

  1. 没有线上 A/B。这是最大的局限。全文所有比较都是离线的(虽然对着已部署基线、用生产群体),新模型尚未有线上实验结果,也就无法验证离线 CL/ER/GZ 提升能否转化为真实业务收益。论文标题说的是 "Migrating",但严格讲这是一次迁移前的可行性论证,而非迁移完成后的复盘。
  2. 技术全部是成熟手段的组合。pairwise-binary 重构、负采样、InfoNCE、DeepFM、two-tower、DIN target attention、attention pooling、double descent、INT8 量化——没有一项是本文提出的。论文的价值在组合方式、评估纪律与工程细节,不在方法新颖性。
  3. 不含任何公开数据集实验。全部结果基于 Intuit 内部生产数据(90.4 万训练行 / 36.0 万时序 holdout / 9 个商品),外部无法复现,也无法与其它工作横向比较。
  4. 规模很小。9 个商品(计划扩到低百量级),这让 two-tower 的 retrieval 优势"潜在而未承重",也意味着结论未必外推到大目录场景。反过来说,论文自己承认 CL 上的落后本质上来自"通话开始时没有转录",这是场景内生的,换个规模未必同形。
  5. CatBoost 的 CL 优势未被真正攻克。除了 §7 那个孤立的 17.2M + 噪声注入配置,attention-pooling 主方案在 CL 上仍差 1.2pp。而 CL 是"最早可能给出的推荐",在业务上未必不重要。
  6. 成本增加是确定的。DeepFM 即使正确批处理后(323/575 req/s)仍远不及 CatBoost 的 9,954 req/s;论文明确说批处理"没有让 DeepFM 比 CatBoost 更便宜"。这次迁移是拿显著更高的服务成本,换 ER/GZ 上 3–5pp 的 F1。
  7. 公平性未审计。作者在 Ethical Considerations 里主动承认:没有跨客户分群做公平性审计,也没有做任何显式的偏差纠正,把它作为一个开放局限而非已解决的问题陈述。
  8. 表 11 与表 9 数值不一致。表 11 里 DeepFM 的 CL/ER/GZ 是 0.4799/0.5550/0.5918,而表 9 的 attention-pooling 行是 0.4867/0.5630/0.5943。论文说服务 benchmark 用的就是表 9 的 checkpoint("base model only, no calibration"),这个差异未被解释。

工业落地价值

这套系统服务的是 Intuit 的实时客服会话推荐:客服与客户通话时,系统按每 20 条 utterance 增量打分,把最相关的商品推给客服,再由下游 DynaPitch 系统转成实时话术。它是多个生态增长与留存计划的骨干,喂给的升级推荐支撑约 3.6 亿美元量级的营收组合。论文给出了完整的服务成本剖面(单请求 124.5ms CPU / 113.3ms GPU,coalescing 下 P50 116–477ms,峰值 VRAM 1.4GB),这类数字在学术论文里几乎看不到。

但必须强调:新模型尚未上线 A/B,噪声注入与更小模型尺寸被"采纳为下一次生产迭代的设计杠杆",也就是说这仍是迁移路上的一站。

作者自己的定位

论文结尾那段写得很克制,也很值得引用:这是一份生产迁移案例研究,不是 benchmark 结果;上面这些杠杆是在一个实时、受非回归约束的系统里成立的——那正是大多数要从树模型迁走的实践者真正身处的环境,而不是 benchmark 规模的环境。一个面临同样迁移的实践者不会在这里找到一个普适赢家,而作者认为这恰恰是诚实且有用的发现。