想象你是一家美食街的运营经理,想在店门口立一块电子屏,预测每一位路过的顾客“进店之后会不会掏钱买东西”。最直接的做法是:观察那些已经进店的人,统计他们中有多少买了单,然后训练一个“进店顾客购买预测器”。可你很快发现问题——你真正要预测的是门口那些路过的、形形色色的人,而你的预测器只见过“愿意进店”的那一小群人。愿意进店的顾客本来就对这个店感兴趣,他们的特征和路过的普通人大不一样。用偏科的数据训练出的模型,一到真实街头就失灵了——这在推荐系统里被称为样本选择偏差(Sample Selection Bias, SSB)。 更麻烦的是,愿意进店的人本来就不多,其中掏钱的就更少了,数据稀疏得让模型无从下手。那该怎么办?一位聪明的店长想出了一个办法:既然“进门后掏钱”这件事数不出来,那就改记两本账——“多少人进店”和“多少人进店且掏钱”。前者乘以后者恰好推出“进门的人里谁会掏钱”。这就是 ESMM(Entire Space Multi-task Model,全空间多任务模型) 的核心思想:在完整的曝光空间上,同时学习点击率和点击转化率,用数学的乘法关系化解样本选择偏差,用共享的特征表示化解数据稀疏。

岗位关联

理解 ESMM,你就掌握了推荐与广告系统中“漏斗行为建模”的标准解法,以及用多任务学习解决训练-部署分布不一致的通用思路——这是设计高精度转化预估系统的必备能力。

目录
  1. 可视化演示
  2. 转化预估的困境:样本选择偏差与数据稀疏
  3. 全空间建模思路:从点击空间到曝光空间
  4. ESMM双塔架构:一本共享的顾客档案
  5. CTCVR概率乘法:打开全空间之门的钥匙
  6. 联合损失训练:双塔之间的对账机制
  7. 对比独立CVR:全空间为何更准
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示ESMM模型的核心架构:共享Embedding层驱动CTR/CVR双塔,通过pCTR×pCVR实现全空间CTCVR预估,解决样本选择偏差问题。

本节配有交互动效(进入视口后自动加载,约 3KB)。

ESMM架构 共享Embedding层连接CTR和CVR两个塔 CVR塔仅在点击样本上训练,CTCVR在全空间评估 pCTCVR = pCTR × pCVR ESMM通过全概率公式将CVR预估转化为全空间问题

转化预估的困境:样本选择偏差与数据稀疏

美食街的经理发现了一个残酷的事实:他在店里观察“进店顾客的购买行为”,用这些数据训练的模型,却要站在大街上预测“每一位路过的人进店后会不会买”。那些愿意进店的人,本来就是被招牌、香味吸引过来的潜在顾客,他们和街上面无表情匆匆走过的路人有着系统性的差异。模型从没见过“不感兴趣的人”长什么样,却要判断“不感兴趣的人”会不会买——这显然是在逼它瞎猜。

在推荐系统中,这个故事每天都在上演。一个用户看到商品卡片(曝光),点击进入详情页(点击),最后下单付款(转化)。传统 CVR 模型把“点击样本”当作训练集:只有那些点击了商品的人,才进入样本池。然而线上推断时,模型要对每一个曝光样本输出预估转化率。训练时的样本来自 点击空间,预测时的样本来自 曝光空间,两个空间的人群分布天然不同,模型学到的规律自然容易失真。

这种错位的本质是什么?点击的人群偏好高活跃、强意图的特征,而曝光人群里混杂了大量低意图用户。除此之外,转化样本本身就极其稀少:一万个曝光里可能只有几十个点击、几个转化。稀少的正样本让模型难以收敛,这就是与样本选择偏差并称“两大顽疾”的数据稀疏性问题。二者叠加,独立训练的 CVR 模型常常在离线指标上虚高、在线上效果上缩水。

回到美食街:只盯着进店顾客记账,永远看不懂整条街的生意。要破解这个困境,经理必须换一种记账方式——这正是全空间建模的出发点。

Pclick(x)≠Pexpo(x)\displaystyle P_{click}(\mathbf{x}) \neq P_{expo}(\mathbf{x})
通俗理解:模型“平时见到的人”(点击样本)和“真正要判断的人”(曝光样本)长相不同,这就是所有偏差的根源。
  1. 两个分布$P_{click}(\mathbf{x})$ 是点击样本的特征分布,$P_{expo}(\mathbf{x})$ 是全部曝光样本的特征分布。
    Pclick(x)≠Pexpo(x)\displaystyle P_{click}(\mathbf{x}) \neq P_{expo}(\mathbf{x})
  2. 分布不相等点击样本只是曝光样本里被“筛选”过的一小部分,二者的特征分布天然不同。
    点击样本: 高意图用户占比大曝光样本: 低意图用户混杂\displaystyle \text{点击样本: 高意图用户占比大} \quad \text{曝光样本: 低意图用户混杂}
  3. 错位后果模型在点击空间上学到的规律,拿到曝光空间上去用,精度大幅下降。
    训练: x∼Pclick部署: x∼Pexpo\displaystyle \text{训练: } \mathbf{x} \sim P_{click} \quad \text{部署: } \mathbf{x} \sim P_{expo}

