如果你在广告技术领域工作,或者关注推荐系统、大模型应用,最近可能注意到一个现象:Meta(原Facebook)的广告系统正在经历一场静默但深刻的变革。过去几年,从简单的CTR预估到复杂的多目标优化,广告排序技术似乎已经进入平台期。但如果你仔细观察Meta近期的技术论文和工程实践,会发现一个关键趋势:他们正在将大语言模型(LLM)的“扩展定律”(Scaling Law)思想,系统地引入到广告排序这个传统领域,而核心载体就是“多阶段序列模型”。
这不仅仅是“用了一个更大的模型”那么简单。其背后是一个根本性的范式转移:从传统的“特征工程+单点模型”思维,转向“序列化用户行为作为上下文,并相信模型规模与数据规模共同驱动效果”的LLM式思维。对于大多数团队而言,这意味着过去积累的很多精细化特征工程和模型微调经验,其边际效益正在急剧下降。
本文要解决的,正是这个认知断层。我们将深入拆解Meta广告排序中“多阶段序列模型”的技术内核,并解释它如何借鉴LLM的扩展定律实现效果飞跃。你会看到:
- 为什么传统的广告排序模型会碰到天花板?问题不在算法本身,而在信息利用的范式。
- “多阶段”和“序列模型”分别解决了什么?这不是两个独立概念,而是一个协同进化的工程体系。
- LLM的扩展定律在这里如何体现?关键不在于使用Transformer架构,而在于相信“规模”本身是一种解决方案。
- 这套技术体系离我们有多远?我们将剖析其核心思想,并给出一个从传统CTR模型向此范式演进的、可操作的实践路径。
无论你是算法工程师希望升级技术栈,还是业务负责人思考技术投入方向,理解这场正在发生的变革,都能帮助你在下一波技术红利中占据先机。
1. 传统广告排序的天花板与序列模型的破局点
要理解新范式的价值,必须先看清旧范式的瓶颈。传统的广告排序(如CTR预估)核心流程可以概括为:收集用户静态特征(性别、年龄)、广告特征(类别、创意)和上下文特征(时间、位置),通过特征交叉、深度网络(如DeepFM、DCN)进行建模,最终输出一个预估分数。
这套体系的核心问题在于“信息表示的碎片化与静态化”:
- 特征工程依赖专家经验:模型效果严重依赖于人工挖掘和组合的高阶特征。这是一个耗时、难以规模化且容易过拟合的过程。
- 行为信息被严重压缩:用户过去一小时、一天、一周的点击、浏览、搜索序列,通常被压缩成几个统计值(如历史点击率、最近点击品类),大量时序和结构信息丢失。
- 模型容量与数据形式不匹配:即使用上最复杂的深度模型,它接收的输入仍然是一组扁平的特征向量,无法原生地理解用户行为背后的“故事”和“意图演变”。
多阶段序列模型的破局思路,正是直击上述痛点:
- 序列化:不再将用户行为压缩为统计特征,而是将用户与内容的交互历史(点击、曝光、停留、搜索词)按时间顺序组织成序列,作为模型的原始输入。这保留了最丰富的信息。
- 多阶段:承认“一刀切”的巨型模型不现实。它将排序任务拆解为“召回 -> 粗排 -> 精排 -> 重排”等多个阶段,每个阶段使用不同复杂度的序列模型,实现精度与效率的平衡。
- 规模优先:借鉴LLM的成功经验,当数据(用户序列)和模型容量(参数规模)同步扩大时,模型性能会按照可预测的“扩展定律”提升,从而减少对精巧特征工程的依赖。
简单来说,新范式的核心假设是:只要给模型足够长的、原生的用户行为序列和足够大的模型容量,它就能自己学会里面隐藏的复杂模式,甚至超越人工设计的特征。这标志着广告排序从“特征驱动”进入了“数据与规模驱动”的新阶段。
2. 核心概念拆解:多阶段、序列模型与扩展定律
2.1 什么是“多阶段”排序?
在工业级推荐/广告系统中,面对海量候选集(数百万甚至数十亿),直接用一个复杂模型对所有候选进行精准打分是不现实的。因此,系统通常采用漏斗形的多阶段处理:
| 阶段 | 目标 | 候选集规模 | 模型特点 | 核心挑战 |
|---|---|---|---|---|
| 召回 | 从全量池中快速找出可能与用户相关的几百/几千个候选。 | 十亿级 | 极简、高效。常用双塔模型、向量检索。 | 覆盖率、多样性。 |
| 粗排 | 对召回结果进行初步质量过滤,将候选集缩减到几百个。 | 千级 | 较轻量级的排序模型,平衡精度与速度。 | 与精排结果的一致性。 |
| 精排 | 对粗排结果进行精准打分排序,决定最终展示顺序。 | 百级 | 最复杂、最精确的模型,使用全部特征。 | 精准度、延迟要求。 |
| 重排 | 对精排结果进行业务规则调整、多样性打散、上下文优化等。 | 几十个 | 规则或轻量级模型,处理列表级目标。 | 用户体验、业务指标。 |
Meta的进化在于,将序列模型的能力从精排阶段,向上游的粗排甚至召回阶段渗透,形成全链路的序列感知能力。
2.2 什么是用于排序的“序列模型”?
这里的序列模型特指能够处理变长行为序列的深度学习模型,其演进路径如下:
- 基础序列模型:如GRU、LSTM。可以处理用户行为序列,但难以并行,对长序列建模能力有限。
- Transformer/自注意力模型:这是当前的主流。通过Self-Attention机制,模型可以同时关注序列中任何位置的信息,非常适合捕捉用户长期兴趣和短期意图的交互。
- 用户行为序列的构建:序列的每个元素通常是一个“交互事件”的嵌入表示,例如
[item_embedding, event_type_embedding, time_embedding]的组合。这比单一的item_id包含更多信息。
一个简化的用户行为序列示意:
[ 搜索“跑步鞋” -> 点击商品A -> 浏览商品A详情页30秒 -> 点击商品B -> 加入购物车商品C -> 搜索“运动袜” ]模型的目标是,给定这个序列,预测用户对下一个候选广告(例如一款新的跑步鞋广告)的互动概率。
2.3 LLM的“扩展定律”是什么?
在LLM领域,扩展定律指模型性能(如损失函数)与三个关键因素之间的幂律关系:
- 模型参数规模(N)
- 训练数据量(D)
- 计算量(C)
核心结论是:只要同步、均衡地扩大N、D、C,模型性能就会持续、可预测地提升,且不会很快饱和。这打破了传统机器学习中“模型大了容易过拟合”的认知。
Meta将其引入广告排序的关键洞察是:在广告场景中,“数据规模(D)”可以重新定义为“有效的用户行为序列长度与多样性”。当模型架构(Transformer)允许处理更长序列(更大的有效D),并且模型容量(N)同步增加时,广告排序的关键指标(如AUC、在线收入)也同样遵循扩展定律持续提升。
这意味着,技术投入的重点从“设计更巧妙的网络结构”部分转向了“如何高效地构建、存储、处理更长的用户行为序列”以及“如何训练和部署更大的序列模型”。
3. 技术架构演进:从精排序列模型到全链路序列化
我们来看一个具体的架构演进示例,理解序列模型如何从精排走向全链路。
3.1 阶段一:精排引入用户行为序列(传统做法)
这是很多公司目前的阶段。在精排模型中,将用户最近N个行为item的ID序列作为输入,通过一个独立的序列建模模块(如Transformer Encoder)提取用户兴趣表征,再与其他特征拼接,输入到主网络。
# 伪代码示例:精排模型中的序列兴趣提取模块 import torch import torch.nn as nn class SequenceInterestModule(nn.Module): def __init__(self, item_embed_dim, seq_hidden_dim, num_heads): super().__init__() self.item_embedding = nn.Embedding(num_items, item_embed_dim) self.position_embedding = nn.Embedding(max_seq_len, item_embed_dim) self.transformer_encoder = nn.TransformerEncoder( nn.TransformerEncoderLayer(d_model=item_embed_dim, nhead=num_heads), num_layers=2 ) self.attention_pooling = nn.Linear(item_embed_dim, 1) def forward(self, user_behavior_seq): # user_behavior_seq: [batch_size, seq_len] item_id序列 seq_emb = self.item_embedding(user_behavior_seq) # [batch, seq, dim] positions = torch.arange(seq_len).expand(batch_size, seq_len) pos_emb = self.position_embedding(positions) seq_input = seq_emb + pos_emb # Transformer编码 seq_output = self.transformer_encoder(seq_input) # [batch, seq, dim] # 注意力池化得到用户兴趣向量 attn_weights = torch.softmax(self.attention_pooling(seq_output), dim=1) user_interest = torch.sum(attn_weights * seq_output, dim=1) # [batch, dim] return user_interest # 在精排主模型中调用 class RankModel(nn.Module): def __init__(self, ...): self.seq_module = SequenceInterestModule(...) self.dnn = MLP(...) def forward(self, user_feat, ad_feat, user_behavior_seq): interest_vec = self.seq_module(user_behavior_seq) combined_feat = torch.cat([user_feat, ad_feat, interest_vec], dim=-1) score = self.dnn(combined_feat) return score局限性:序列信息只在精排阶段使用,召回和粗排阶段仍然依赖简单的用户画像,存在信息断层和一致性偏差。
3.2 阶段二:粗排序列化与向量化检索
Meta的关键进展是让粗排甚至召回也“感知”序列。
- 粗排序列化:训练一个轻量级的序列模型(如层数更少的Transformer),输入用户短期序列,输出用户当前兴趣向量。这个向量与候选广告向量进行快速内积运算,实现高效的粗排打分。模型必须极度精简以满足延迟要求。
- 召回向量化:使用双塔模型,用户塔的输入就是用户行为序列(通过一个简单的序列编码器),产出用户向量;广告塔产出广告向量。通过近似最近邻搜索(ANN)进行召回。这实现了基于序列语义的召回,而不仅仅是基于协同过滤或标签匹配。
# 伪代码示例:用于召回/粗排的轻量级序列双塔模型 class LightSeqEncoder(nn.Module): """轻量级序列编码器,用于用户塔""" def __init__(self, item_embed_dim, output_dim): super().__init__() self.item_embed = nn.Embedding(num_items, item_embed_dim) # 使用简单的Mean Pooling或单层Transformer self.pooling = nn.AdaptiveAvgPool1d(1) # 或一个单层Transformer def forward(self, behavior_seq): # behavior_seq: [batch, seq_len] emb_seq = self.item_embed(behavior_seq) # [batch, seq, dim] # 简单平均池化 user_vec = emb_seq.mean(dim=1) # [batch, dim] # 或者通过一个线性层投影到输出维度 user_vec = self.projection(user_vec) return user_vec class AdEncoder(nn.Module): """广告塔,编码广告特征""" def __init__(self, ad_feat_dim, output_dim): super().__init__() self.mlp = MLP(ad_feat_dim, [output_dim]) def forward(self, ad_features): return self.mlp(ad_features) # 训练时,使用对比学习损失(如InfoNCE) def info_nce_loss(user_vec, pos_ad_vec, neg_ad_vecs, temperature=0.1): # user_vec: [batch, dim] # pos_ad_vec: [batch, dim] # neg_ad_vecs: [batch, num_neg, dim] pos_sim = torch.sum(user_vec * pos_ad_vec, dim=-1) / temperature # [batch] neg_sim = torch.matmul(user_vec.unsqueeze(1), neg_ad_vecs.transpose(1,2)).squeeze(1) / temperature # [batch, num_neg] logits = torch.cat([pos_sim.unsqueeze(1), neg_sim], dim=1) # [batch, 1+num_neg] labels = torch.zeros(logits.shape[0], dtype=torch.long).to(logits.device) # 正样本在位置0 loss = nn.CrossEntropyLoss()(logits, labels) return loss3.3 阶段三:统一序列建模与扩展定律实践
这是Meta目前探索的前沿。其核心是构建一个“统一的行为序列基础模型”。
- 超长序列:不再局限于最近100或200个行为,而是尝试处理成千上万个用户历史行为事件,构建真正的“用户终身行为序列”。
- 多任务预训练:在一个巨大的、去标识化的用户行为序列语料库上,使用类似LLM的预训练任务(如下一个行为预测、行为掩码恢复)来训练一个庞大的Transformer模型。这个模型学习的是用户行为背后的通用模式和意图演化规律。
- 高效微调与服务:将这个“基础模型”作为特征提取器,或者通过Prompt Tuning、Adapter等参数高效微调技术,快速适配到下游不同的排序任务(点击率、转化率、时长等)。在服务时,可能需要复杂的模型蒸馏、裁剪和加速技术,以将大模型的能力迁移到各阶段的小模型中。
扩展定律在此体现为:当这个统一序列基础模型的参数规模(N)和用于预训练的用户行为序列数据量(D)同步扩大时,其在所有下游广告任务上的表现都获得持续提升。这本质上是在广告领域复现了“大预言模型”的成功路径。
4. 工程实现挑战与核心解决方案
将上述架构投入生产,面临巨大挑战。Meta的工程实践给出了方向性答案。
4.1 挑战一:超长序列的存储与实时读取
用户长达数月甚至数年的行为序列,数据量巨大。精排模型需要毫秒级读取。
- 解决方案:分层存储与在线聚合。
- 热存储:用户最近几小时/天的详细行为(包含完整上下文),存储在内存数据库(如Redis)或内存缓存中,供精排使用。
- 温存储:更早的历史行为(如过去30天),存储在列式数据库(如ClickHouse)或特征平台中,可以按需读取或用于离线训练。
- 冷存储/归档:长期历史行为,存储在对象存储(如HDFS)中,主要用于离线模型训练和长期兴趣挖掘。
- 实时聚合服务:一个独立的在线服务,实时消费用户行为流,并预先计算好不同时间窗口的聚合特征(如过去1小时点击品类分布),供粗排和召回快速读取。
4.2 挑战二:序列模型的在线推理延迟
Transformer模型的自注意力复杂度是序列长度的平方级(O(n²)),直接处理长序列延迟不可接受。
- 解决方案:模型压缩与推理优化组合拳。
- 模型蒸馏:用大序列模型(教师)训练小序列模型(学生),将知识压缩到可部署的模型中。
- 序列裁剪与重要性采样:不是使用全部历史序列,而是通过一个轻量级模型预测哪些历史行为对当前预测最重要,只选取Top-K个行为输入精排模型。
- 高效的注意力机制:采用线性注意力(Linear Attention)、局部窗口注意力等近似方法,降低计算复杂度。
- 硬件与编译优化:使用TensorRT、TVM等工具对模型图进行编译优化,充分利用GPU/ASIC的算力。
4.3 挑战三:大规模序列模型的训练
训练一个覆盖数十亿用户、处理超长序列的Transformer模型,需要巨大的算力和数据管道。
- 解决方案:分布式训练与负样本采样。
- 数据并行:将海量训练数据分片到多个GPU/机器上。
- 模型并行:当单个GPU放不下整个模型时,将模型的不同层分布到不同设备上。
- 流水线并行:将模型按层切分,让不同的设备同时处理同一个批次数据的不同阶段,提高设备利用率。
- 负样本采样:在召回/粗排模型训练中,全局负采样(从全库随机选负样本)至关重要,但计算代价高。通常采用流式训练,在数据流中实时构造批次内负样本(Batch内其他样本作为负例),并结合定期从全库采样的“困难负样本”进行增强。
5. 从理论到实践:一个简化的精排序列模型实现示例
让我们通过一个PyTorch的简化示例,将上述部分概念串联起来。我们实现一个集成了用户行为序列Transformer的精排模型。
import torch import torch.nn as nn import torch.nn.functional as F import numpy as np class MultiHeadAttention(nn.Module): """简化的多头自注意力,用于教学示例""" def __init__(self, embed_dim, num_heads): super().__init__() self.embed_dim = embed_dim self.num_heads = num_heads self.head_dim = embed_dim // num_heads assert self.head_dim * num_heads == embed_dim, "embed_dim must be divisible by num_heads" self.qkv_proj = nn.Linear(embed_dim, 3 * embed_dim) self.out_proj = nn.Linear(embed_dim, embed_dim) def forward(self, x, mask=None): # x: [batch_size, seq_len, embed_dim] batch_size, seq_len, _ = x.shape qkv = self.qkv_proj(x) # [batch, seq, 3*embed_dim] qkv = qkv.reshape(batch_size, seq_len, 3, self.num_heads, self.head_dim) qkv = qkv.permute(2, 0, 3, 1, 4) # [3, batch, num_heads, seq, head_dim] q, k, v = qkv[0], qkv[1], qkv[2] # 缩放点积注意力 attn_scores = torch.matmul(q, k.transpose(-2, -1)) / (self.head_dim ** 0.5) # [batch, num_heads, seq, seq] if mask is not None: attn_scores = attn_scores.masked_fill(mask == 0, float('-inf')) attn_weights = F.softmax(attn_scores, dim=-1) attn_output = torch.matmul(attn_weights, v) # [batch, num_heads, seq, head_dim] # 合并多头 attn_output = attn_output.transpose(1, 2).contiguous().view(batch_size, seq_len, self.embed_dim) output = self.out_proj(attn_output) return output class TransformerBlock(nn.Module): def __init__(self, embed_dim, num_heads, ff_dim, dropout=0.1): super().__init__() self.attention = MultiHeadAttention(embed_dim, num_heads) self.norm1 = nn.LayerNorm(embed_dim) self.norm2 = nn.LayerNorm(embed_dim) self.ffn = nn.Sequential( nn.Linear(embed_dim, ff_dim), nn.GELU(), nn.Dropout(dropout), nn.Linear(ff_dim, embed_dim), nn.Dropout(dropout) ) def forward(self, x, mask=None): # 残差连接与层归一化 attn_out = self.attention(x, mask) x = self.norm1(x + attn_out) ffn_out = self.ffn(x) x = self.norm2(x + ffn_out) return x class UserBehaviorEncoder(nn.Module): """用户行为序列编码器""" def __init__(self, num_items, embed_dim=64, seq_len=50, num_heads=4, num_layers=2, ff_dim=256): super().__init__() self.item_embedding = nn.Embedding(num_items, embed_dim) self.position_embedding = nn.Embedding(seq_len, embed_dim) self.dropout = nn.Dropout(0.1) self.transformer_blocks = nn.ModuleList([ TransformerBlock(embed_dim, num_heads, ff_dim) for _ in range(num_layers) ]) # 序列池化层:使用注意力池化得到用户兴趣向量 self.attention_pool = nn.Linear(embed_dim, 1) def forward(self, behavior_seq): # behavior_seq: [batch_size, seq_len] batch_size, seq_len = behavior_seq.shape device = behavior_seq.device # 1. 物品嵌入 item_emb = self.item_embedding(behavior_seq) # [batch, seq, dim] # 2. 位置嵌入 positions = torch.arange(seq_len, device=device).expand(batch_size, seq_len) pos_emb = self.position_embedding(positions) # 3. 组合并添加Dropout seq_input = self.dropout(item_emb + pos_emb) # 4. 通过Transformer块 for block in self.transformer_blocks: seq_input = block(seq_input) # 5. 注意力池化得到用户向量 attn_weights = torch.softmax(self.attention_pool(seq_input), dim=1) # [batch, seq, 1] user_interest = torch.sum(attn_weights * seq_input, dim=1) # [batch, dim] return user_interest class RankModelWithSequence(nn.Module): """集成序列信息的精排模型""" def __init__(self, user_feat_dim, ad_feat_dim, num_items, seq_embed_dim=64): super().__init__() # 用户行为序列编码器 self.seq_encoder = UserBehaviorEncoder(num_items, embed_dim=seq_embed_dim) # 用户和广告的静态特征处理MLP self.user_mlp = nn.Sequential( nn.Linear(user_feat_dim, 128), nn.ReLU(), nn.Dropout(0.2), nn.Linear(128, 64) ) self.ad_mlp = nn.Sequential( nn.Linear(ad_feat_dim, 128), nn.ReLU(), nn.Dropout(0.2), nn.Linear(128, 64) ) # 最终预测层 self.final_mlp = nn.Sequential( nn.Linear(seq_embed_dim + 64 + 64, 256), # 拼接序列向量、用户静态向量、广告向量 nn.ReLU(), nn.Dropout(0.3), nn.Linear(256, 128), nn.ReLU(), nn.Linear(128, 1), nn.Sigmoid() ) def forward(self, user_static_feat, ad_feat, user_behavior_seq): # 编码行为序列 seq_interest = self.seq_encoder(user_behavior_seq) # [batch, seq_embed_dim] # 处理静态特征 user_static_vec = self.user_mlp(user_static_feat) # [batch, 64] ad_vec = self.ad_mlp(ad_feat) # [batch, 64] # 特征拼接与最终预测 combined = torch.cat([seq_interest, user_static_vec, ad_vec], dim=-1) pctr = self.final_mlp(combined) return pctr # 模拟训练流程 def train_step(model, optimizer, user_static, ad_feat, behavior_seq, label): model.train() optimizer.zero_grad() prediction = model(user_static, ad_feat, behavior_seq) loss = F.binary_cross_entropy(prediction.squeeze(), label) loss.backward() optimizer.step() return loss.item() # 初始化模型 num_items = 100000 # 假设有10万个商品 user_feat_dim = 30 ad_feat_dim = 25 model = RankModelWithSequence(user_feat_dim, ad_feat_dim, num_items) optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) # 模拟一批数据 batch_size = 32 seq_len = 50 user_static = torch.randn(batch_size, user_feat_dim) ad_feat = torch.randn(batch_size, ad_feat_dim) behavior_seq = torch.randint(0, num_items, (batch_size, seq_len)) # 随机生成行为序列 label = torch.randint(0, 2, (batch_size,)).float() # 训练一步 loss = train_step(model, optimizer, user_static, ad_feat, behavior_seq, label) print(f"Training loss: {loss:.4f}")代码关键点解释:
UserBehaviorEncoder类封装了行为序列的Transformer编码过程,包括物品嵌入、位置嵌入和多层Transformer块。MultiHeadAttention和TransformerBlock是简化的自注意力实现,便于理解。工业级实现会使用PyTorch内置的nn.TransformerEncoderLayer以获得更好的优化。RankModelWithSequence将序列兴趣向量、用户静态特征向量和广告特征向量拼接,通过MLP进行最终CTR预估。- 训练时,需要准备三部分数据:用户静态特征、广告特征、用户行为序列ID。实际生产中,序列的构建和负采样是数据管道的关键。
6. 效果评估与线上A/B测试关键指标
引入序列模型后,如何科学评估其效果?离线评估和在线A/B测试需关注不同维度。
6.1 离线评估指标
| 指标 | 含义 | 在序列模型评估中的侧重点 |
|---|---|---|
| AUC | 模型整体排序能力。 | 基础指标,需确保比基线模型有提升。 |
| GAUC | 按用户分组计算的AUC,消除用户间差异。 | 更重要的指标,能更好反映模型对个体用户兴趣的捕捉能力。 |
| LogLoss | 预测概率与真实标签的交叉熵。 | 观察损失下降程度,特别是对长尾样本的拟合情况。 |
| NDCG@K | 衡量Top K个推荐结果的质量。 | 评估序列模型在列表顶部位置的排序精度。 |
| 用户兴趣相似度 | 通过模型提取的用户向量,计算同一用户不同时间点的兴趣变化。 | 辅助评估序列模型是否学到了有意义的、随时间演化的用户表征。 |
6.2 在线A/B测试核心指标
在线实验是最终检验标准。除了业务核心指标(如点击率CTR、转化率CVR、人均收入ARPDAU),还需关注系统指标:
- 业务指标:
- CTR/CVR 相对提升:最直接的收益体现。
- 深度转化指标:如下单率、付费率、观看时长等,序列模型可能对长期价值有更好预估。
- 多样性/新颖性:序列模型可能发掘用户潜在兴趣,带来更丰富的推荐结果。
- 系统指标:
- 推理延迟(P99 Latency):序列模型,尤其是长序列模型,会显著增加计算耗时。必须监控P99延迟是否在可接受范围内。
- QPS与吞吐量:在同等资源下,模型变复杂可能导致吞吐量下降。
- CPU/GPU/内存使用率:监控资源消耗的增长。
- 一致性指标:
- 线上-线下一致性:确保离线AUC的提升能转化为线上效果。若不一致,可能是线上数据分布、特征实时性、服务抖动等原因。
A/B测试策略建议:先小流量实验,重点观察延迟和系统负载。稳定后,逐步扩大流量,同时细致分析不同用户群体(新用户/老用户、活跃用户/沉默用户)的效果差异。序列模型对数据丰富的活跃用户效果提升通常更明显。
7. 常见问题与排查思路
在实际落地序列模型时,你会遇到一些典型问题。
| 问题现象 | 可能原因 | 排查思路 | 解决方案建议 |
|---|---|---|---|
| 离线AUC提升,但线上效果不变或下降 | 1. 线上特征与离线训练特征不一致。 2. 用户行为序列在线构建存在延迟或丢失。 3. 模型服务化时序列截断逻辑不一致。 4. 线上流量分布与训练数据分布差异大。 | 1. 对比线上打分请求的特征日志与训练样本特征。 2. 检查序列特征服务的数据新鲜度(如Flink处理延迟)。 3. 在离线环境模拟线上服务,进行一致性校验。 4. 分析实验桶和训练集的人群分布差异。 | 1. 建立严格的特征上线校验流程。 2. 监控序列特征 pipeline 的端到端延迟。 3. 在离线评估中加入线上数据分布的模拟测试。 |
| 模型推理延迟过高 | 1. 序列长度过长,Transformer计算复杂度高。 2. 模型层数或参数量过大。 3. 服务框架未优化(如未使用TensorRT)。 4. GPU显存不足,导致频繁内存交换。 | 1. 使用性能分析工具(如PyTorch Profiler)定位耗时模块。 2. 监控GPU利用率和显存使用情况。 3. 测试不同序列长度下的延迟变化。 | 1. 对长序列进行重要性采样或分层建模(近期细粒度,远期粗粒度)。 2. 对模型进行蒸馏或剪枝。 3. 使用模型编译优化工具(如TorchScript, TensorRT)。 4. 考虑使用缓存,对相同用户序列的重复请求复用计算结果。 |
| 新用户/冷启动用户效果差 | 序列模型严重依赖历史行为,新用户行为序列短或为空。 | 1. 分析新用户群体的单独指标。 2. 查看模型对新用户的预测分数分布是否异常。 | 1. 为行为序列短的用户设计回退机制(如使用全局平均池化或切换到非序列模型)。 2. 引入side information(如注册信息、当前上下文)加强表征。 3. 采用元学习或快速适应技术,让模型能从小样本中快速学习。 |
| 训练过程不稳定或难以收敛 | 1. 用户行为序列噪声大,存在大量非相关行为。 2. 超长序列导致梯度消失/爆炸。 3. 负样本采样策略不当。 | 1. 检查训练Loss曲线是否震荡。 2. 分析序列中有效行为的比例。 3. 检查梯度范数。 | 1. 对原始行为进行清洗和过滤(如过滤曝光未点击、停留过短的行为)。 2. 使用梯度裁剪和更稳定的优化器(如AdamW)。 3. 优化负采样策略,增加困难负样本的比例。 |
| 序列特征存储成本激增 | 存储每个用户的原始行为序列,存储开销随用户量和序列长度线性增长。 | 监控特征存储服务的容量和成本变化。 | 1. 采用有损压缩,如存储item ID的差值编码或哈希编码。 2.分层存储,仅将近期热序列放在高速存储中。 3. 探索使用行为序列的摘要向量(通过一个轻量模型实时生成)替代原始序列。 |
8. 最佳实践与演进方向
基于Meta等公司的前沿实践,我们可以总结出一些最佳实践和未来演进方向。
8.1 当前阶段的最佳实践
- 从精排开始,逐步下沉:不要一开始就追求全链路序列化。先在精排阶段引入用户短期(如最近50-100个)行为序列,验证效果和性能成本。成熟后,再将序列能力以向量化形式下沉到粗排和召回。
- 构建统一的特征平台:序列数据的实时生成、存储、读取是系统工程。投资建设一个高可用、低延迟的实时特征平台,能够统一管理用户行为流,并为不同阶段模型提供不同粒度的序列特征。
- 模型轻量化与加速先行:在模型设计初期就考虑部署成本。对于在线服务,优先选择蒸馏、剪枝和高效的注意力机制(如Linformer, Performer)。将大模型的能力“打包”进小模型。
- 重视离线评估与仿真:建立完善的离线评估体系,除了AUC/GAUC,增加线上仿真环境,尽可能模拟线上服务的数据流和延迟,提前发现一致性问题和性能瓶颈。
- 数据质量高于模型复杂度:清洗高质量的用户行为序列比堆叠更复杂的模型结构更有效。重点关注序列的完整性、时效性和噪声过滤。
8.2 未来的演进方向
- 超长序列与终身记忆:探索如何处理用户成千上万的终身行为。这可能涉及层次化记忆网络(将近期行为存于工作记忆,长期模式存于外部记忆)或检索增强(从海量历史中实时检索相关片段)。
- 多模态序列融合:不仅有点击序列,还有搜索词序列、视频观看序列、语音交互序列等。如何跨模态地统一建模用户意图,是下一个突破口。
- 生成式广告与序列模型:结合LLM的生成能力,广告系统可能不再只是“排序”,而是能根据用户实时序列动态生成个性化的广告创意或落地页。序列模型将成为理解用户需求的“大脑”。
- 因果推断与反事实学习:当前的序列模型主要学习相关性。引入因果推断,可以更好地估计“如果给用户展示另一个广告会发生什么”,从而做出更优决策,避免反馈循环偏差。
- 联邦学习与隐私保护:用户行为序列包含敏感信息。如何在保护用户隐私的前提下,利用多方数据训练更强大的序列模型,是合规和技术上的共同挑战。
Meta广告排序向多阶段序列模型的演进,清晰地展示了一条技术发展路径:从离散特征到连续序列,从局部优化到全局建模,从人工设计到规模驱动。对于广大算法工程师和团队来说,立即全面复刻Meta的架构是不现实的,但理解其背后的思想——充分利用原始行为序列的时空信息,并相信模型与数据规模扩展的力量——足以指导我们当下的技术选型。
最直接的行动建议是:立即开始在你的精排模型中,尝试加入用户行为序列特征。从一个简单的GRU或轻量级Transformer开始,处理好数据管道和线上服务性能。当你亲眼看到GAUC的提升后,自然会沿着这条路径,逐步探索粗排的序列化、召回的双塔化,乃至最终迈向统一序列基础模型的未来。
技术的浪潮由巨头引领,但价值的捕获属于每一个看清方向并果断行动的实践者。广告排序的“LLM化”时代已拉开序幕,你现在所做的每一次序列建模实验,都是在为未来的系统积累至关重要的经验。