ESMM学习笔记:如何解决CVR预估中的样本选择偏差与数据稀疏难题
ESMM学习笔记如何解决CVR预估中的样本选择偏差与数据稀疏难题引言CVR预估的两大挑战在推荐系统和广告点击率预估领域CVRConversion Rate转化率预估是一个核心任务。与CTRClick-Through Rate点击率预估不同CVR预估面临两个特殊的难题1.样本选择偏差Sample Selection BiasSSBCVR模型通常只在点击样本上训练但实际预测时需要对所有曝光样本进行预估。点击样本和未点击样本在用户行为模式上存在显著差异导致训练集和预测集的分布不一致模型泛化能力下降。2.数据稀疏Data SparsityDS转化行为远比点击行为稀少导致CVR训练样本数量远少于CTR。在广告场景中点击率可能只有1%-5%而转化率可能只有0.1%-0.5%这意味着CVR模型可用的正样本极其有限容易过拟合。为了解决这两个问题阿里巴巴在2018年提出了ESMMEntire Space Multi-Task Model全空间多任务模型。本文将循序渐进地讲解ESMM的原理与实现。## 基础概念从CTR预估到CVR预估### CTR预估与CVR预估的关系在电商或广告系统中用户行为通常遵循一个漏斗模型曝光Impression → 点击Click → 转化ConversionCTR预估关注的是给定曝光样本用户点击的概率 P(click1|impression)。CVR预估关注的是给定点击样本用户转化的概率 P(conversion1|click1)。传统方法中CVR模型只在点击样本上训练这就导致了样本选择偏差问题——模型从未见过未点击样本的特征分布却在预测时需要对它们进行推断。### 多任务学习的思想ESMM的核心思想是将CVR预估和CTR预估作为两个相关任务联合训练。既然点击和转化是顺序发生的那么我们可以利用CTR任务中丰富的点击样本信息来辅助CVR任务。具体来说ESMM引入了一个辅助任务——CTCVRClick-Then-Conversion Rate点击后转化率即 P(click1, conversion1|impression)。通过全空间所有曝光样本上的CTCVR预估间接学习CVR。## ESMM模型架构详解ESMM的模型架构包含以下几个关键部分1.共享嵌入层Shared Embedding Layer将高维稀疏特征映射到低维稠密向量。2.两个独立的塔Two TowersCTR塔和CVR塔分别用于预估CTR和CVR。3.乘积层Multiplication Layer通过CTR和CVR的乘积得到CTCVR的预估值。关键公式- CTR预估pCTR P(click1|impression)- CVR预估pCVR P(conversion1|click1)- CTCVR预估pCTCVR P(click1, conversion1|impression) pCTR × pCVR训练时我们用全空间样本包括未点击和点击来优化CTCVR损失和CTR损失而CVR损失则通过反向传播间接优化。## 代码实现从零构建ESMM下面我们实现一个简化版的ESMM模型。首先我们使用TensorFlow 2.x来构建模型。### 示例1模型架构定义pythonimport tensorflow as tffrom tensorflow.keras.layers import Dense, Embedding, Flatten, Concatenate, Inputfrom tensorflow.keras import Modelclass ESMM(Model): def __init__(self, feature_columns, embedding_dim8): feature_columns: 字典包含每个特征的类别数 embedding_dim: 嵌入维度 super(ESMM, self).__init__() self.feature_columns feature_columns self.embedding_dim embedding_dim # 共享嵌入层 self.embeddings {} for feat_name, vocab_size in feature_columns.items(): self.embeddings[feat_name] Embedding( input_dimvocab_size, output_dimembedding_dim, namefembedding_{feat_name} ) # CTR塔简单MLP self.ctr_dense1 Dense(64, activationrelu, namectr_dense1) self.ctr_dense2 Dense(32, activationrelu, namectr_dense2) self.ctr_output Dense(1, activationsigmoid, namectr_output) # CVR塔简单MLP self.cvr_dense1 Dense(64, activationrelu, namecvr_dense1) self.cvr_dense2 Dense(32, activationrelu, namecvr_dense2) self.cvr_output Dense(1, activationsigmoid, namecvr_output) def call(self, inputs, trainingFalse): # inputs: 字典每个键对应一个特征值为整数索引 # 共享嵌入 embeddings [] for feat_name in self.feature_columns.keys(): feat_input inputs[feat_name] emb self.embeddings[feat_name](feat_input) embeddings.append(Flatten()(emb)) # 展平为向量 # 拼接所有特征嵌入 concat_emb Concatenate()(embeddings) # CTR塔 ctr_hidden self.ctr_dense1(concat_emb) ctr_hidden self.ctr_dense2(ctr_hidden) ctr_pred self.ctr_output(ctr_hidden) # CVR塔 cvr_hidden self.cvr_dense1(concat_emb) cvr_hidden self.cvr_dense2(cvr_hidden) cvr_pred self.cvr_output(cvr_hidden) # CTCVR CTR * CVR ctcvr_pred ctr_pred * cvr_pred return ctr_pred, cvr_pred, ctcvr_pred# 假设有3个特征用户ID1000个、商品ID500个、广告位ID10个feature_columns { user_id: 1000, item_id: 500, ad_pos: 10}# 创建模型实例model ESMM(feature_columns, embedding_dim8)# 查看模型结构model.build(input_shape({user_id: (None,), item_id: (None,), ad_pos: (None,)}))model.summary()### 示例2训练循环与损失函数在ESMM的训练中我们需要同时优化CTR损失和CTCVR损失。注意CVR损失是基于点击样本的但ESMM巧妙地通过共享嵌入和乘积操作来间接优化。pythonimport numpy as np# 生成模拟数据def generate_synthetic_data(num_samples10000): np.random.seed(42) # 特征用户ID、商品ID、广告位ID data { user_id: np.random.randint(0, 1000, sizenum_samples), item_id: np.random.randint(0, 500, sizenum_samples), ad_pos: np.random.randint(0, 10, sizenum_samples) } # 标签点击5%概率和转化点击后10%概率 click np.random.binomial(1, 0.05, sizenum_samples) conversion np.random.binomial(1, 0.1 * click, sizenum_samples) # 只有点击后才可能转化 return data, click, conversion# 自定义损失函数def ctr_loss(y_true, y_pred): # y_true: [click, conversion] 这里我们只使用click return tf.keras.losses.binary_crossentropy(y_true[:, 0], y_pred[:, 0])def ctcvr_loss(y_true, y_pred): # y_true: [click, conversion] 这里使用conversion因为CTCVRclick conversion return tf.keras.losses.binary_crossentropy(y_true[:, 1], y_pred[:, 0])# 准备数据X, click, conversion generate_synthetic_data(2000)# 构建输入字典input_dict {k: v for k, v in X.items()}# 构建标签第一列是click第二列是conversiony np.stack([click, conversion], axis1)# 编译模型注意TensorFlow需要自定义损失函数optimizer tf.keras.optimizers.Adam(learning_rate0.001)# 训练步骤tf.functiondef train_step(features, labels): with tf.GradientTape() as tape: # 前向传播 ctr_pred, cvr_pred, ctcvr_pred model(features, trainingTrue) # 计算损失 # CTR损失使用点击标签 loss_ctr tf.reduce_mean( tf.keras.losses.binary_crossentropy(labels[:, 0], tf.squeeze(ctr_pred)) ) # CTCVR损失使用转化标签因为转化只发生在点击后 loss_ctcvr tf.reduce_mean( tf.keras.losses.binary_crossentropy(labels[:, 1], tf.squeeze(ctcvr_pred)) ) # 总损失两个损失相加可加权重 total_loss loss_ctr loss_ctcvr # 计算梯度并更新 gradients tape.gradient(total_loss, model.trainable_variables) optimizer.apply_gradients(zip(gradients, model.trainable_variables)) return total_loss, loss_ctr, loss_ctcvr# 训练10个epochfor epoch in range(10): total_loss, loss_ctr, loss_ctcvr train_step(input_dict, y) print(fEpoch {epoch1}: Total Loss {total_loss:.4f}, CTR Loss {loss_ctr:.4f}, CTCVR Loss {loss_ctcvr:.4f})# 训练完成后预测CVR注意只在点击样本上评估更合理import randomtest_sample { user_id: np.array([random.randint(0, 999)]), item_id: np.array([random.randint(0, 499)]), ad_pos: np.array([random.randint(0, 9)])}_, cvr_pred, _ model(test_sample, trainingFalse)print(f预测CVR: {cvr_pred.numpy()[0][0]:.4f})## 高级用法与优化技巧### 多任务学习的权重平衡在ESMM中CTR损失和CTCVR损失的权重可以调整。如果CVR任务特别稀疏可以适当增加CTCVR损失的权重让模型更关注转化信号。### 特征工程与嵌入维度在实际应用中共享嵌入层的维度选择非常关键。过小的嵌入维度会导致信息丢失过大的维度则会导致过拟合。通常建议在8-64之间调整。### 其他变体ESMM的后续改进包括-ESMMAttention在CVR塔中引入注意力机制让模型更关注与转化相关的特征。-Multi-ESMM处理多步转化如加购、收藏、支付的序列任务。## 总结ESMM通过多任务学习巧妙解决了CVR预估中的两大难题1.样本选择偏差通过在全空间所有曝光样本上训练CTCVR任务避免了只在点击样本上训练导致的分布偏移。即使未点击样本的CVR不可观测模型也能通过CTR和CTCVR的乘积关系学到正确的CVR。2.数据稀疏通过共享嵌入层让CTR任务中丰富的点击数据帮助CVR任务学习更好的特征表示。CTR任务提供了大量的训练信号缓解了CVR正样本不足的问题。ESMM的核心思想可以概括为用辅助任务CTR、CTCVR来间接优化主任务CVR同时利用共享表示来迁移知识。这种范式在推荐系统、广告预估等领域具有广泛的应用价值。理解ESMM不仅有助于解决CVR预估问题也为处理其他存在样本选择偏差或数据稀疏的任务提供了思路——例如在医疗诊断中我们可以用高频率的检查结果来辅助预测罕见疾病。

