跳转至

数据稀疏与样本穿越

样本稀疏:系统性专题讲解

一、什么是样本稀疏,为什么会发生

样本稀疏(Data Sparsity,DS)是指在训练某个目标任务时,正样本的绝对数量或相对比例严重不足,导致模型无法从数据中学到足够可靠的规律。

在推荐系统中,用户的行为本身就呈现出漏斗状的稀疏结构:

\[\text{曝光量} \gg \text{点击量} \gg \text{转化量} \gg \text{深度转化量}\]

以 CVR(转化率)预估任务为例,模型只能在"点击样本"中训练,但点击样本本身已经是曝光的一小部分,转化样本则更是点击中的少数。越往漏斗深处走,正样本就越稀少。ESMM 论文中正是在这个场景下明确提出了 DS 问题。

二、如何判断当前场景存在样本稀疏

判断的核心指标是正样本率,即正样本数量占总训练样本的比例:

\[r = \frac{N_{\text{正样本}}}{N_{\text{正样本}} + N_{\text{负样本}}}\]

工业界通常以如下经验范围作为参考:

\(r > 10\%\) 时,正负样本基本均衡,样本稀疏问题不突出。\(r\)\(1\%\)\(10\%\) 之间时,存在轻度不均衡,模型有一定学习压力,但通常不会严重影响效果。\(r\)\(0.1\%\)\(1\%\) 之间时,属于中度稀疏,模型在该任务上的收敛质量会明显下降,需要专项处理。\(r < 0.1\%\) 时,属于严重稀疏,模型几乎无法从正样本中学到有效信号,任何不加干预的训练结果都不可信。

除了看比例,还需要看正样本的绝对数量。即使正样本率看起来还好,若正样本的绝对量只有几百或几千条,模型同样无法泛化。两个条件需要同时满足:比例合理,且绝对量足够(通常建议单任务正样本不低于数万量级)。

实践中,以下现象往往是样本稀疏的外在信号:模型在训练集上 Loss 下降缓慢或震荡;验证集 AUC-PR 显著低于 ROC-AUC;模型对几乎所有样本输出接近于 0 的概率值;线上上线后该任务塔对整体排序的贡献极弱。

三、解决样本稀疏的方案体系

解决思路可以沿两条主线展开:一是在数据层面干预,直接调整样本的分布;二是在模型与训练层面干预,让模型在不改变数据的情况下更好地利用稀疏信号。

第一类:采样策略——在数据层面调整比例

最直接的方式是人为调整正负样本的比例。负样本欠采样(Undersampling)是从负样本中随机丢弃一部分,使比例接近均衡。这个方法实现简单,但代价是主动丢失了真实存在的数据信息,且会使模型学到的概率分布偏离真实世界,预测值需要事后通过校准修正。正样本过采样(Oversampling)则是将现有正样本复制多份以扩充数量,但复制不产生新知识,容易导致模型对这部分样本过拟合,泛化能力反而下降。

这两种方法是传统做法,ESMM 论文将其列为 DS 问题的对比基线,正是因为它们都只是在有限的样本空间内部做调整,没有从根本上解决信息不足的问题。

第二类:损失函数调整——让稀疏信号得到更多关注

不改变数据,而是改变模型对不同样本的学习权重。正样本加权 BCE(Weighted Binary Cross-Entropy)是最简单可控的方式,直接对正样本的损失乘以一个权重系数 \(w^+\)

\[\mathcal{L} = -\left[ w^+ \cdot y \log \hat{y} + (1-y) \log(1-\hat{y}) \right]\]

\(w^+\) 越大,模型预测错误一个正样本所受到的惩罚越重,从而迫使模型认真对待稀少的正样本。这是工程上应该优先尝试的手段,因为它几乎没有副作用,且调参直觉清晰。

Focal Loss 在此基础上更进一步,它不是固定地对正样本加权,而是根据预测置信度动态调整权重:预测越确定的样本(无论正负)权重越低,难分类的样本权重越高。这使模型把更多精力集中在真正难以区分的样本上,在正样本率低于 \(1\%\) 的极端稀疏场景下效果突出,但训练稳定性略弱于简单加权。