全空间建模思路:从点击空间到曝光空间

经理决定做一次彻底的改革:把账本从“进店顾客”扩展到“所有路过的顾客”。可新的麻烦立刻出现了——一个没有进店的人,你永远无法知道他在店里会不会掏钱。他可能今天只是赶时间没进来,但他其实是这家店的忠实买家;他也可能压根看不上这家店。这个“潜在购买”标签,在统计上根本观测不到。

在数学上,这种“观测遮蔽”非常清晰。设 $y$ 表示用户是否点击,$z$ 表示用户是否转化(购买),那么模型真正能观测到的转化信号是 $y \cdot z$:只有当 $y=1$ 时,$z$ 才有机会被记录;当 $y=0$ 时,$y \cdot z$ 永远等于 0,但我们不知道背后的 $z$ 到底是 0 还是 1。这就是缺失标签问题(Missing Not At Random, MNAR)——标签缺失本身就和用户行为相关。

明白了这个障碍,思路反而清晰了:既然 $z$ 在全空间上不可观测,那不如换个目标。在全部曝光样本上,有两个量是完全可观测的:一是用户会不会点击($y$),二是用户会不会点击且转化($y \cdot z$)。如果能通过这两个量把 CVR 反推出来,就能绕开标签遮蔽的陷阱。ESMM 正是沿着这条“借道可观测概率”的思路设计出来的。

经理终于明白:与其纠结“没进店的人会不会买”,不如把“进店的人里有多少真买了”转化成“路过的人里有多少进店且买了”——这个数,站在大街上就能数得清。

zobs=y⋅z\displaystyle z_{obs} = y \cdot z
通俗理解:顾客没进店,我们就看不到他的“购买小票”。观测到的购买行为,是被“点击”这扇门过滤后留下的记录。
  1. 定义变量$y=1$ 表示用户点击,$z=1$ 表示用户转化,$z_{obs}$ 表示系统里能观测到的转化信号。
    y∈{0,1},z∈{0,1},zobs∈{0,1}\displaystyle y \in \{0,1\}, \quad z \in \{0,1\}, \quad z_{obs} \in \{0,1\}
  2. 乘法遮蔽$z_{obs} = y \cdot z$,只有 $y=1$ 时 $z_{obs}$ 才可能为 1。
    y=0⇒zobs=0,y=1⇒zobs=z\displaystyle y=0 \Rightarrow z_{obs}=0, \quad y=1 \Rightarrow z_{obs}=z
  3. 不可直接监督未点击样本的 $z_{obs}=0$ 不代表真实 $z=0$,因此不能拿它当负样本训练 CVR 分类器。
    E[z∣y=0]≠0\displaystyle \mathbb{E}[z \mid y=0] \neq 0

ESMM双塔架构:一本共享的顾客档案

经理给团队分了两个小组:A 组负责预测“路人进店的概率”,B 组负责预测“进店顾客购买的概率”。两个小组没有各干各的,而是共用同一本“顾客偏好档案”——每位顾客的兴趣、消费能力、历史偏好都浓缩在这本档案里。A 组从档案里看“是否容易被吸引进门”,B 组从档案里看“进门后是否愿意掏钱”。档案是共用的,但各自得出的判断不同。

这本“顾客档案”就是 ESMM 中的共享 Embedding 层。模型先用一个 Embedding 网络把高维稀疏的用户特征、商品特征压缩成稠密向量 $e(\mathbf{x})$,然后兵分两路:CTR 塔接收 $e(\mathbf{x})$ 输出点击率 $\hat{p}_{CTR}$,CVR 塔接收 $e(\mathbf{x})$ 输出点击后的转化率 $\hat{p}_{CVR}$。两个塔都是多层感知机,最后一层接 sigmoid 函数把输出压到 0-1 之间作为概率。

关键的设计细节在于:CVR 塔没有独立的转化标签作为直接监督。它的“老师”不是真实转化标签 $z$,而是下一节要讲的乘积约束——CTR 塔的输出乘上 CVR 塔的输出,去逼近真实的全空间标签。这种“间接监督”让 CVR 塔在训练时始终与 CTR 塔协同进化。同时,CTR 任务样本量远大于 CVR,共享 Embedding 让 CVR 塔白拿到了 CTR 任务学到的丰富特征表示,极大缓解了数据稀疏。

