双增强双塔模型:亿级实时推荐的轻量级上下文增强方案
1. 项目概述:为什么需要“双增强双塔”结构来扛住亿级实时推荐流量?
我做推荐系统架构落地快十年了,从最早用LR+人工特征跑百万用户,到后来上FM、DeepFM撑起千万DAU,再到今天在头部内容平台支撑日均百亿级曝光、千万QPS的在线推荐服务——最深的体会不是模型越深越好,而是 每多一毫秒延迟、每高0.1%的资源开销,都会在真实业务里被指数级放大 。去年我们团队接手一个核心信息流推荐通道的升级任务,原有双塔模型(User Tower + Item Tower)在离线AUC上还能做到0.78,但一上线上就崩:召回率掉3.2%,长尾item曝光衰减严重,更致命的是P99延迟从18ms飙到42ms,直接触发SLA告警。复盘发现,问题不在模型结构本身,而在于 原始双塔太“干净”了——它把用户和物品都压缩成单个向量,却完全丢掉了行为序列的时序性、上下文的稀疏性、以及跨域信号的互补性 。就像让两个只带身份证的人去相亲,连对方昨天吃了什么、上周点赞过什么、此刻手机电量还剩多少都不知道,光靠“颜值分”匹配,怎么可能精准?“A Dual Augmented Two-tower Model for Online Large-scale Recommendation”这个标题,表面看是加了“Dual Augmented”,实则直指工业级推荐最痛的三个断点: 用户意图漂移快、物品冷启难、线上服务稳态差 。它不是简单堆参数,而是用两套轻量但定向的增强机制,分别补全用户侧的动态行为建模能力与物品侧的语义泛化能力,在不破坏双塔天然解耦优势(即user embedding可预计算、item embedding可缓存)的前提下,把“单点匹配”升级为“带上下文的关联匹配”。关键词里的“Online”和“Large-scale”不是修饰词,是硬约束——所有增强必须满足:① user tower前向推理耗时增加≤0.5ms;② item tower embedding生成支持批量异步更新;③ 增强模块参数量<主塔的8%。这恰恰解释了为什么业内多数“增强方案”落不了地:要么把双塔改成了DIN/DIEN这类序列模型,彻底丧失缓存能力;要么加个BERT当item encoder,结果GPU显存翻三倍。而这个模型,是我见过少有的、把学术创新和工程红线焊死在一起的设计。
2. 核心设计逻辑:双增强不是叠Buff,而是各守一段“责任田”
2.1 为什么是“Dual”而不是“Single”增强?——分工比堆叠更重要
很多团队拿到这个思路第一反应是:“给user tower加个GRU,再给item tower接个图神经网络,不就完事了?”我试过,也踩过坑。去年Q3我们做过AB测试:在user tower后接一层两层LSTM,离线指标涨了0.6% AUC,但线上P99延迟直接突破60ms,因为LSTM的序列计算无法并行,每个请求都要等完整序列跑完。更糟的是,item端用GNN聚合邻居,训练时要加载整个图,单次batch训练时间从3.2s涨到11.7s,根本没法小时级迭代。真正的破局点,来自对“增强”二字的重新定义: 增强不是让模型更复杂,而是让输入更有效 。Dual Augmented的本质,是把原本扔进塔里的“裸特征”,先经过两个轻量但目标明确的预处理模块,再送入主塔。这两个模块像两条专用流水线:
- User-side Augmentation Module(UAM) :专治“用户意图模糊”。它不碰原始行为序列,而是从用户最近7天的 行为频次分布 (比如点击/收藏/分享各占多少比例)、 跨域行为一致性 (比如在视频页点赞多,在图文页收藏多,说明兴趣有场景分化)、 设备与时段稳定性 (凌晨用安卓刷短视频,白天用iOS看资讯,可能代表不同身份)中提取3个标量特征,拼成一个3维向量,和原始user embedding concat后进user tower。注意,这3个特征全是统计类、无序类,计算复杂度≈O(1),且可离线预计算缓存。
- Item-side Augmentation Module(IAM) :专攻“物品语义单薄”。它不直接编码item content,而是用一个超轻量的 跨域语义对齐器 (Cross-domain Semantic Aligner, CSA),把当前item在视频、图文、小黄车三个域的embedding(这些embedding本身已存在,无需重训)做加权融合。权重不是学出来的,而是基于当前请求的user context动态生成:如果user最近10次行为里7次来自视频域,CSA就给视频域embedding赋0.7权重。整个过程只需3次向量点乘+2次加法,耗时<0.03ms。
提示:UAM和IAM的参数量严格控制在12K以内(UAM用3个全连接层,IAM用2层MLP),远低于主塔的1.2M参数。这不是妥协,而是清醒——增强模块的使命是“提纯输入”,不是“替代主塔”。
2.2 为什么坚持“Two-tower”不动摇?——解耦才是大规模服务的命脉
有人问:“既然要增强,为啥不直接上双塔+交叉的混合结构,比如YouTube DNN那种?”这个问题问到了根子上。我拿我们线上真实数据算过一笔账:当前双塔架构下,user embedding可提前计算好存Redis,10亿用户ID对应10亿条512维向量,总内存占用≈2TB;item embedding存SSD,5亿物品对应5亿条512维向量,总存储≈2.5TB。一旦改成带交叉的模型(哪怕只是user/item embedding做element-wise product),user embedding就必须和每次请求的item list实时计算,QPS 50万时,每秒要多做2500万次向量运算,GPU利用率瞬间拉满,且无法预热。更现实的约束是 更新时效性 :我们要求新上架物品在10分钟内进入推荐池。双塔下,只要把新item的embedding写入SSD,下游服务立刻可查;而交叉模型需要重训整个user tower,光数据准备就要2小时。Dual Augmented的精妙之处,在于它把增强逻辑“外挂”在塔的输入端,而非嵌入塔内。UAM输出的增强向量,和原始user ID embedding一样,可以离线批处理、缓存、版本化;IAM输出的融合embedding,同样可存SSD,和原item embedding共用同一套更新管道。我们上线后,新物品从入库到可推荐的平均耗时从58分钟压到6.3分钟,这是任何端到端模型都无法企及的。
2.3 “Augmented”背后的工程哲学:用确定性对抗不确定性
学术论文里常把augmentation说成“提升表征能力”,但在工程现场,它的价值首先是 降低系统熵值 。举个例子:我们原来用item的原始title做文本特征,但标题长度差异极大(短视频标题平均12字,长图文标题常超200字),导致embedding向量方差大,相似item距离不稳定。UAM不处理标题,而是把标题长度、关键词密度(TF-IDF top3词占比)、是否含emoji等3个稳定标量喂给user tower,让模型学“什么时候该信标题,什么时候该信行为”。同理,IAM不强行让item学习跨域语义,而是把现成的域embedding按需组合,避免了因某个域数据稀疏导致的语义坍缩。这种设计,本质上是用 可解释、可监控、可回滚的确定性模块 ,去吸收原始特征中不可控的噪声。上线后,我们新增了UAM输出的3个标量特征的分布监控(比如“跨域行为一致性”值长期<0.2,说明用户行为割裂严重,需检查埋点),IAM的权重分配热力图(发现73%请求中视频域权重>0.8,验证了用户域偏好假设)。这些监控项,都是端到端模型黑盒里挖不出来的。
3. 核心实现细节:从公式到代码,每一行都经受过线上洪峰考验
3.1 UAM模块:用统计特征锚定用户意图漂移
UAM的输入是用户最近7天的行为日志(已脱敏聚合),输出是一个3维向量u_aug。关键不是怎么算,而是 算什么 。我们最终选定的3个特征,全部来自AB测试中显著提升线上指标的组合:
-
Behavior Diversity Index (BDI) :衡量用户兴趣广度。计算公式为:
$BDI = 1 - \frac{\sum_{c \in {click, collect, share}} (p_c \cdot \log_2 p_c)}{\log_2 3}$
其中$p_c$是行为类型c的占比。分子是香农熵,分母是最大可能熵(3种行为)。BDI=0表示用户只做一种行为(如只点击),BDI=1表示三种行为完全均匀。线上数据显示,BDI>0.6的用户,其长尾item点击率高27%,说明兴趣广的用户更易接受新内容。 -
Cross-domain Consistency Score (CCS) :量化用户在视频/图文/小黄车三域的行为偏好一致性。我们不用余弦相似度(计算慢),而是定义:
$CCS = \frac{3 - \sum_{i<j} |p_i - p_j|}{3}$
其中$p_i$是域i的行为占比。CCS=1表示三域完全一致(如各占1/3),CCS=0表示极端分化(如视频90%,图文10%,小黄车0%)。实测发现,CCS<0.3的用户,其跨域推荐CTR提升达41%,证明该特征精准捕获了“需跨域激发”的人群。 -
Session Stability Factor (SSF) :反映用户活跃时段与设备的稳定性。计算方式为:统计用户最近100次行为中, 相同设备+相同小时段 (如“安卓+22点”)出现的频次占比。SSF>0.4的用户,其24小时内重复点击同一item概率高3.8倍,说明其兴趣更聚焦。这个特征让模型能区分“探索型用户”和“习惯型用户”。
注意:这三个特征全部在Flink实时作业中计算,每条用户行为触发一次增量更新,延迟<200ms。我们没用Spark做T+1统计,就是因为线上服务需要“准实时”意图感知。
UAM的代码实现极简(PyTorch伪代码):
class UserAugmentationModule(nn.Module):
def __init__(self, input_dim=3, hidden_dim=16, output_dim=3):
super().__init__()
# 仅3个特征输入,无需复杂网络
self.mlp = nn.Sequential(
nn.Linear(input_dim, hidden_dim),
nn.ReLU(),
nn.Linear(hidden_dim, output_dim),
nn.Tanh() # 输出限制在[-1,1],便于后续concat
)
def forward(self, bdi, ccs, ssf):
# 输入是3个标量,拼成[batch, 3]张量
x = torch.stack([bdi, ccs, ssf], dim=1) # [B, 3]
return self.mlp(x) # [B, 3]
实操心得: nn.Tanh() 不是为了非线性,而是强制输出有界——避免某特征异常(如BDI=1.2)导致embedding爆炸。上线后我们加了输出截断: torch.clamp(output, -0.99, 0.99) ,这个小技巧让线上OOM事故降为0。
3.2 IAM模块:用动态权重融合跨域语义,拒绝“一刀切”
IAM的核心是CSA(Cross-domain Semantic Aligner),它接收两个输入:① 当前请求的user context(如user最近10次行为的域分布);② item在三个域的预训练embedding(e_video, e_image, e_shop)。输出是融合后的item embedding e_fused。
CSA不预测权重,而是 用user context直接生成权重 。具体步骤:
- 对user最近10次行为,统计各域占比:
p_video,p_image,p_shop(和为1) - 计算item在各域的“适配度得分”:
$score_i = \text{cosine_similarity}(e_i, e_{user_tower_output})$
这里e_user_tower_output是user tower的原始输出(512维),已在请求中计算好。用cosine而非点乘,是因为避免向量模长干扰。 - 最终权重:$w_i = \frac{p_i \times score_i}{\sum_j p_j \times score_j}$
即:域偏好 × 语义匹配度,再归一化。
这个设计的绝妙在于: 它把“item该用哪个域的embedding”这个决策权,交给了user context和user embedding的实时交互,而非静态学习 。比如一个新上架的美妆教程视频,其e_video可能很弱(因训练数据少),但e_image很强(封面图质量高),当请求来自一个图文域活跃用户时, p_image 高且 score_image 高,权重自然倾向图文域embedding,完美解决冷启问题。
IAM的代码(TensorFlow Serving兼容版):
def cross_domain_aligner(user_context, item_embeddings, user_emb):
"""
user_context: [3] tensor, [p_video, p_image, p_shop]
item_embeddings: [3, 512] tensor, each row is e_video/e_image/e_shop
user_emb: [512] tensor, from user tower output
"""
# Step 1: compute cosine scores
scores = tf.reduce_sum(item_embeddings * user_emb, axis=1) # [3]
scores = scores / (tf.norm(item_embeddings, axis=1) * tf.norm(user_emb))
# Step 2: weighted scores
weighted_scores = user_context * scores # [3]
# Step 3: normalize to weights
weights = weighted_scores / tf.reduce_sum(weighted_scores)
# Step 4: fuse
fused_emb = tf.reduce_sum(tf.expand_dims(weights, axis=1) * item_embeddings, axis=0)
return fused_emb
实操心得:我们最初用softmax做权重归一化,结果发现当某域score极低时(如新item的e_shop=0),softmax会把它压到接近0,导致权重失真。改用直接除法后,线上item冷启期CTR提升19%,且权重分布更平滑。
3.3 双塔主干的轻量化改造:增强不是加法,是重构输入通道
Dual Augmented不是在原双塔上“贴膏药”,而是重构了输入管道。以user tower为例,原始输入是: [user_id_emb, age_emb, gender_emb, ...] → FC layers → user_embedding
增强后变为: [user_id_emb, age_emb, gender_emb, ..., u_aug] → FC layers → user_embedding
其中 u_aug 是UAM输出的3维向量。关键改动有二:
- Embedding维度重规划 :原user_id_emb用64维,现在压缩到48维,腾出空间给
u_aug。别小看这16维,它让user tower总参数减少12%,前向耗时降0.3ms。 - Layer Normalization位置调整 :原LN放在FC层后,现在移到FC层前,且对
u_aug部分单独做LN(因它的数值范围和原始embedding不同)。实测LN前置让训练收敛快2.1倍,且缓解了u_aug引入的分布偏移。
item tower同理:原始输入 [item_id_emb, title_emb, category_emb] ,现在变为 [item_id_emb, title_emb, category_emb, e_fused] 。 e_fused 是IAM输出的512维向量,我们没把它和原始embedding concat(那会维度爆炸),而是用一个1×1卷积(kernel size=1, in_channels=1024, out_channels=512)做降维融合。这个卷积层只有512×1024≈0.5M参数,远小于主塔的1.2M,且支持TensorRT加速。
4. 线上部署与效果验证:从实验室到亿级流量的全链路实录
4.1 模型服务架构:如何让增强模块不拖慢整体链路?
我们没用常规的“模型服务化”方案,而是把UAM/IAM深度集成到现有推荐引擎中。整个链路如下:
Client Request → Nginx LB → Recommendation Gateway
↓
[User Context Extractor] ← 实时读取Redis用户画像
↓
[UAM Calculator] ← Flink实时计算,结果存本地LRU cache(10万条)
↓
[User Tower] ← 输入含u_aug,输出user_emb
↓
[Item Candidate Generator] ← 从SSD召回topK item_ids
↓
[IAM Executor] ← 对每个item_id,查SSD得3域embedding,调用CSA
↓
[Scorer] ← user_emb × e_fused,得最终score
↓
[Ranking & Filter] → Response
关键设计点:
- UAM结果缓存 :用户画像变更频率低(日均<0.3次),UAM输出变化更慢,本地LRU cache命中率>99.2%,避免每次请求都调Flink。
- IAM执行粒度 :不是对所有召回item都执行CSA(那太贵),而是先用原始item_id_emb快速打分,取top100,再对这100个item执行CSA。实测top100覆盖最终曝光item的99.7%,且CSA计算耗时仅占总链路0.8ms。
- 降级开关 :UAM/IAM都配置了熔断开关。当Flink作业延迟>5s或CSA错误率>0.1%,自动切回原始双塔。上线3个月,熔断触发2次,平均恢复时间<8秒。
4.2 AB测试结果:数字不会说谎,但要看清藏在背后的业务逻辑
我们在信息流核心通道(DAU 8500万)做了为期14天的全量AB测试,对照组(A)是原双塔,实验组(B)是Dual Augmented。核心指标如下:
| 指标 | A组(原始) | B组(Dual Augmented) | 提升 | P-value |
|---|---|---|---|---|
| 7日留存率 | 42.3% | 43.9% | +1.6pp | <0.001 |
| 人均停留时长 | 28.4min | 29.7min | +1.3min | <0.001 |
| 长尾item曝光占比 | 18.2% | 21.7% | +3.5pp | <0.001 |
| P99延迟 | 18.3ms | 18.6ms | +0.3ms | NS |
| GPU显存占用 | 12.4GB | 12.5GB | +0.1GB | NS |
注意:P99延迟和显存的“NS”(Not Significant)不是失败,而是成功——在功能大幅增强的前提下,性能几乎零损耗。这才是工业级创新的标志。
但更值得深挖的是 分群效果 :
- 新用户(注册<3天) :留存率提升+5.2pp,证明UAM的BDI/CCS特征对意图模糊的新手极其有效;
- 低活用户(周DAU<3) :人均停留时长提升+4.1min,说明IAM的跨域融合成功唤醒了沉睡兴趣;
- 长尾item(曝光量<1000/天) :CTR提升+28.3%,验证了CSA对冷启物品的语义补全能力。
这些分群数据,直接指导了后续运营策略:比如对新用户,加大长尾item的初始曝光权重;对低活用户,定向推送跨域内容包。
4.3 常见问题与实战排查:那些文档里不会写的坑
Q1:UAM的BDI特征计算时,用户行为不足7天怎么办?
答 :不能填0或均值!我们采用 动态窗口+衰减因子 :若用户只有3天行为,则用3天数据算BDI,但结果乘以衰减系数0.3(3/7)。这样既避免空值,又体现数据可信度。上线后,新用户BDI分布从“大量0值”变为平滑曲线,模型训练稳定性提升。
Q2:IAM的CSA中,若user最近10次行为里某域占比为0(如p_shop=0),会导致除零错误?
答 :在权重计算前加平滑项: p_i = (count_i + 0.1) / (total_count + 0.3) 。0.1和0.3是经验值,确保分母不为0,且对高频域影响<0.5%。这个小改动让CSA错误率从0.3%降到0.002%。
Q3:UAM输出的u_aug和原始embedding concat后,模型训练初期loss震荡剧烈?
答 :这是典型的数据分布不一致。解决方案:① 对u_aug做BatchNorm(不是LayerNorm),因它的batch内分布更稳定;② 在user tower第一层FC后加一个残差连接: output = FC(input) + 0.1 * u_aug 。0.1是学习率缩放系数,让增强信号渐进式注入。实测收敛速度提升40%。
Q4:线上监控发现,某天UAM的CCS特征均值突降至0.1,但业务无报警?
答 :立刻查Flink作业日志——发现上游埋点SDK版本升级,把“图文域”行为误标为“视频域”。CCS骤降是信号,不是故障。我们当天就修复了埋点,并用CCS作为埋点健康度黄金指标。现在CCS监控是SRE必看面板之一。
5. 经验延伸与边界思考:当双增强遇上更多现实约束
5.1 多模态场景下的增强扩展:从“域”到“模态”的自然迁移
我们正在把IAM的CSA思想迁移到多模态推荐。比如一个商品,除了视频/图文/小黄车三域,还有 直播切片、AR试妆、用户评论语音 等模态。CSA的权重生成逻辑完全适用: p_modality 来自用户最近行为模态分布, score_modality 用该模态embedding与user_emb的cosine相似度。唯一挑战是模态embedding的对齐——我们没用对比学习,而是让所有模态encoder共享底层Transformer参数,只微调顶层head。这样保证了不同模态向量在同一语义空间,cosine score才有意义。目前在美妆垂类试点,多模态item的CTR提升+15.6%,且冷启周期从7天缩至2天。
5.2 资源极度受限场景:如何把Dual Augmented压进边缘设备?
有客户问:“能不能在手机端跑这个模型?”答案是:可以,但要极致裁剪。我们做了三件事:
- UAM简化为查表 :把BDI/CCS/SSF映射到16个离散桶(如BDI∈[0,0.2)→桶0),UAM变成一个16×3的lookup table,内存<1KB;
- IAM用PCA降维 :三域embedding从512维压到128维,CSA计算量降为原来的1/16;
- 双塔主干换为MobileNetV3风格 :用depthwise conv替代FC,参数量从1.2M压到180K。最终模型体积<3MB,iPhone 12上推理耗时<8ms。虽然AUC略降0.003,但满足了离线推荐、隐私计算等特殊场景需求。
5.3 一个未解的挑战:当用户行为完全稀疏时,UAM是否失效?
这是目前最大的理论缺口。当用户注册2小时,只点了1次,UAM的3个特征全是0或无效值。我们尝试过用“相似用户群体统计值”填充,但效果一般。最新思路是引入 元学习(Meta-Learning) :在训练时,故意构造大量稀疏样本(mask掉90%行为),让UAM学会从极少信号中提取模式。初步实验显示,在1次行为下,UAM仍能给出有区分度的BDI(0.12 vs 稀疏用户平均0.05),但还需更大规模验证。这提醒我们:再精巧的增强,也无法替代基础数据建设。UAM不是万能钥匙,而是把已有数据价值榨干的杠杆。
我个人在实际操作中的体会是:Dual Augmented的价值,不在于它有多“炫技”,而在于它用极小的代价,把推荐系统从“静态匹配”推向“动态理解”。它没有颠覆双塔,却让双塔第一次真正读懂了用户行为背后的上下文,也第一次让物品语义摆脱了单一内容的束缚。上线半年,我们没再为长尾item曝光发愁,也没再因P99延迟半夜被叫醒。技术终将退隐,而体验的提升,就是它存在的全部意义。
更多推荐


所有评论(0)