第三类:扩大训练空间——ESMM 的核心思路

前两类方法的共同局限在于:它们都局限在原有的训练样本空间内做文章。ESMM 提出了一条根本不同的路径——不在小空间内调比例,而是直接把训练空间扩大

ESMM 将 CTR 预估塔的训练从"点击样本空间"扩展到"全量曝光样本空间"。全量曝光样本的规模远大于点击样本,正负样本的绝对数量都大幅增加,模型可以从中学到更丰富的用户行为规律。CVR 塔则通过 CTCVR 联合概率的监督信号间接受益于这个扩大的空间,绕开了"只有点击样本才有 CVR 标签"的限制。

这个思路的本质是:与其在稀缺的数据里反复挖掘,不如从架构层面找到更丰富的监督来源。

第四类:知识迁移与辅助任务——借助相关任务的信号

当某个任务的正样本实在太少,无法单独有效训练时,可以借助相关任务的监督信号进行迁移。在 MTL 框架中,这体现为设计辅助任务(Auxiliary Task):用样本更丰富的上层目标(如点击)为样本稀疏的下层目标(如深度转化)提供梯度信号,通过共享底层表征,使下层目标的特征提取质量得到间接提升。这本质上是一种弱监督学习:目标任务的信号虽少,但可以借助结构上相关的任务来填补信息缺口。

四、方案选择的优先级建议

面对具体的样本稀疏问题,建议按照从低成本到高成本的顺序逐步尝试。首先确认正样本率和绝对数量,判断稀疏的严重程度。若属于轻中度稀疏,优先尝试正样本加权 BCE,成本最低且效果稳定。若属于严重稀疏且正样本绝对量也不足,则需要考虑扩大训练空间(如 ESMM 的思路)或引入辅助任务。Focal Loss 和采样策略可作为补充手段,在前述方法效果有限时配合使用。

你们当前的延迟消费任务(delay_clickdelay_dur),正样本率大约落在哪个区间?这将直接决定哪条路径最值得优先投入。

样本穿越

这是一个在工业推荐系统中极其重要、却又容易被忽视的问题。我们从最根本的直觉出发,一层一层建立起完整的理解。

一、核心直觉:时间的单向性

在现实世界中,时间只能向前流动。用户在 \(t\) 时刻做出的决策,只能依赖他在 \(t\) 时刻之前所掌握的信息,绝不可能"看到未来"。

推荐系统的模型,本质上是在模拟这个决策过程。它在训练时学到的规律,应该完全基于"在预测那一刻之前已经发生的事情"。样本穿越的定义就是:在构造训练样本时,不小心把"未来的信息"混入了当前时刻的特征或标签中,导致模型在训练时见过了它在推理时本不该知道的东西。

这就造成了训练与推理之间的根本性不一致:训练时模型"开了天眼",推理时它却只能看到正常的信息,于是离线指标虚高,上线后效果大幅跳水。

二、样本穿越的两种类型

第一类:特征穿越(Feature Leakage)。

设某条训练样本的请求发生时刻为 \(t_0\)。正确的做法是,该样本所使用的一切特征,必须只能依赖 \((-\infty,\ t_0)\) 范围内的数据。

特征穿越的发生,就是特征的计算实际上使用了 \(t_0\) 之后的数据。用一个直接的例子说明:

\[f_{\text{正确}} = \text{用户过去7天点击次数,统计截止时间} < t_0\]
\[f_{\text{错误}} = \text{用户过去7天点击次数,统计截止时间} = t_0 + \Delta t\]

两者的差别只是统计截止时间不同,但后者把 \([t_0,\ t_0+\Delta t]\) 这段时间内发生的点击行为也算进去了。模型训练时用了这个"含有未来信息的特征",而推理时 \(t_0\) 时刻根本不存在这段数据——这就是特征穿越。