B 组没有自己的“购买小票”作为直接功课,只能靠 A 组的进门率和自己判断相乘后与店里的实际流水对账——这套“对账机制”,正是 ESMM 的数学灵魂。

p^CTR(x)=σ(fCTR(e(x))),p^CVR(x)=σ(fCVR(e(x)))\displaystyle \hat{p}_{CTR}(\mathbf{x}) = \sigma\big(f_{CTR}(e(\mathbf{x}))\big), \quad \hat{p}_{CVR}(\mathbf{x}) = \sigma\big(f_{CVR}(e(\mathbf{x}))\big)
通俗理解:CTR 塔和 CVR 塔就像两位共用同一本顾客档案的分析师,笔记相同,但各自问的问题不同。
  1. 共享映射$e(\mathbf{x})$ 是共享 Embedding 层,把原始特征 $\mathbf{x}$ 映射为稠密向量。
    e:Rd→Rk,x↦e(x)\displaystyle e: \mathbb{R}^{d} \to \mathbb{R}^{k}, \quad \mathbf{x} \mapsto e(\mathbf{x})
  2. 双塔各自预测$f_{CTR}$ 与 $f_{CVR}$ 是两个独立的神经网络塔,从同一向量出发、各算各的概率。
    fCTR(e(x))∈R,fCVR(e(x))∈R\displaystyle f_{CTR}(e(\mathbf{x})) \in \mathbb{R}, \quad f_{CVR}(e(\mathbf{x})) \in \mathbb{R}
  3. Sigmoid 压缩$\sigma(\cdot)$ 把任意实数映射到 $(0,1)$ 区间,使其可解释为概率。
    σ(t)=11+e−t∈(0,1)\displaystyle \sigma(t) = \frac{1}{1 + e^{-t}} \in (0,1)

CTCVR概率乘法:打开全空间之门的钥匙

老板听说了经理的困境,一句话点醒了他:“进门买东西的概率,不就是'进门概率'乘以'进门后掏钱概率'吗?你与其在店里数'进店顾客谁买',不如站在大街上数'谁进店且买了'——这个数当场就能数出来!”经理恍然大悟:虽然“进店后购买”在没进店的人身上不可观测,但“进店且购买”在所有人身上都是可观测的——因为只要真的发生了购买,系统一定会记录。

在曝光空间上,ESMM 定义了三个概率:点击率 $\mathrm{pCTR}(\mathbf{x}) = P(y=1 \mid \mathbf{x})$,点击后转化率 $\mathrm{pCVR}(\mathbf{x}) = P(z=1 \mid y=1, \mathbf{x})$,以及点击且转化率 $\mathrm{pCTCVR}(\mathbf{x}) = P(y=1, z=1 \mid \mathbf{x})$。根据条件概率的链式法则,三者存在恒等式:$\mathrm{pCTCVR} = \mathrm{pCTR} \times \mathrm{pCVR}$。这个等式不需要任何独立性假设——只要“转化必然发生在点击之后”这一事实成立,它就如 1+1=2 一样可靠。

这个等式之所以关键,是因为它把“不可观测的 $\mathrm{pCVR}$”变成了“两个可观测量的比值”:$\mathrm{pCVR} = \mathrm{pCTCVR} \div \mathrm{pCTR}$。在全部曝光样本上,CTR 的标签是 $y$,CTCVR 的标签是 $y \cdot z$,两者都在系统日志里躺着,随时可以拿来训练。既然训练时监督的是乘积 $\hat{p}_{CTR} \times \hat{p}_{CVR}$ 与真实标签的一致性,CVR 塔就被“间接地”在全空间上训练了——这就是全空间建模的数学根基。

有了这把钥匙,经理终于可以理直气壮地站在大街上记账了。接下来要解决的,是怎么让模型“同时看好两本账”。

pCTCVR(x)=P(y=1, z=1∣x)=P(y=1∣x)×P(z=1∣y=1, x)=pCTR(x)×pCVR(x)\displaystyle \mathrm{pCTCVR}(\mathbf{x}) = P(y=1,\, z=1 \mid \mathbf{x}) = P(y=1 \mid \mathbf{x}) \times P(z=1 \mid y=1,\, \mathbf{x}) = \mathrm{pCTR}(\mathbf{x}) \times \mathrm{pCVR}(\mathbf{x})
通俗理解:“在大街上数到的人”等于“进店的人”乘以“进店后掏钱的比例”。前两件事可观测,第三件事就能被反推出来。
  1. 联合事件$y=1, z=1$ 表示用户既点击又转化,这是曝光样本上可直接观测的事件。
    P(y=1, z=1∣x)\displaystyle P(y=1,\, z=1 \mid \mathbf{x})
  2. 链式法则$P(A \cap B) = P(A) \times P(B \mid A)$,其中 $A$ 是点击、$B$ 是点击后转化。
    P(y=1∣x)×P(z=1∣y=1,x)\displaystyle P(y=1 \mid \mathbf{x}) \times P(z=1 \mid y=1, \mathbf{x})
  3. 记法缩写两个因子分别记作 $\mathrm{pCTR}$ 与 $\mathrm{pCVR}$,乘积即 $\mathrm{pCTCVR}$。
    pCTCVR(x)=pCTR(x)×pCVR(x)\displaystyle \mathrm{pCTCVR}(\mathbf{x}) = \mathrm{pCTR}(\mathbf{x}) \times \mathrm{pCVR}(\mathbf{x})