相关新闻

AI订阅管家:智能监控与优化你的数字消费

AI订阅管家:智能监控与优化你的数字消费

1. 为什么我们总在不知不觉中成为"年费冤大头"前几天整理信用卡账单时,我突然发现自己在过去一年里,竟然为各种用不着的订阅服务支付了将近2000元。从某音乐平台的自动续费,到早已遗忘的云存储会员,再到试用后忘记取消的…

2026/10/17 23:02:48 阅读更多 →
Docker容器化实战:定制镜像、配置Yum源与部署服务

Docker容器化实战:定制镜像、配置Yum源与部署服务

这次我们来看 Docker 容器化技术中几个非常核心的实战操作:如何定制一个简单的 Docker 镜像、如何在容器内部配置 Yum 软件仓库,以及如何在容器内完成服务的安装与部署。对于需要在隔离环境中快速构建和部署应用的开发者或运维人员来说,掌握这些技能是摆脱“只会用现成镜像”…

2026/10/17 23:18:59 阅读更多 →
独立开发者如何利用Taotoken以更低成本实验多种大模型API

独立开发者如何利用Taotoken以更低成本实验多种大模型API

独立开发者如何利用Taotoken以更低成本实验多种大模型API 对于独立开发者或小型工作室而言,在有限的预算内探索不同的大模型能力,是一项兼具挑战与机遇的任务。直接对接多个厂商的API,意味着需要分别注册账号、管理多个密钥、理解不同的计费…

