文章目录
- 1、动机与挑战(信用卡业务广告)
- 2、信用卡用户整体架构:
- 3、模型结构
- 3.1、核心部分
-
Modeling the Sequential Dependence among Audience Multi-step Conversions with Multi-task Learning in Targeted Display Advertising
-
论文发表在 KDD-2021。论文做的是金融场景下(信用卡业务)的顺序依赖性多任务问题解决框架。
-
在ESMM的基础上,利用不用任务之间的顺序依赖关系,文章提出了一种自适应信息模型传输多任务AITM(Adaptive Information Transfer Multi-task)框架。基于AIT模块,利用self-attention自适应地提取不同转换阶段的传输信息进行融合输出。现已应用于美团app中。
1、动机与挑战(信用卡业务广告)
- 用户多步转化过程中的路径序列依赖更长。
- 在多步转化的长序列依赖中(前一步骤发生了,后一步骤才可能发生),正反馈(正样本)是逐步稀硫的,类别不平衡越来越严重。
信用卡业务的5个阶段如下,一般来说阶段越低,转换效率越高。业务中,关心的是核卡成功率和激活成功率。 - 曝光
- 点击
- 申请
- 核卡(授信):意味着用户信用良好,通过申请并被授予了一定的信用卡额度。实时判断
- 激活:用户在授信并且收到邮寄的信用卡之后,可以激活信用卡并使用。是否激活的标签通常比较难获得,因为信用卡邮寄需要一定的时间,而且用户主动去激活也需要一定的表现期,所以这里类别不平衡更为严重。在这里,我们通常看用户是否会在核卡后14天内激活信用卡。
2、信用卡用户整体架构:
- 简单了解一下就ok。
3、模型结构
即图a为MMOE,图b为ESMM,图c即为AITM。
AITM其实就是在ESMM的基础上,引入了attention机制(和self-attention类似但有区别)来建模相邻任务的输出(即当前任务的输出为用attention综合了前一任务的输出和当前任务的输出)。
- 设有T个任务,那模型输出的各转换步骤的概率的顺序依赖为:y1≥y2≥…≥yT。
- tower论文用的是MLP(也可以是其他,比如deepfm, nfm等)
- 两个相邻的任务,通过AIT模块(即attention)进行特征交互,用于学习两相邻任务应该迁移什么信息。AIT模块的输出继续作为下一任务的输入之一。
其实整体来看和MMOE差不多,区别就是在后面加了attention来使相邻任务的特征进行交互(即控制应该迁移前一任务的什么信息到当前任务)。
3.1、核心部分
-
aitm也采用share embedding,这样有利于缓解后一个任务的类别不平衡的问题,训练出一个更好的embedding;还可以减少模型的参数。
-
ait模块(即attention)和self-attention类似,但有区别。这里q,k,v向量通过全连接层映射得到,然后qk内积(对应位置直接相乘)并缩放,得到 (bs, 2) ,然后softmax的注意力得分;然后注意力得分.unsqueeze(dim=-1)得 (bs, 2, 1),和 v向量 (bs, 2, dim) 直接内积得 (bs, 2, dim),最后.sum(dim=1) 即两向量通过注意力得分加权相加,得 (bs, dim)。
ait代码实现如下:
class AttentionLayer(nn.Module):
"""attention for info tranfer
Args:
dim (int): attention dim
Shape:
Input: (batch_size, 2, dim):(2, dim)代表相邻两任务的输出特征
Output: (batch_size, dim)
"""
def __init__(self, dim=32):
super().__init__()
self.dim = dim
self.q_layer = nn.Linear(dim, dim)
self.k_layer = nn.Linear(dim, dim)
self.v_layer = nn.Linear(dim, dim)
self.softmax = nn.Softmax(dim=1)
def forward(self, x):
Q = self.q_layer(x)
K = self.k_layer(x)
V = self.v_layer(x)
# 注意Q,K对应文职相乘
a = torch.sum(torch.mul(Q, K), -1) / torch.sqrt(torch.tensor(self.dim)) # (bs, 2)
a = self.softmax(a)
# 各向量乘上对应的softmax得分,然后向量直接相加
outputs = torch.sum(torch.mul(torch.unsqueeze(a, -1), V), dim=1) # (bs, 2, 1)*(bs, 2, dim) sum-> (bs, dim)
return outputs
-
每个ait模块的输出经过mlp映射过sigmoid后即可输出对应任务的概率。
-
另外地,aitm为确保任务之间的顺序依赖性,给二分类交叉熵损失函数加上了一个约束项。aitm用这种方法对目标函数进行约束,保证了最终结果的合理性,同时也可以提高最终结果的准确性。约束项的计算法方式如下;其原理很假单,也就是如果当前任务的概率如果大于前一任务的概率 (不正常的情况),那损失为y_t - y_t-1;若当前任务概率小于前一任务的概率 (正常情况),那就不惩罚,损失为0。
aitm loss代码实现为:
# 写在了model class里
def calibrator_loss(self, label1, pred1, label2, pred2, constraint_weight=0.6):
"""
由于aitm针对的问题是具有多步转化顺序依赖关系的多任务学习,
因此前一个任务的预估值应该远大于下一个任务的预估值(全样本空间中的ctr一定比cvr高),
因此文章提出了一个calibrator作为一个loss惩罚项。
事实上就是当yt > y_{t-1}时,即认为是不合理的,惩罚一波loss;否则合理的话,这一项就为0。文章用这种方法对目标函数进行约束,保证了最终结果的合理性,同时也可以提高最终结果的准确性。
"""
# 和nn.BCELoss()一样的,一个是函数,一个是类而已。
# 都默认对batch内所有loss取平均
loss1 = F.binary_cross_entropy(pred1, label1) # 标量
loss2 = F.binary_cross_entropy(pred2, label2)
label_constraint = torch.maximum(pred2 - pred1, torch.zeros_like(label1))
constraint_loss = torch.sum(label_constraint) / len(label1) # 标量,公式里是要取平均的
# 这里可以自由发挥
loss = (loss1 + loss2)/2 + constraint_weight * constraint_loss
# loss = loss1 + loss2 + constraint_weight * constraint_loss
return loss
总的来说,创新点在ait模块用于交互相邻任务的特征,改进了loss确保了相邻任务的顺序依赖性。
参考链接:https://zhuanlan.zhihu.com/p/506921131