联合损失训练:双塔之间的对账机制

新方案落地后,小组的任务变成每天对账:A 组负责对“进门率”这本账,B 组没有独立的账可对,但老板定了规矩——每天的销售流水(实际点击且转化的人数)必须同时等于 A 组的进门率乘以 B 组的转化率。对不上账,两个组都要找原因:是 A 组把进门率估高了?还是 B 组把掏钱概率估偏了?这种“互相牵制”的机制,保证了任何一本账都不会离谱。

ESMM 的训练目标正是这样设计的。总损失由两项组成:第一项 $L_{CTR}$ 是 CTR 塔的交叉熵损失,拿点击标签 $y$ 与 $\hat{p}_{CTR}$ 比较;第二项 $L_{CTCVR}$ 是乘积塔的交叉熵损失,拿组合标签 $y \cdot z$ 与 $\hat{p}_{CTR} \times \hat{p}_{CVR}$ 比较。总损失是这两项之和,反向传播时梯度同时流向 CTR 塔、CVR 塔和共享 Embedding 层。

这个设计妙在何处?首先,所有曝光样本(包括未点击样本)都会进入 $L_{CTCVR}$ 的计算:未点击样本的标签 $y \cdot z = 0$,它会推动 $\hat{p}_{CTR} \times \hat{p}_{CVR}$ 向 0 靠拢,由于 $\hat{p}_{CTR}$ 本身已经很小,乘积自然趋近于 0,CVR 塔收到的正是“这类人的转化率不该高”的信号。其次,CTR 损失的梯度通过共享 Embedding 增强了底层特征,CVR 塔得以享受数据量红利。这就是“两本账一起查”的数学实现。

每天对账,越对越准。但光有机制还不够,这套机制到底比老办法强在哪?还需要一场面对面的较量。

L=LCTR+LCTCVR=∑(x,y)ℓ(y, p^CTR(x))+∑(x,y,z)ℓ(y⋅z, p^CTR(x)⋅p^CVR(x))\displaystyle L = L_{CTR} + L_{CTCVR} = \sum_{(\mathbf{x},y)} \ell\big(y,\, \hat{p}_{CTR}(\mathbf{x})\big) + \sum_{(\mathbf{x},y,z)} \ell\big(y \cdot z,\ \hat{p}_{CTR}(\mathbf{x}) \cdot \hat{p}_{CVR}(\mathbf{x})\big)
通俗理解:A 组对“进门率”的账,两组一起对“进门且购买率”的账。对不上就一起调整,谁错改谁。
  1. 交叉熵损失$\ell(\cdot, \cdot)$ 是二元交叉熵,衡量模型预测概率与真实标签的差距。
    ℓ(y,p^)=−[ylog⁡p^+(1−y)log⁡(1−p^)]\displaystyle \ell(y, \hat{p}) = -\big[y \log \hat{p} + (1-y) \log(1-\hat{p})\big]
  2. CTR 损失第一项用点击标签 $y$ 直接监督 CTR 塔,数据量大、信号密集。
    LCTR=∑(x,y)ℓ(y, p^CTR(x))\displaystyle L_{CTR} = \sum_{(\mathbf{x},y)} \ell\big(y,\, \hat{p}_{CTR}(\mathbf{x})\big)
  3. CTCVR 损失第二项用组合标签 $y \cdot z$ 监督联合概率 $\hat{p}_{CTR} \cdot \hat{p}_{CVR}$,这是 CVR 塔唯一的监督来源。
    LCTCVR=∑(x,y,z)ℓ(y⋅z, p^CTR(x)⋅p^CVR(x))\displaystyle L_{CTCVR} = \sum_{(\mathbf{x},y,z)} \ell\big(y \cdot z,\ \hat{p}_{CTR}(\mathbf{x}) \cdot \hat{p}_{CVR}(\mathbf{x})\big)
  4. 梯度回传总损失对两个塔和共享层同时求梯度,CVR 塔通过乘积链路获得梯度。
    ∇θCVRL=∇θCVRLCTCVR=∑x∈Dexpo∂ℓ∂p^CTCVR⋅p^CTR(x)⋅∂p^CVR∂θCVR\displaystyle \nabla_{\theta_{CVR}} L = \nabla_{\theta_{CVR}} L_{CTCVR} = \sum_{x \in \mathcal{D}_{expo}} \frac{\partial \ell}{\partial \hat{p}_{CTCVR}} \cdot \hat{p}_{CTR}(\mathbf{x}) \cdot \frac{\partial \hat{p}_{CVR}}{\partial \theta_{CVR}}