2026/10/17 23:21:46 阅读更多 →

最新新闻

uview-plus密码切换失效的根因排查与正确修复方案

uview-plus密码切换失效的根因排查与正确修复方案

上周给小程序做登录页,密码框要求支持明文密文切换。组件用的uview-plus,照着文档写了几行代码,结果右上角那个小眼睛点了完全没反应,真机上试了好几次,要么纹丝不动,要么闪一下又弹回密文状态。在项目群里…

2026/10/18 17:04:56 阅读更多 →
多传感器融合三条路线深度对比:前融合、特征级与决策级的信息损耗与工程实践

多传感器融合三条路线深度对比:前融合、特征级与决策级的信息损耗与工程实践

我先把话说在前面:多传感器融合这个主题,市面上讲“算法公式”的教程很多,但真正把前融合、特征级融合、决策级融合这三条路线放在同一个项目里拉通对比、还跑过实车数据的人,不多。这篇文章我不会从教科书定义开始,而…

2026/10/18 17:04:56 阅读更多 →
USB不只是采集画面还能输出显示:从UVC到DP Alt Mode的方案与避坑指南

USB不只是采集画面还能输出显示:从UVC到DP Alt Mode的方案与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 17:04:56 阅读更多 →
数字IC社招UVM面试高频题拆解与工程实践回答策略

数字IC社招UVM面试高频题拆解与工程实践回答策略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 17:04:56 阅读更多 →
开发板待机功耗超标排查:从芯片睡眠到系统级漏电归因

开发板待机功耗超标排查:从芯片睡眠到系统级漏电归因

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 17:04:56 阅读更多 →
ROS2小车网页遥控:WebSocket桥接实现低延迟物理控制

ROS2小车网页遥控:WebSocket桥接实现低延迟物理控制

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 17:03:56 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →