多任务学习模型之ESMM介绍与实现

简介: 本文介绍的是阿里巴巴团队发表在 SIGIR’2018 的论文《Entire Space Multi-Task Model: An Effective Approach for Estimating Post-Click Conversion Rate》。文章基于 Multi-Task Learning (MTL) 的思路,提出一种名为ESMM的CVR预估模型,有效解决了真实场景中CVR预估面临的数据稀疏以及样本选择偏差这两个关键问题。后续还会陆续介绍MMoE,PLE,DBMTL等多任务学习模型。

多任务学习背景

目前工业中使用的推荐算法已不只局限在单目标(ctr)任务上,还需要关注后续的转换链路,如是否评论、收藏、加购、购买、观看时长等目标。

本文介绍的是阿里巴巴团队发表在 SIGIR’2018 的论文《Entire Space Multi-Task Model: An Effective Approach for Estimating Post-Click Conversion Rate》。文章基于 Multi-Task Learning (MTL) 的思路,提出一种名为ESMM的CVR预估模型,有效解决了真实场景中CVR预估面临的数据稀疏以及样本选择偏差这两个关键问题。后续还会陆续介绍MMoE,PLE,DBMTL等多任务学习模型。

论文介绍

CVR预估面临两个关键问题:

1. Sample Selection Bias (SSB)

转化是在点击之后才“有可能”发生的动作,传统CVR模型通常以点击数据为训练集,其中点击未转化为负例,点击并转化为正例。但是训练好的模型实际使用时,则是对整个空间的样本进行预估,而非只对点击样本进行预估。即训练数据与实际要预测的数据来自不同分布,这个偏差对模型的泛化能力构成了很大挑战,导致模型上线后,线上业务效果往往一般。

2. Data Sparsity (DS)

CVR预估任务的使用的训练数据(即点击样本)远小于CTR预估训练使用的曝光样本。仅使用数量较小的样本进行训练,会导致深度模型拟合困难。

一些策略可以缓解这两个问题,例如从曝光集中对unclicked样本抽样做负例缓解SSB,对转化样本过采样缓解DS等。但无论哪种方法,都没有从实质上解决上面任一个问题。

由于点击=>转化,本身是两个强相关的连续行为,作者希望在模型结构中显示考虑这种“行为链关系”,从而可以在整个空间上进行训练及预测。这涉及到CTR与CVR两个任务,因此使用多任务学习(MTL)是一个自然的选择,论文的关键亮点正在于“如何搭建”这个MTL。

首先需要重点区分下,CVR预估任务与CTCVR预估任务。

  • CVR = 转化数/点击数。是预测“假设item被点击,那么它被转化”的概率。CVR预估任务,与CTR没有绝对的关系。一个item的ctr高,cvr不一定同样会高,如标题党文章的浏览时长往往较低。这也是不能直接使用全部样本训练CVR模型的原因,因为无法确定那些曝光未点击的样本,假设他们被点击了,是否会被转化。如果直接使用0作为它们的label,会很大程度上误导CVR模型的学习。
  • CTCVR = 转换数/曝光数。是预测“item被点击,然后被转化”的概率。

其中x,y,z分别表示曝光,点击,转换。注意到,在全部样本空间中,CTR对应的label为click,而CTCVR对应的label为click & conversion,这两个任务是可以使用全部样本的。因此,ESMM通过学习CTR,CTCVR两个任务,再根据上式隐式地学习CVR任务。具体结构如下:

网络结构上有两点值得强调:

  1. 共享Embedding。 CVR-task和CTR-task使用相同的特征和特征embedding,即两者从Concatenate之后才学习各自独享的参数;
  2. 隐式学习pCVR。这里pCVR 仅是网络中的一个variable,没有显示的监督信号。

具体地,反映在目标函数中:

代码实现

基于EasyRec推荐算法框架,我们实现了ESMM算法,具体实现可移步至github:EasyRec-ESMM

EasyRec介绍:EasyRec是阿里云计算平台机器学习PAI团队开源的大规模分布式推荐算法框架,EasyRec 正如其名字一样,简单易用,集成了诸多优秀前沿的推荐系统论文思想,并且有在实际工业落地中取得优良效果的特征工程方法,集成训练、评估、部署,与阿里云产品无缝衔接,可以借助 EasyRec 在短时间内搭建起一套前沿的推荐系统。作为阿里云的拳头产品,现已稳定服务于数百个企业客户。

模型前馈网络:

  def build_predict_graph(self):
    """Forward function.
    Returns:
      self._prediction_dict: Prediction result of two tasks.
    """
    # 此处从Concatenate后的tensor(all_fea)开始,省略其生成逻辑
    cvr_tower_name = self._cvr_tower_cfg.tower_name
    dnn_model = dnn.DNN(
        self._cvr_tower_cfg.dnn,
        self._l2_reg,
        name=cvr_tower_name,
        is_training=self._is_training)
    cvr_tower_output = dnn_model(all_fea)
    cvr_tower_output = tf.layers.dense(
        inputs=cvr_tower_output,
        units=1,
        kernel_regularizer=self._l2_reg,
        name='%s/dnn_output' % cvr_tower_name)
    ctr_tower_name = self._ctr_tower_cfg.tower_name
    dnn_model = dnn.DNN(
        self._ctr_tower_cfg.dnn,
        self._l2_reg,
        name=ctr_tower_name,
        is_training=self._is_training)
    ctr_tower_output = dnn_model(all_fea)
    ctr_tower_output = tf.layers.dense(
        inputs=ctr_tower_output,
        units=1,
        kernel_regularizer=self._l2_reg,
        name='%s/dnn_output' % ctr_tower_name)
    tower_outputs = {
        cvr_tower_name: cvr_tower_output,
        ctr_tower_name: ctr_tower_output
    }
    self._add_to_prediction_dict(tower_outputs)
    return self._prediction_dict

loss计算:

注意:计算CVR的指标时需要mask掉曝光数据。

  def build_loss_graph(self):
    """Build loss graph.
    Returns:
      self._loss_dict: Weighted loss of ctr and cvr.
    """
    cvr_tower_name = self._cvr_tower_cfg.tower_name
    ctr_tower_name = self._ctr_tower_cfg.tower_name
    cvr_label_name = self._label_name_dict[cvr_tower_name]
    ctr_label_name = self._label_name_dict[ctr_tower_name]
    ctcvr_label = tf.cast(
        self._labels[cvr_label_name] * self._labels[ctr_label_name], 
        tf.float32)
    cvr_loss = tf.keras.backend.binary_crossentropy(
        ctcvr_label, self._prediction_dict['probs_ctcvr'])
    cvr_loss = tf.reduce_sum(cvr_losses, name="ctcvr_loss")
    # The weight defaults to 1.
    self._loss_dict['weighted_cross_entropy_loss_%s' %
                      cvr_tower_name] = self._cvr_tower_cfg.weight * cvr_loss
    ctr_loss = tf.reduce_sum(tf.nn.sigmoid_cross_entropy_with_logits(
        labels=tf.cast(self._labels[ctr_label_name], tf.float32),
        logits=self._prediction_dict['logits_%s' % ctr_tower_name]
        ), name="ctr_loss")
    self._loss_dict['weighted_cross_entropy_loss_%s' %
                    ctr_tower_name] = self._ctr_tower_cfg.weight * ctr_loss
    return self._loss_dict

note: 这里loss是 weighted_cross_entropy_loss_ctr + weighted_cross_entropy_loss_cvr, EasyRec框架会自动对self._loss_dict中的内容进行加和。

metric计算:

注意:计算CVR的指标时需要mask掉曝光数据。

 

  def build_metric_graph(self, eval_config):
    """Build metric graph.
    Args:
      eval_config: Evaluation configuration.
    Returns:
      metric_dict: Calculate AUC of ctr, cvr and ctrvr.
    """
    metric_dict = {}
    cvr_tower_name = self._cvr_tower_cfg.tower_name
    ctr_tower_name = self._ctr_tower_cfg.tower_name
    cvr_label_name = self._label_name_dict[cvr_tower_name]
    ctr_label_name = self._label_name_dict[ctr_tower_name]
    for metric in self._cvr_tower_cfg.metrics_set:
      # CTCVR metric
      ctcvr_label_name = cvr_label_name + '_ctcvr'
      cvr_dtype = self._labels[cvr_label_name].dtype
      self._labels[ctcvr_label_name] = self._labels[cvr_label_name] * tf.cast(
          self._labels[ctr_label_name], cvr_dtype)
      metric_dict.update(
          self._build_metric_impl(
              metric,
              loss_type=self._cvr_tower_cfg.loss_type,
              label_name=ctcvr_label_name,
              num_class=self._cvr_tower_cfg.num_class,
              suffix='_ctcvr'))
      # CVR metric
      cvr_label_masked_name = cvr_label_name + '_masked'
      ctr_mask = self._labels[ctr_label_name] > 0
      self._labels[cvr_label_masked_name] = tf.boolean_mask(
          self._labels[cvr_label_name], ctr_mask)
      pred_prefix = 'probs' if self._cvr_tower_cfg.loss_type == LossType.CLASSIFICATION else 'y'
      pred_name = '%s_%s' % (pred_prefix, cvr_tower_name)
      self._prediction_dict[pred_name + '_masked'] = tf.boolean_mask(
          self._prediction_dict[pred_name], ctr_mask)
      metric_dict.update(
          self._build_metric_impl(
              metric,
              loss_type=self._cvr_tower_cfg.loss_type,
              label_name=cvr_label_masked_name,
              num_class=self._cvr_tower_cfg.num_class,
              suffix='_%s_masked' % cvr_tower_name))
    for metric in self._ctr_tower_cfg.metrics_set:
      # CTR metric
      metric_dict.update(
          self._build_metric_impl(
              metric,
              loss_type=self._ctr_tower_cfg.loss_type,
              label_name=ctr_label_name,
              num_class=self._ctr_tower_cfg.num_class,
              suffix='_%s' % ctr_tower_name))
    return metric_dict

实验及不足

我们基于开源AliCCP数据,进行了大量实验,实验部分请期待下一篇文章。实验发现,ESMM的跷跷板现象较为明显,CTR与CVR任务的效果较难同时提升。


参考文献

  1. Entire Space Multi-Task Model: An Effective Approach for Estimating Post-Click Conversion Rate
  2. 阿里CVR预估模型之ESMM
  3. EasyRec-ESMM使用介绍多任务学习模型之ESMM介绍与实现

注:本文图片及公示均引用自论文:Entire Space Multi-Task Model: An Effective Approach for Estimating Post-Click Conversion Rate


若有收获,就点个赞吧

相关实践学习
使用PAI+LLaMA Factory微调Qwen2-VL模型,搭建文旅领域知识问答机器人
使用PAI和LLaMA Factory框架,基于全参方法微调 Qwen2-VL模型,使其能够进行文旅领域知识问答,同时通过人工测试验证了微调的效果。
机器学习概览及常见算法
机器学习(Machine Learning, ML)是人工智能的核心,专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能,它是使计算机具有智能的根本途径,其应用遍及人工智能的各个领域。 本课程将带你入门机器学习,掌握机器学习的概念和常用的算法。
目录
相关文章
|
存储 算法 Python
一文速学-时间序列分析算法之指数平滑法详解+Python代码实现
一文速学-时间序列分析算法之指数平滑法详解+Python代码实现
3327 0
一文速学-时间序列分析算法之指数平滑法详解+Python代码实现
|
12月前
|
机器学习/深度学习 算法 机器人
强化学习:时间差分(TD)(SARSA算法和Q-Learning算法)(看不懂算我输专栏)——手把手教你入门强化学习(六)
本文介绍了时间差分法(TD)中的两种经典算法:SARSA和Q-Learning。二者均为无模型强化学习方法,通过与环境交互估算动作价值函数。SARSA是On-Policy算法,采用ε-greedy策略进行动作选择和评估;而Q-Learning为Off-Policy算法,评估时选取下一状态中估值最大的动作。相比动态规划和蒙特卡洛方法,TD算法结合了自举更新与样本更新的优势,实现边行动边学习。文章通过生动的例子解释了两者的差异,并提供了伪代码帮助理解。
966 2
|
数据可视化 Ubuntu Linux
PyCharm连接远程服务器配置的全过程
相信很多人都遇见过这种情况:实验室成员使用同一台服务器,每个人拥有自己的独立账号,我们可以使用服务器更好的配置完成实验,毕竟自己哪有money拥有自己的3090呢。 通常服务器系统采用Linux,而我们平常使用频繁的是Windows系统,二者在操作方面存在很大的区别,比如我们实验室的服务器采用Ubuntu系统,创建远程交互任务时可以使用Terminal终端或者VNC桌面化操作,我觉得VNC很麻烦,所以采用Terminal进行实验,但是Terminal操作给我最不好的体验就是无法可视化中间实验结果,而且实验前后的数据上传和下载工作也让我头疼不已。
|
机器学习/深度学习 算法 搜索推荐
Elasticsearch:崭新的打分机制 - Learning To Rank (LTR)
【6月更文挑战第8天】Elasticsearch 的 Learning To Rank (LTR) 打分机制通过机器学习改进搜索结果排序,以适应复杂需求和用户行为。传统打分基于词频等,而 LTR 利用训练数据学习更合理的排序,考虑文本、用户行为等特征。示例代码展示了如何在 Elasticsearch 中运用 LTR。尽管实施 LTR 需要高质量训练数据和专业选择算法,但它能处理模糊搜索、多因素排序,提升搜索体验,增强应用价值和竞争力。随着技术发展,LTR 将在 Elasticsearch 中发挥更大作用。
456 6
|
人工智能 自动驾驶 数据库
领域大模型的训练需要什么数据?
领域大模型的训练需要什么数据?
1588 0
|
机器学习/深度学习 存储 人工智能
【机器学习】GBDT (Gradient Boosting Decision Tree) 深入解析
GBDT,全称为Gradient Boosting Decision Tree,即梯度提升决策树,是机器学习领域中一种高效且强大的集成学习方法。它通过迭代地添加决策树以逐步降低预测误差,从而在各种任务中,尤其是回归和分类问题上表现出色。本文将深入浅出地介绍GBDT的基本原理、算法流程、关键参数调整策略以及其在实际应用中的表现与优化技巧。
4298 2
|
Web App开发 人工智能
AI Studio 基本操作
AI Studio 基本操作
1139 0
|
机器学习/深度学习 数据处理
【机器学习】生成式模型与判别式模型有什么区别?
【5月更文挑战第10天】【机器学习】生成式模型与判别式模型有什么区别?
|
机器学习/深度学习 自然语言处理 并行计算
【深度学习】Attention的原理、分类及实现
文章详细介绍了注意力机制(Attention)的原理、不同类型的分类以及如何在Keras中实现Attention。文章涵盖了Attention的基本概念、计算区域、所用信息、结构层次等方面,并提供了实现示例。
2288 0
|
机器学习/深度学习 人工智能 算法
【机器学习】平均绝对误差 (MAE) 与均方误差 (MSE) 有什么区别?
【5月更文挑战第17天】【机器学习】平均绝对误差 (MAE) 与均方误差 (MSE) 有什么区别?

热门文章

最新文章