3.4.2. 任务依赖建模

前面介绍的多目标方法主要解决任务间的相关性冲突,但现实场景中任务间往往存在明确的依赖关系。用户行为具有天然的时序性:曝光→点击→转化,这种严格的依赖关系带来了新的挑战。

传统方法在处理这种依赖时面临两个核心问题:样本选择偏差(CVR模型在点击样本上训练,却要在全量样本上预测)和数据稀疏性(转化事件极其稀少)。

本节介绍两个全空间建模方法:ESSM解决经典的CTR-CVR联合建模问题,ESM2将思想扩展到更复杂的多阶段行为链路。

3.4.2.1. ESSM

../../_images/essm_sample_bias.png

图3.4.5 点击率和转化率预估的样本空间

在推荐系统的用户行为链中,存在严格的时序依赖关系。以电商场景为例:

(3.4.6)\[\text{曝光(Impression)} \rightarrow \text{点击(Click)} \rightarrow \text{转化(Conversion)}\]

这种链式结构导致两个关键问题:

  1. 样本选择偏差(Sample Selection Bias):传统CVR模型仅在点击样本(CTR正样本)上训练,但线上预估需覆盖全量曝光样本,训练/预估样本分布差异导致泛化能力下降

  2. 数据稀疏性(Data Sparsity):转化样本量 = 曝光量 × CTR × CVR,典型场景:CTR≈2%, CVR≈0.5% → 转化样本仅为曝光的万分之一,稀疏样本难以支撑复杂模型学习

ESSM (Entire Space Multi-task Model) 通过概率图约束重建任务关系:

../../_images/essm.png

图3.4.6 ESSM模型结构

  • 输入层:全量曝光样本特征 \(\mathbf{x}\)

  • 共享表征层:基础特征提取模块(原论文中采用的是Shared-Bottom的简单共享结构,也可将其直接替换成MMoE、PLE等复杂的底层共享模型)

  • 任务塔层:

    • CTR Tower:预测点击概率 \(pCTR = f_{ctr}(\mathbf{h})\)

    • CVR Tower:预测转化概率 \(pCVR = f_{cvr}(\mathbf{h})\)

  • 输出层:

    • \(pCTR = f_{ctr}(\mathbf{h})\)\(pCVR = f_{cvr}(\mathbf{h})\),其中\(pCVR\)不用用来计算Loss

    • \(pCTCVR = pCTR \times pCVR\),该值用来计算从曝光空间到转化的Loss

损失函数的设计:

(3.4.7)\[\mathcal{L} = \mathcal{L}_{CTR} + \mathcal{L}_{CTCVR}\]

其中:

  • \(\mathcal{L}_{CTR}\) 是标准的二分类交叉熵损失,使用全量曝光样本:

    (3.4.8)\[\mathcal{L}_{CTR} = - \frac{1}{N} \sum_{i=1}^N \left[ y_i^{click} \log(pCTR_i) + (1 - y_i^{click}) \log(1 - pCTR_i) \right]\]
  • \(\mathcal{L}_{CTCVR}\) 是CTCVR任务的交叉熵损失, 通过概率转化公式\(pCTCVR = pCTR \times pCVR\)使得CVR Tower的参数更新是在曝光空间下进行的

    (3.4.9)\[\mathcal{L}_{CTCVR} = - \frac{1}{N} \sum_{i=1}^N \left[ y_i^{click} \cdot y_i^{conv} \log(pCTCVR_i) + (1 - y_i^{click} \cdot y_i^{conv}) \log(1 - pCTCVR_i) \right]\]

ESSM的核心创新在于CVR塔的梯度来源,CVR塔同时接收两种梯度:

(3.4.10)\[\nabla_{CVR} = \underbrace{\frac{\partial \mathcal{L}_{CTCVR}}{\partial pCTCVR} \cdot pCTR}_{\text{全空间梯度}} + \underbrace{\frac{\partial \mathcal{L}_{shared}}{\partial \mathbf{h}}}_{\text{共享层梯度}}\]

MMOE代码实践

def build_essm_model(
        feature_columns,
        task_tower_dnn_units=[128, 64],
        ):
    input_layer_dict = build_input_layer(feature_columns)
    # 构建特征嵌入表字典
    group_embedding_feature_dict = build_group_feature_embedding_table_dict(feature_columns, input_layer_dict, prefix="embedding/")

    # 连接不同组的嵌入向量作为各个网络的输入
    dnn_inputs = concat_group_embedding(group_embedding_feature_dict, 'dnn')

    ctr_output_logits = DNNs(task_tower_dnn_units + [1], name="ctr_dnn")(dnn_inputs)
    cvr_output_logits = DNNs(task_tower_dnn_units + [1], name="cvr_dnn")(dnn_inputs)

    ctr_output_prob = PredictLayer(name="ctr_output")(ctr_output_logits)
    cvr_output_prob = PredictLayer(name="cvr_output")(cvr_output_logits)
    ctcvr_output_prob = tf.keras.layers.Lambda(lambda x: tf.multiply(x[0], x[1]))([ctr_output_prob, cvr_output_prob])

    # 构建模型
    model = tf.keras.Model(inputs=list(input_layer_dict.values()), outputs=[ctr_output_prob, ctcvr_output_prob])
    return model

完整训练流程:

  1. 导入相关包

import sys
import funrec
from funrec.utils import build_metrics_table
  1. 样本和特征处理

config = funrec.load_config('essm')
train_data, test_data = funrec.load_data(config.data)
feature_columns, processed_data = funrec.prepare_features(config.features, train_data, test_data)
  1. 模型训练

model = funrec.train_model(config.training, feature_columns, processed_data)
  1. 模型评估

metrics = funrec.evaluate_model(model, processed_data, config.evaluation, feature_columns)
print(build_metrics_table(metrics))
+----------------+---------------+-------------+-----------------+----------------+--------------+------------------------+-----------------------+
|   auc_is_click |   auc_is_like |   auc_macro |   gauc_is_click |   gauc_is_like |   gauc_macro |   valid_users_is_click |   valid_users_is_like |
+================+===============+=============+=================+================+==============+========================+=======================+
|         0.6027 |        0.6566 |      0.6297 |          0.5734 |         0.5835 |       0.5785 |                    928 |                   530 |
+----------------+---------------+-------------+-----------------+----------------+--------------+------------------------+-----------------------+

3.4.2.2. ESM2

ESSM成功解决了曝光→点击→转化这一两阶段行为链路的样本偏差问题,但在真实工业场景中,用户行为链路往往更长更复杂。

../../_images/esm2_seq.png

图3.4.7 用户下单链路图

如图所示,用户从曝光到转化可能会有非常多条的路径,例如 曝光->点击->加入购物车->购买曝光->点击->加入许愿池->加入购物车->购买等。为了方便后续建模,对点击后的行为分解做了进一步的简化,将加入购物车、加入心愿单归并为决定行为(Deterministic Action,DAction),将其余行为归并为其他行为(Other Action,OAction)

../../_images/esm2_seq_2.png

图3.4.8 简化后的用户下单链路图

为了更好理解后续建模时的数学表达,先对简化后图中的过程,做进一步的数学表示。

  • \(y_1=p(\text{点击}|\text{曝光})\)

  • \(y_2=p(\text{决定行为}|\text{点击})\)

  • \(y_3=p(\text{购买}|\text{决定行为})\)

  • \(y_4=p(\text{购买}|\text{其他行为})\)

根据上述定义,更便于理解ESM2模型的结构图:

../../_images/esm2.png

图3.4.9 ESM2模型结构图

ESM2模型有四个塔,分别用来预测上述的\(y_1,y_2,y_3\)\(y_4\),对于这四个塔的输出并不是算4个Loss,而是分别计算曝光->点击曝光->决定行为曝光->购买这三个Loss。可以很明显的看出,这三个Loss都是在曝光空间上计算的,和ESSM在曝光空间优化CVR有着异曲同工之处。下面对于上述的三个Loss做简单的介绍,下面的\(BCE_{Loss}\)表示的是二元交叉熵损失。

\(L_{ctr}\)点击率预估损失:

(3.4.11)\[\begin{split}\begin{aligned} L_{ctr} &= \frac{1}{N} \text{BCE}_{\text{Loss}}(y_{\text{isClick}}^i, \text{pCTR}_i) \\ & = \frac{1}{N} \text{BCE}_{\text{Loss}}(y_{\text{isClick}}^i, \text{y}_1^i) \end{aligned}\end{split}\]

\(L_{ctavr}\)点击且决定行为概率预估损失:

(3.4.12)\[\begin{split}\begin{aligned} L_{ctavr} &= \frac{1}{N} \text{BCE}_{\text{Loss}}(y_{\text{isDAction}}^i, \text{pCTAVR}_i) \\ & = \frac{1}{N} \text{BCE}_{\text{Loss}}(y_{\text{isDAction}}^i, \text{y}_1^i \cdot \text{y}_2^i) \end{aligned}\end{split}\]

\(L_{ctcvr}\)转化率预估损失:

(3.4.13)\[\begin{split}\begin{aligned} L_{ctcvr} &= \frac{1}{N} \text{BCE}_{\text{Loss}}(y_{\text{isPurchuse}}^i, \text{pCTCVR}_i) \\ & = \frac{1}{N} \text{BCE}_{\text{Loss}}(y_{\text{isPurchuse}}^i, \text{y}_1^i (\text{y}_2^i \cdot \text{y}_3^i + (1 - \text{y}_2^i) \cdot \text{y}_4^i)) \end{aligned}\end{split}\]

从简化后的用户下单链路图中可以看出,用户最终转化是有两条链路的,分别为:

  • 曝光->点击->决定行为->购买=>\(y_1 \cdot y_2 \cdot y_3\)

  • 曝光->点击->其他行为->购买=>\(y_1 \cdot (1-y_2) \cdot y_4\)

合并上述两条链路的结果就可以得到\(pCTCVR=y_1(y_2 \cdot y_3 + (1-y_2) \cdot y_4)\)

最终上述三个损失通过加权融合的方式进行联合优化,

(3.4.14)\[L_{final} = w_{ctr} \cdot L_{ctr} + w_{ctavr} \cdot L_{ctavr} + w_{ctcvr} \cdot L_{ctcvr}\]

其中\(w_{ctr},w_{ctavr},w_{ctcvr}\)分别为三个损失的权重。

ESM2通过这种多阶段的概率乘积方式,将复杂的用户行为链路分解为多个可建模的子任务,同时确保每个任务都在曝光空间中进行联合优化。这种设计不仅有效解决了样本选择偏差问题,还通过共享底层特征表征,降低了数据稀疏性对模型性能的影响。更重要的是,ESM2提供了一种通用的建模思路,可以灵活扩展到更长的行为链路和更多样化的用户决策路径中。