对比独立CVR:全空间为何更准

美食街的另一家店,店主还在用老办法:只在进店顾客身上收集数据,训练“进店后购买预测器”。两位店主在街口打赌,看谁的模型能更准地判断路过的行人“进店后会不会买”。老店主发现,自己的模型在店里对老顾客百试百灵,可一到街上就疯狂打脸——因为街上的人,和进店的人根本不像。而新店主的模型,每天看的都是整条街的数据,自然更懂路人的心思。

独立 CVR 模型与 ESMM 的差距,本质上就是训练空间与部署空间的错位。独立 CVR 的训练数据是 $\mathcal{D}_{click}$(点击样本),只有曝光样本 $\mathcal{D}_{expo}$ 中很小的一部分;虽然 $\mathcal{D}_{click} \subset \mathcal{D}_{expo}$,但两者的特征分布差异巨大。ESMM 则从头到尾在 $\mathcal{D}_{expo}$ 上训练——CTR 损失和 CTCVR 损失覆盖全部曝光样本,训练分布与线上部署分布完全一致。

在稀疏性问题上,ESMM 同样占优。CVR 任务的正样本只是点击样本中转化的一小撮,但通过共享 Embedding,CVR 塔获得了 CTR 任务海量样本训练出的特征表示;同时,CTCVR 损失让所有未点击样本也间接参与了 CVR 塔的训练,用“0 转化”的联合信号约束了 CVR 塔的预测边界。在淘宝、阿里妈妈等工业场景中,ESMM 相比独立 CVR 模型的离线 AUC 通常提升 1~2 个百分点,线上转化率与 GMV 也有明显正向收益——这就是全空间建模的实证力量。

这场比试的结局早已注定:模型不是练得越久越准,而是见过的世界越接近真实世界才越准。全空间训练,就是让模型在真实世界里长大。

Dclick⊂Dexpo,Pclick(x)≠Pexpo(x)\displaystyle \mathcal{D}_{click} \subset \mathcal{D}_{expo}, \quad P_{click}(\mathbf{x}) \neq P_{expo}(\mathbf{x})
通俗理解:老店主在小样本上反复练习,新店主在大街上天天实战。考试考的是大街,谁赢自然不言而喻。
  1. 子集关系点击样本是曝光样本的子集,但只是其中被“筛选”过的很小一部分。
    Dclick⊂Dexpo,∣Dclick∣≪∣Dexpo∣\displaystyle \mathcal{D}_{click} \subset \mathcal{D}_{expo}, \quad |\mathcal{D}_{click}| \ll |\mathcal{D}_{expo}|
  2. 分布不等子集虽小,但它与全集的人群特征分布并不相同,这是偏差的数学来源。
    Pclick(x)≠Pexpo(x)\displaystyle P_{click}(\mathbf{x}) \neq P_{expo}(\mathbf{x})
  3. 训练空间对比独立 CVR 只使用 $\mathcal{D}_{click}$ 训练,ESMM 使用整个 $\mathcal{D}_{expo}$ 训练。
    独立CVR: min⁡θ∑x∈Dclickℓ(z,p^CVR)ESMM: min⁡θ∑x∈Dexpo[ℓ(y,p^CTR)+ℓ(yz,p^CTRp^CVR)]\displaystyle \text{独立CVR: }\min_{\theta} \sum_{x \in \mathcal{D}_{click}} \ell(z, \hat{p}_{CVR}) \quad \text{ESMM: }\min_{\theta} \sum_{x \in \mathcal{D}_{expo}} \big[\ell(y, \hat{p}_{CTR}) + \ell(yz, \hat{p}_{CTR}\hat{p}_{CVR})\big]

AI 应用场景

电商搜索与推荐:商品购买概率预估

阶段:离线训练与在线排序推理

解决的问题:电商推荐卡片的最终目标是预估“用户点击后购买”的概率(CVR),但传统独立 CVR 模型只用点击样本训练,导致在曝光样本上预估偏差大;购物行为漏斗长、转化数据极稀疏,单塔模型难以收敛。

具体案例:某电商平台的“猜你喜欢”信息流,用户曝光 100 个商品、点击 6 个、购买 1 个。将 CVR 预估模型从独立单塔替换为 ESMM 后,全量曝光样本进入训练,共享 Embedding 吸收了点击信号,离线 AUC 提升 1.3 个百分点,线上人均成交金额(GMV)提升 2.1%。

广告竞价系统:点击后行为转化预估

阶段:广告排序与出价预估

解决的问题:广告平台按“用户点击广告并完成注册/付费”结算,需要预估点击后的深度转化率。深度转化事件比点击更稀疏,且用户从曝光到转化存在延迟与多路径干扰,独立模型几乎学不动。

具体案例:某信息流广告平台的“激活率”预估。广告主按激活付费,平台用 ESMM 同时建模广告点击率(CTR 主模型)与点击后激活率(CVR 塔),两个塔共享用户兴趣 Embedding。新广告冷启动期间,CTR 任务的大数据量为 CVR 塔提供了先验特征,冷启动激活率预估误差下降约 15%。

Python 演示

python70 行
"""ESMM 简化实现:全空间多任务 CVR 建模演示"""import torchimport torch.nn as nn torch.manual_seed(42) N = 20000          # 曝光样本数FEAT_DIM = 12BATCH = 512EPOCHS = 15 # 1. 模拟曝光空间的点击/转化数据X = torch.randn(N, FEAT_DIM)w_ctr = torch.randn(FEAT_DIM) * 0.4w_cvr = torch.randn(FEAT_DIM) * 0.4p_ctr = torch.sigmoid(X @ w_ctr - 0.5)p_cvr_given_click = torch.sigmoid(X @ w_cvr - 0.3)y = torch.bernoulli(p_ctr)                     # 点击标签z = torch.bernoulli(p_cvr_given_click) * y    # 转化标签(仅点击后可观测)  class ESMM(nn.Module):    """共享Embedding + CTR/CVR 双塔 + 乘法输出"""     def __init__(self, feat_dim, emb_dim=8):        super().__init__()        self.shared_emb = nn.Sequential(            nn.Linear(feat_dim, emb_dim), nn.ReLU()        )        self.ctr_tower = nn.Sequential(            nn.Linear(emb_dim, emb_dim), nn.ReLU(), nn.Linear(emb_dim, 1)        )        self.cvr_tower = nn.Sequential(            nn.Linear(emb_dim, emb_dim), nn.ReLU(), nn.Linear(emb_dim, 1)        )     def forward(self, x):        e = self.shared_emb(x)        p_ctr = torch.sigmoid(self.ctr_tower(e))        p_cvr = torch.sigmoid(self.cvr_tower(e))        return p_ctr, p_cvr, p_ctr * p_cvr    # pCTCVR = pCTR * pCVR  model = ESMM(FEAT_DIM)opt = torch.optim.Adam(model.parameters(), lr=0.01)bce = nn.BCELoss(reduction="mean") for epoch in range(EPOCHS):    perm = torch.randperm(N)    total_loss = 0.0    for i in range(0, N, BATCH):        idx = perm[i: i + BATCH]        xb, yb, zb = X[idx], y[idx].float(), z[idx].float()        yb = yb.view(-1, 1)        zb = zb.view(-1, 1)        p_ctr, p_cvr, p_ctcvr = model(xb)        loss_ctr = bce(p_ctr, yb)                  # 点击损失        loss_ctcvr = bce(p_ctcvr, yb * zb)         # 点击且转化损失        loss = loss_ctr + loss_ctcvr        opt.zero_grad()        loss.backward()        opt.step()        total_loss += loss.item() * len(idx)     with torch.no_grad():        p_ctr_all, p_cvr_all, _ = model(X)        acc_ctr = ((p_ctr_all > 0.5).float() == y).float().mean().item()        mae_cvr = (p_cvr_all[:, 0] - p_cvr_given_click).abs().mean().item()    print(f"epoch {epoch+1:2d} | loss {total_loss/N:.4f} | "          f"acc_ctr {acc_ctr:.3f} | mae_cvr {mae_cvr:.4f}")
目的:用 5 万不到的模拟曝光数据训练一个简化版 ESMM,演示“CTR 损失 + CTCVR 乘积损失”如何联合驱动双塔学习,并观察 CVR 塔在没有直接转化标签监督时,依然能逼近真实转化概率。
  1. z = torch.bernoulli(p_cvr_given_click) * y模拟真实世界“转化只在点击后可观测”,未点击样本强制记 0。
  2. self.shared_emb = nn.Sequential(nn.Linear(feat_dim, emb_dim), nn.ReLU())共享 Embedding 底座,CTR 和 CVR 塔共用同一个特征向量。
  3. return p_ctr, p_cvr, p_ctr * p_cvr乘法输出 CTCVR,这是 CVR 塔获得监督信号的唯一通道。
  4. loss_ctr = bce(p_ctr, yb)点击塔的交叉熵损失,样本量大、信号充足。
  5. loss_ctcvr = bce(p_ctcvr, yb * zb)联合乘积损失,让所有曝光样本都参与 CVR 塔训练。
  6. loss = loss_ctr + loss_ctcvr两路损失求和,反向传播时梯度同时流向两个塔。
  7. loss.backward()梯度通过乘积链路传导至 CVR 塔,实现“间接监督”。
  8. mae_cvr = (p_cvr_all[:, 0] - p_cvr_given_click).abs().mean().item()用不可观测的真实转化概率评估 CVR 塔学到了多少。