第二类:标签穿越(Label Leakage)。

这是特征与标签在时间轴上发生了重叠污染。以延迟消费任务为例,定义如下:

\[\text{标签窗口}:[t_0,\ t_0 + 7\text{days}]\]
\[\text{标签}:y = \mathbf{1}[\text{用户在此窗口内发生消费}]\]

这个设定本身是合理的,问题出在特征上。如果某个特征的定义是"用户最近7天的消费时长",且其计算范围恰好也覆盖了 \([t_0,\ t_0+7\text{days}]\),那么这个特征就把标签窗口内发生的行为吸收进来了——模型通过特征间接"看到"了标签,泄露就此发生。

正确的约束非常简单,一句话即可表达:

\[\text{所有特征的计算截止时间} \leq t_0\]

只要这条约束被严格执行,标签穿越就不会发生。

三、如何判断某个场景是否存在样本穿越

判断的核心工具只有一个:在脑海中严格还原"请求发生那一刻",逐一审问每一个特征和标签。

具体来说,可以按照以下三个追问来检验。

第一个追问:这个特征的统计时间窗口,有没有跨越样本的发生时刻? 比如"过去N天的行为统计",这个"过去"的基准时间点是什么?是样本发生时刻,还是数据处理时刻?如果是数据处理时刻,而数据处理发生在样本发生之后,就存在穿越风险。

第二个追问:这个特征在推理时能不能实时拿到? 有些特征在离线构造样本时很容易计算,但在线上推理时根本不存在。比如"这篇文章最终的总点击量",离线可以统计,但在线上请求到来时,文章还没有经历那么多点击,这个特征本质上是一个来自未来的全局统计量。

第三个追问:标签的时间窗口与特征的时间窗口是否存在重叠? 这是延迟标签任务中最容易踩的坑。你需要在时间轴上把两个窗口画出来,肉眼检查是否有交叉。

四、几个高频的易错场景

场景一:实时特征的"未来均值"问题。 在大规模推荐中,Item 侧的统计特征(如点击率、转化率)通常是滚动计算的。如果使用的是全量历史数据计算出来的"全局 CTR",而不是"截至请求时刻的历史 CTR",就发生了穿越,因为全局 CTR 包含了请求之后发生的点击行为。

场景二:Join 操作的时间戳不对齐。 在构造训练样本时,经常需要把用户行为日志和用户画像表做 Join。如果用户画像表没有时间版本管理,使用的是"当前最新的画像"而不是"样本发生时刻的画像",就会引入穿越。一个用户可能在样本发生后才修改了兴趣标签,但训练时你已经用了修改后的版本。

场景三:你们正在做的延迟目标任务。 delay_click 这类目标,标签本身就是在请求发生之后的一段时间窗口内回填的。这是合理的。但要特别注意:用于训练的任何特征,都不能包含标签窗口内发生的信息。换句话说,如果标签窗口是 \([t_0,\ t_0+7\text{days}]\),那么所有特征的计算截止时间必须严格 \(\leq t_0\)

五、如何在实践中系统地防御

最有效的防御机制是建立一套基于事件时间(Event Time)而非处理时间(Processing Time)的特征生成体系

具体来说,每一条训练样本都应该携带一个严格的"特征截止时间戳",特征平台在计算任何统计特征时,都以这个时间戳为上界,绝不使用这个时间戳之后的数据。这在工业界通常被称为 Point-in-Time Correct Feature Engineering,是防止样本穿越的工程基石。

除此之外,有一个非常实用的诊断手段:如果你的模型离线指标异常地好,好得让人不敢相信,这往往是样本穿越的强烈信号。 真实世界的推荐任务通常很难,AUC 能到 0.75 已经不错。如果突然出现 0.95 的 AUC,第一反应不应该是庆祝,而应该是立刻去检查数据管道。

你们目前在做的延迟消费任务,有没有做过特征截止时间戳的对齐检查?这是我最想先确认的一个点。