目录
一、引言
1.1 往期回顾
1.2 本期概要
二、Shared-Bottom Multi-task Model(SBMM)
2.1 技术原理
2.2 技术优缺点
2.3 业务代码实践
三、总结
一、引言
在朴素的深度学习ctr预估模型中(如DNN),通常以一个行为为预估目标,比如通过ctr预估点击率。但实际推荐系统业务场景中,更多是多种目标融合的结果,比如视频推荐,会存在视频点击率、视频完整播放率、视频播放时长等多个目标,而多种目标如何更好的融合,在工业界与学术界均有较多内容产出,由于该环节对实际业务影响最为直接,特开此专栏对推荐系统深度学习多目标问题进行讲述。
1.1 往期回顾
上一篇文章主要介绍了推荐系统多目标算法中的“样本Loss加权”,该方法在训练时Loss乘以样本权重实现对多种目标的加权,通过引导Loss梯度的学习方向,让模型参数朝着你设定的权重方向去学习。
1.2 本期概要
今天进一步深化,主要介绍Shared-Bottom Multi-task Model算法,该算法中文可译为“底部共享多任务模型”,该算法设定多个任务,每个任务设定多个目标,通过“Loss计算时调整每个任务的权重”,亦或是“每个塔单元内,多目标Loss计算时调整每个目标的权重”进行多任务多目标的调整。
二、Shared-Bottom Multi-task Model(SBMM)
2.1 技术原理
Shared-Bottom Multi-task Model(SBMM)全称为底层共享多任务模型,主要由底层共享网络、多任务塔、多目标输出构成。核心原理:通过构造多任务多目标样本数据,在Loss计算环节,将各任务Loss求和(或加权求和),对Loss求导(求梯度)后,逐步后向传播迭代。
- 底部网络:Shared-Bottom 网络通常位于底部,可以为一个DNN网络,或者emb+pooling+mlp的方式对input输入的稀疏(sparse)特征进行稠密(dense)化。
- 多个任务塔:底部网络上层接N个任务塔(Tower),每个塔根据需要可以定义为简单或复杂的多层感知器(mlp)网络。每个塔可以对应特定的场景,比如一二级页面场景。
- 多个目标:每个任务塔(Tower)可以输出多个学习目标,每个学习目标还可以像上一篇文章一样进行样本Loss加权。每个目标可以对应一种特定的指标行为,比如点击、时长、下单等。
2.2 技术优缺点
相比于上一篇文章提到的样本Loss加权融合法,以及后续文章将会介绍的MoE、MMoE方法,有如下优缺点:
优点:
- 可以对多级场景任务进行建模,使得ctcvr等点击后转化问题可以被深度学习
- 浅层参数共享,互相补充学习,任务相关性越高,模型的loss可以降低到更低
缺点:
- 跷跷板问题:任务没有好的相关性时,这种Hard parameter sharing会损害效果
2.3 业务代码实践
我们以小红书推荐场景为例,用户在一级发现页场景中停留并点击了“误杀3”中的一个视频笔记,在二级场景视频播放页中观看并点赞了视频。
跨场景多目标建模:我们定义一个SBMM算法结构,底层是一个3层的MLP(64,32,16),MLP出来后接一级场景Tower和二级场景Tower,一级场景任务中分别定义视频一级页“是否停留”、“停留时长”、“是否点击”,二级场景任务中分别定义“点击后播放时长”,“播放后是否点赞”
伪代码:
导入 pytorch 库
定义 SharedBottomMultiTaskModel 类 继承自 nn.Module:
定义 __init__ 方法 参数 (self, 输入维度, 隐藏层1大小, 隐藏层2大小, 隐藏层3大小, 输出任务1维度, 输出任务2维度):
初始化共享底部的三层全连接层
初始化任务1的三层全连接层
初始化任务2的三层全连接层
定义 forward 方法 参数 (self, 输入数据):
计算输入数据通过共享底部后的输出
从共享底部输出分别计算任务1和任务2的结果
返回任务1和任务2的结果
生成虚拟样本数据:
创建训练集和测试集
实例化模型对象
定义损失函数和优化器
训练循环:
前向传播: 获取预测值
计算每个任务的损失
反向传播和优化
PyTorch版本:
算法逻辑
- 导入必要的库。
- 定义一个类来表示共享底部和特定任务头部的模型结构。
- 在初始化方法中定义共享底部和两个独立的任务头部网络层。
- 实现前向传播函数,处理输入数据通过共享底部后分发到不同的任务头部。
- 生成虚拟样本数据。
- 定义损失函数和优化器。
- 编写训练循环。
- 进行模型预测。
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
class SharedBottomMultiTaskModel(nn.Module):
def __init__(self, input_dim, hidden1_dim, hidden2_dim, hidden3_dim, output_task1_dim, output_task2_dim):
super(SharedBottomMultiTaskModel, self).__init__()
# 定义共享底部的三层全连接层
self.shared_bottom = nn.Sequential(
nn.Linear(input_dim, hidden1_dim),
nn.ReLU(),
nn.Linear(hidden1_dim, hidden2_dim),
nn.ReLU(),
nn.Linear(hidden2_dim, hidden3_dim),
nn.ReLU()
)
# 定义任务1的三层全连接层
self.task1_head = nn.Sequential(
nn.Linear(hidden3_dim, hidden2_dim),
nn.ReLU(),
nn.Linear(hidden2_dim, output_task1_dim)
)
# 定义任务2的三层全连接层
self.task2_head = nn.Sequential(
nn.Linear(hidden3_dim, hidden2_dim),
nn.ReLU(),
nn.Linear(hidden2_dim, output_task2_dim)
)
def forward(self, x):
# 计算输入数据通过共享底部后的输出
shared_output = self.shared_bottom(x)
# 从共享底部输出分别计算任务1和任务2的结果
task1_output = self.task1_head(shared_output)
task2_output = self.task2_head(shared_output)
return task1_output, task2_output
# 构造虚拟样本数据
torch.manual_seed(42) # 设置随机种子以保证结果可重复
input_dim = 10
task1_dim = 3
task2_dim = 2
num_samples = 1000
X_train = torch.randn(num_samples, input_dim)
y_train_task1 = torch.randn(num_samples, task1_dim) # 假设任务1的输出维度为task1_dim
y_train_task2 = torch.randn(num_samples, task2_dim) # 假设任务2的输出维度为task2_dim
# 创建数据加载器
train_dataset = TensorDataset(X_train, y_train_task1, y_train_task2)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
# 实例化模型对象
model = SharedBottomMultiTaskModel(input_dim, 64, 32, 16, task1_dim, task2_dim)
# 定义损失函数和优化器
criterion_task1 = nn.MSELoss()
criterion_task2 = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练循环
num_epochs = 10
for epoch in range(num_epochs):
model.train()
running_loss = 0.0
for batch_idx, (X_batch, y_task1_batch, y_task2_batch) in enumerate(train_loader):
# 前向传播: 获取预测值
outputs_task1, outputs_task2 = model(X_batch)
# 计算每个任务的损失
loss_task1 = criterion_task1(outputs_task1, y_task1_batch)
loss_task2 = criterion_task2(outputs_task2, y_task2_batch)
#print(f'loss_task1:{loss_task1},loss_task2:{loss_task2}')
total_loss = loss_task1 + loss_task2
# 反向传播和优化
optimizer.zero_grad()
total_loss.backward()
optimizer.step()
running_loss += total_loss.item()
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/len(train_loader):.4f}')
# 模型预测
model.eval()
with torch.no_grad():
test_input = torch.randn(1, input_dim) # 构造一个测试样本
pred_task1, pred_task2 = model(test_input)
print(f'任务1预测结果: {pred_task1}')
print(f'任务2预测结果: {pred_task2}')
三、总结
本文从技术原理、技术优缺点方面对推荐系统深度学习多任务多目标“Shared-Bottom Multi-task Model”算法进行讲解,该模型使用深度学习模型对多个任务场景多个目标的业务问题进行建模,使得用户在多个场景连续性行为可以被学习,在现实推荐系统业务中是比较基础的方法,后面本专栏还会陆续介绍MoE、MMoE等多任务多目标算法,期待您的关注和支持。
如果您还有时间,欢迎阅读本专栏的其他文章:
【深度学习】多目标融合算法(一):样本Loss加权(Sample Loss Reweight)
【深度学习】多目标融合算法(二):底部共享多任务模型(Shared-Bottom Multi-task Model)