常见误区

误区用户没点击就不会有后续转化,那拿点击样本训练 CVR 模型不是正好吗?模型学的不就是“点击后买不买”吗?

正解CVR 模型线上要预测的是所有被曝光的商品,而不仅仅是被点击的商品。没点击不代表用户不会转化——他只是这次没看到或没兴趣点,但换个场景可能就买了。用点击样本训练,模型永远没见过“没点击的人”,却要在他们身上做预测,这叫样本选择偏差。全空间建模的意义就在于此。

为何常见:人们容易混淆“事件发生的逻辑顺序”(转化必须以点击为前提)与“统计训练数据的采样方式”(训练集应该覆盖全部曝光人群)。前者正确,后者才是偏差的来源。

误区ESMM 是双塔多任务,那 CVR 塔就应该像普通分类器一样,用真实转化标签 z 直接算交叉熵,只是额外加了共享 Embedding 而已。

正解ESMM 的精髓恰恰在于 CVR 塔没有直接转化标签监督。CVR 塔的损失来自 pCTR × pCVR 与 y·z 之间的 CTCVR 交叉熵。因为未点击样本的 z 不可观测,直接拿 z 当标签要么只能覆盖点击子集(回到 SSB),要么把未点击样本错误地当成负样本(标签噪声)。

为何常见:看到“多任务学习”就天然假设每个任务有独立监督信号,忽略了 ESMM 里 CVR 是作为“中间变量”被乘积约束训练的特殊设计。

误区ESMM 之所以能解决偏差,主要是因为它让 CVR 塔和 CTR 塔共享了 Embedding,特征学得更好了。

正解共享 Embedding 确实缓解了数据稀疏,但它不直接解决样本选择偏差。SSB 的根源是训练损失只落在点击样本上;ESMM 真正的功臣是 CTCVR 损失——它让全部曝光样本都进入了损失计算,CVR 塔因此获得了全空间的梯度信号。共享 Embedding 只是锦上添花。

为何常见:把“多任务共享参数”与“全空间训练”混为一谈。二者的作用对象不同:共享 Embedding 作用于特征表示,CTCVR 损失作用于样本空间。

误区既然 CTCVR 等于两个概率相乘,那 ESMM 一定假设了点击事件和转化事件相互独立——这在直觉上说不通,因为点击和转化强相关。

正解这不需要独立性假设。条件概率链式法则告诉我们 P(y=1,z=1|x)=P(y=1|x)·P(z=1|y=1,x),这是数学恒等式,只要“转化必然在点击后发生”就成立。乘积第二项 P(z=1|y=1,x) 本身就是“在已点击条件下转化”的条件概率,它已经编码了点击与转化的依赖关系。

为何常见:看到“概率相乘”就联想到朴素贝叶斯的独立性假设。乘法形式不等于独立性,关键是看清乘的到底是“边缘概率”还是“条件概率”。

面试题

Q1. ESMM 模型主要用于解决什么问题?

单选题 · easy

  • A.样本选择偏差与数据稀疏性← 正确答案
  • B.提升CTR模型的表达能力
  • C.降低推荐系统的推理延迟
  • D.解决用户的冷启动问题

答案:A。ESMM 的设计初衷就是同时解决 CVR 预估的两大顽疾——样本选择偏差(SSB)和数据稀疏性。B、C、D 都不是 ESMM 要解决的核心问题。

Q2. ESMM 中“全空间建模”的“全空间”指的是什么?

单选题 · easy

  • A.所有点击样本组成的空间
  • B.所有曝光样本组成的空间← 正确答案
  • C.所有转化样本组成的空间
  • D.所有用户特征组成的空间

答案:B。全空间指完整的曝光空间。ESMM 在全部曝光样本上计算 CTR 损失和 CTCVR 损失,而不是只用点击子集训练,这正是它消除样本选择偏差的关键。

Q3. ESMM 中 CVR 塔的直接监督信号来自哪里?

