是什么
RecsysTwoTowerModel 是 Phoenix 双塔召回模型的代码构件。它是一个 Haiku hk.Module,定义在 phoenix/xrex/models/recsys_two_tower_model.py:1610。配置类是同一文件中的 RecsysTwoTowerModelConfig(:2854)。本页说明类结构与方法;双塔召回的工作原理见 Phoenix 召回:双塔检索架构。
旧构件的去向。 旧页面描述的
PhoenixRetrievalModel、CandidateTower、RetrievalOutput(phoenix/recsys_retrieval_model.py)与测试文件test_recsys_retrieval_model.py已在 2026-08-13 删除。现在的候选塔是RecsysCandidateTower。召回不再返回RetrievalOutput,而由forward直接返回各语料区间的 top-K。
用户塔不是独立的类。它是一个 RecsysAggregatedModel 实例,与排序模型用同一个类、同一个 transformer,只是不带候选段(recsys_two_tower_model.py:3138-3152)。见 RecsysAggregatedModel(排序模型)。
配置:RecsysTwoTowerModelConfig
该类用 @configclass 声明,位于 recsys_two_tower_model.py:2854-3150:
| 字段 | 含义 | 类默认值 |
|---|---|---|
user_tower_config |
用户塔,一个 RecsysAggregatedModelConfig |
必填 |
candidate_tower_config |
候选塔,RecsysCandidateModelConfig |
必填 |
use_in_batch_negatives |
把批内其他用户的候选用作负样本 | True |
num_global_negatives_per_example |
每个样本的全局负样本数 | 0(各预设设为 64) |
logq_correction_scale |
log-Q 校正系数 | 2.0 |
fixed_temperature |
固定温度;0 表示可学习(初值 0.1) | 0.0 |
own_negative_logit_offset |
自身负反馈候选计入分母时的偏移 | 0.0 |
positive_count_user_weight_power |
按正样本数给用户加权时的幂 | 0.0 |
mask_in_batch_false_negatives |
剔除批内假负样本 | False |
user_query_heads |
用户查询头数;0 表示平均池化 | 0 |
mol_item_components 等 mol_* |
MoL 打分的组件数、适配器秩、门控隐藏层 | 0(关) |
positive_actions / hard_negative_actions / soft_negative_actions |
home 头的正样本、硬负样本、软负样本动作 |
点赞 / 7 种负反馈 / 未停留 |
immersive_*、ld_*、tweet_click_positive_actions |
其他头的动作清单 | 见 :2918-2965 |
head_names / head_dataset_mapping |
候选头名称;语料到头的映射 | ["home"] / None |
w_fav / w_ld / w_tweet_click |
服务时各打分槽位的权重 | 1.0 / 0.0 / 0.0 |
safety_filter_mode |
安全标签过滤 | "off" |
计算属性如下(:2977-3012):
blend_heads:head_names中属于("ld", "tweet_click")的头。这类头称为混合头:它们不负责语料,只增加打分槽位(:75-80)。num_table_slots = 1 + len(blend_heads):索引每行分成的段数。query_head_weights = (w_fav, w_ld?, w_tweet_click?):服务时每段的权重。
make()(:3112-3150)先做两项校验:head_names 数量与候选头数一致;混合头不能是第 0 个头,也不能负责语料。然后它构造用户塔、候选塔和模型。
预设位于 phoenix/xrex/configs/xrecsys_two_tower.py:439-580:xrecsys_two_tower、xrecsys_two_tower_immersive_only(_gb300)、xrecsys_two_tower_combined(_gb300)、xrecsys_two_tower_home_only、xrecsys_two_tower_nano。另有 xrecsys_reply_two_tower_v2.py(回复召回,本站未展开)。
RecsysCandidateTower(候选塔)
该类是 hk.Module,位于 recsys_two_tower_model.py:180-331。它的配置 RecsysCandidateModelConfig(:334-394)包含 emb_table_width、enable_linear_proj、max_posts(默认 10,240,000)和 num_candidate_heads。
| 方法 | 位置 | 行为 |
|---|---|---|
_concat_then_mlp |
:203-246 |
展平 → Linear(2W) → SiLU → Linear(W) → L2 归一化;头 h > 0 使用独立的参数名 |
_project_then_sum |
:248-320 |
帖子、作者、SID 的每个 token 各乘一个 [W, W] 投影后相加 → L2 归一化 |
_mean_pool |
:329-331 |
取均值 → L2 归一化,无参数 |
__call__ |
:322-327 |
按 use_project_then_sum → enable_linear_proj → 均值的优先顺序选择一种方式 |
RecsysCandidateModelConfig.make_post_embeddings(num_table_slots)(:365-394)创建形状为 [max_posts, emb_table_width × num_table_slots] 的 bfloat16 索引参数,即 checkpoint 里的 post_embeddings。
MolScorer(MoL 打分)
该类是 NamedTuple,位于 recsys_two_tower_model.py:710-884。mol_item_components > 0 时启用(combined 预设为 1):
item_components:每条帖子经低秩适配器生成若干个单位向量(:720-727)。mix:计算“每个用户查询头 × 每个帖子组件”的余弦相似度,送进隐藏层为 32 的门控 MLP,经 softmax 后加权求和(:729-737)。serving_side_tables:为服务预计算每条帖子的范数与门控偏置。这些值存进 checkpoint,或在启动时计算(:825-862)。- 配套 CUDA 核
xrex/cuda/mol_epilogue/执行融合打分与 int8 量化(:924-1003)。
RecsysTwoTowerModel 方法
| 方法 | 位置 | 作用 |
|---|---|---|
build_user_inputs |
:1621-1815 |
构造用户塔输入;feature_prep_enabled 时使用特征准备路径(不含候选),否则用 block_history_reduce |
candidate_token_stack |
:1894-1907 |
构造候选 token 栈:可选帖子哈希、作者哈希、SID token |
build_candidate_embeddings |
:1909-1916 |
把 token 栈送入指定头的候选塔 |
build_candidate_embeddings_from_lookups |
:1918-1949 |
构建索引时使用:从已查出的嵌入直接计算候选向量 |
__call__ |
:1951-1969 |
返回用户表示、各头的候选表示和统计量 |
candidate_representations |
:1971-1988 |
为每个候选头各计算一次 |
user_representation |
:1990-2128 |
transformer → 平均池化或查询头池化 → L2 归一化 |
_query_heads_pool / _packed_query_heads |
:2157-2215 |
带时间衰减的多查询头注意力池化 |
_mol_scorer |
:2217-2252 |
构造 MolScorer 参数 |
forward |
:2279-2600 |
服务前向:逐槽位打分并加权 → 掩码 → top_k_by_key |
loss |
:2719-2815 |
每个头调用一次 retrieval_loss 后相加,再加激活 L2 正则 |
metrics |
:2817-2851 |
召回率 @K 等指标 |
模块级函数:retrieval_loss(:1035-1380,采样 softmax 对比损失)、positive_and_negative_masks(:675-702)、score_over_slots(:150-177,按槽位加权求和)。
与排序模型的异同
| 维度 | 召回 RecsysTwoTowerModel |
排序 RecsysAggregatedModel |
|---|---|---|
| 序列 | 前缀 + 历史,无候选 | 前缀 + 历史 + 候选 |
| 用户 ID 嵌入 | 生产预设关闭 | 开 |
| 帖子 ID 嵌入 | 生产预设关闭,改用 SID + 作者 | 开,另加 SID |
| 输出 | 单位向量(或 4 个查询向量) | 64 个动作 logit + 8 个连续值 |
| 损失 | 对比损失 | 多标签 BCE + 连续损失 |
| 稠密优化器 | AdamW(phoenix/TRAINING.md:21-25) |
旗舰与 nano 用 Muon |
设计决策
| 决策 | 理由 |
|---|---|
| 用户塔直接复用排序模型类 | 两个模型共用主干与输入机制,只在输出头上不同(phoenix/README.md:250-257) |
| 混合头占用独立槽位 | 新目标(如 10-13 的 ld)不改动原有头的向量;服务时用权重调整比例 |
| MoL 打分可选 | 点积计算成本低;MoL 让一个用户能匹配多种兴趣。源码未写理由,此为推测 |
源码锚点
phoenix/xrex/models/recsys_two_tower_model.py:1610-2851——RecsysTwoTowerModelphoenix/xrex/models/recsys_two_tower_model.py:2854-3150——RecsysTwoTowerModelConfigphoenix/xrex/models/recsys_two_tower_model.py:180-394—— 候选塔及其配置phoenix/xrex/models/recsys_two_tower_model.py:710-1003——MolScorer与服务端打分phoenix/xrex/models/recsys_two_tower_model.py:1035-1380——retrieval_lossphoenix/xrex/configs/xrecsys_two_tower.py:251-580—— 预设
相关页面
- Phoenix 召回:双塔检索架构 —— 双塔召回原理
- RecsysAggregatedModel(排序模型) —— 用户塔复用的类
- Phoenix 召回索引:rankall 与事件层 —— 索引语料的来源
- Phoenix 训练 —— 训练与服务
- Transformer 骨架 —— 用户塔的 transformer