单选题 · mid

  • A.转化标签的交叉熵损失
  • B.点击标签的交叉熵损失
  • C.联合乘积的CTCVR损失← 正确答案
  • D.独立分类器的监督损失

答案:C。CVR 塔没有独立的转化标签损失。它的监督信号来自 pCTR×pCVR 与 y·z 标签的 CTCVR 交叉熵损失,通过乘积链路传导梯度,这是 ESMM 与普通多任务模型的本质区别。

Q4. 在 ESMM 中,pCTCVR 与 pCTR、pCVR 的关系是?

单选题 · mid

  • A.pCTCVR = pCTR / pCVR
  • B.pCTCVR = pCTR + pCVR
  • C.pCTCVR = pCVR / pCTR
  • D.pCTCVR = pCTR × pCVR← 正确答案

答案:D。根据条件概率链式法则,点击且转化 = 点击概率 × 点击后转化概率,即 pCTCVR = pCTR × pCVR。这是恒等式,不需要独立假设。

Q5. 为什么独立 CVR 模型会产生样本选择偏差?

单选题 · mid

  • A.训练集只用点击样本而部署面对曝光样本← 正确答案
  • B.训练集样本数量太少导致模型欠拟合
  • C.模型网络结构过于简单表达能力不足
  • D.Embedding维度设置不合理影响学习

答案:A。SSB 的根源是训练数据采样分布与线上部署分布不一致:训练只见过点击样本,却要对所有曝光样本预测。B 是稀疏性问题,C、D 是模型容量问题,都不属于样本选择偏差。

Q6. ESMM 中共享 Embedding 层的主要作用是什么?

单选题 · hard

  • A.强制CTR与CVR两个塔的输出相等
  • B.让CVR塔借助CTR大样本获得鲁棒特征← 正确答案
  • C.将模型总参数量减少到原来的一半
  • D.使CTR与CVR的损失可以直接相加

答案:B。CTR 任务训练样本远多于 CVR,共享 Embedding 让 CVR 塔复用 CTR 学到的特征表示,缓解数据稀疏。A 会让两个塔失去意义,C、D 不是共享层的目标。

Q7. ESMM 的联合损失由哪些部分组成?

多选题 · mid

  • A.点击标签与CTR预测的交叉熵← 正确答案
  • B.转化标签与CVR预测的交叉熵
  • C.点击转化联合CTCVR的交叉熵← 正确答案
  • D.L2正则对模型参数的约束项

答案:AC。ESMM 总损失 = CTR 损失 + CTCVR 损失。B 是容易混淆的错误项——CVR 塔没有直接转换标签损失;D 中的正则项属于通用训练技巧,不是 ESMM 损失的组成部分。

Q8. 对于曝光样本中未点击的样本(y=0),CTCVR 损失如何影响 CVR 塔?

单选题 · hard

  • A.该样本不参与CVR塔的梯度更新
  • B.通过CTCVR逼近零间接约束CVR塔← 正确答案
  • C.直接把CVR标签设为零参与更新
  • D.提高该样本在CTR塔中的权重

答案:B。y=0 时 y·z=0,CTCVR 损失要求 pCTR×pCVR 逼近 0。由于 pCTR 本身小,乘积趋近于 0,梯度经乘积链路以较小幅值传导到 CVR 塔,形成“这类人群转化率不应高”的间接约束。A 错误,C 错误地把未点击当负样本,D 与 CTCVR 损失无关。

Q9. 关于 ESMM 与独立 CVR 模型的对比,下列说法正确的有?

多选题 · extreme

  • A.全空间模型使用曝光样本训练← 正确答案
  • B.两种模型推理阶段都可输出转化率← 正确答案
  • C.独立模型在点击集上的指标偏乐观← 正确答案
  • D.全空间模型转化塔得到直接监督

答案:ABC。A 对,ESMM 用曝光空间训练;B 对,两种模型部署时都输出 pCVR;C 对,独立 CVR 在点击集上测试因训练测试同分布而 AUC 虚高,这正是 SSB 的体现;D 错,ESMM 的 CVR 塔没有直接转化标签监督,靠 CTCVR 乘积约束。

Q10. 下列哪些场景适合用 ESMM 类模型?

多选题 · hard

  • A.电商商品点击后的购买行为预测← 正确答案
  • B.广告点击后的注册激活行为预测← 正确答案
  • C.文章曝光后点赞率的单层行为预测
  • D.视频点击后的付费订阅行为预测← 正确答案

答案:ABD。ESMM 适用于“曝光→点击→转化”的漏斗式行为序列建模。A、B、D 都存在点击后转化的中间环节,可以用乘法关系分解;C 的点赞是曝光后的直接行为,没有点击中间层,不需要多任务分解,单任务建模即可。