Skip to content

Phoenix 排序模型

生产版 Phoenix 排序:[用户前缀 | 历史 | 候选] 一条序列,64 个离散动作概率加连续值头,停留时长用 CREAD 损失

Updated

这一页回答什么

Phoenix 排序模型将用户特征、交互历史与一批候选帖子拼接为单条序列输入。模型经过 transformer 编码后,为每条候选帖子预测多项离散行为概率以及停留时长等连续值。编排服务 home-mixer 的 PhoenixScorer 组件提取这些预测值计算最终排序得分。

2026-08-13 起,官方仓库删除了 phoenix/ 目录下 Grok-1 的演示代码(recsys_model.py、grok.py、run_pipeline.py),替换为生产训练与在线服务代码,核心实现位于 phoenix/xrex/(phoenix/README.md:9-21)。本页所有结论均基于新版代码。旧版 PhoenixModel 类已废弃,当前生产排序模型实现类为 RecsysAggregatedModel。

核心结论

  1. 单序列串联三段结构:模型将输入拼接为 [用户前缀 token | 历史 S | 候选 C]。旗舰配置 home_direct_packed 包含 2 个用户前缀 token(用户 ID token 与用户画像 token)、1022 个历史 token 与 64 个候选 token,前缀与历史合计 1024 个 token(phoenix/xrex/configs/xrecsys.py:316-319、:56-68)。
  2. 多路特征投影求和生成 token:系统为帖子与作者哈希、语义 ID、产品面、帖龄、时区、本地小时、关注关系以及互动计数分别配置嵌入表或投影矩阵,通过求和将各路表征合并为单张 D 维 token(phoenix/xrex/models/recsys_feature_prep.py:1233-1330)。
  3. 候选注意力严格隔离:单条候选 token 仅对用户前缀、历史序列以及自身施加注意力计算。旗舰配置包含 8 层 transformer,末尾 2 层仅更新候选表征,历史序列状态在第 6 层计算完成后冻结(详见 候选隔离注意力掩码)。
  4. 输出结构划分双类预测头:模型输出 64 个离散动作 logit(槽位编号严格对应 ActionName 枚举值),以及 8 个连续值槽位(包含停留时长、点击后停留时长、视频续看秒数与主页访问秒数等)(phoenix/xrex/configs/xrecsys.py:45-48、phoenix/python/common/xai-proto/proto/recsys.proto:699-708)。
  5. 停留时长自 2026-10-08 改用 CREAD 损失:算法在 30 个对数间隔阈值上计算分类损失,并叠加 Huber 还原误差。旗舰配置将旧有的 Tweedie 与 MAE 停留损失权重置为 0(phoenix/xrex/models/loss_recsys.py:110-187、phoenix/xrex/configs/xrecsys.py:334-338)。
  6. 公开互动计数直接输入候选特征:候选 token 包含点赞、回复、转发、引用与浏览 5 项计数。若帖子发布时间超过约 14 天,系统将上述计数置零并添加陈旧标记(phoenix/xrex/models/recsys_feature_prep.py:733-768、phoenix/crates/common/xai-recsys/src/util.rs:765-790)。
  7. 编排层消费 29 项模型预测字段:home-mixer 消费的预测字段覆盖上游 README 划分的五大类别(互动、点击、注意力、作者、负反馈)。源码另包含 README 未列出的 4 项预测字段(xai-value-model/phoenix_scores.rs:1-32)。

输入:三段拼成一条序列

flowchart LR
    U["用户前缀 ×2<br/>用户与 IP 哈希 / 画像特征"] --> SEQ
    H["历史 ×1022<br/>帖+作者+SID+动作+停留+上下文"] --> SEQ
    C["候选 ×64<br/>帖+作者+SID+互动计数+帖龄+上下文"] --> SEQ
    SEQ["拼接成 B × 1088 × 2560"] --> T["Transformer 8 层"]

build_feature_prep_streams 依次构造三段特征,_assemble_unpacked 执行拼接并返回候选段起始偏移量 candidate_start_offset = 用户前缀长度 + 历史长度(phoenix/xrex/models/recsys_feature_prep.py:1233-1342)。

段 构造函数 加进 token 的成分(旗舰 home_direct_packed 打开的项)
用户 ID token _build_user_token(recsys_feature_prep.py:502-531) 用户 2 个哈希嵌入、IP 2 个哈希嵌入,各自投影后相加
用户画像 token _build_user_features_token(:534) 国家、语言、经纬度(正弦编码)、性别、年龄段;enable_user_installed_apps=False(xrecsys.py:353-359)
历史 token _build_seq_tokens、_add_context_features、_add_sid_features、_add_history_features(:1134、:626、:868、:925) 帖/作者哈希、语义 ID、产品面、帖龄、时区、本地小时、关注关系、互动计数、动作多热向量、停留时长分桶、点击后停留、首页续看/主页访问秒数、距本次请求的时间间隔
候选 token 同上,最后一步换成 _add_candidate_features(:1075) 帖/作者哈希、语义 ID、产品面、帖龄、时区、本地小时、关注关系、互动计数、陈旧标记、距最近一次历史曝光的时间间隔

时间间隔与首页续看/主页访问历史只在 home_direct_packed 打开(xrecsys.py:554-559)。

历史里的动作与停留时长

  • 动作表征:系统为历史中的每条帖子生成 64 维多热向量(用户触发的动作置为 1),乘以 [64, D] 维度权重表后累加至 token 向量(recsys_feature_prep.py:933-940)。代码已移除将 0/1 映射为 ±1 的旧式逻辑;全零动作向量通过 action_mask 清零。
  • 停留时长分桶表(10-08 新增,enable_dwell_bucket_table=True):历史帖子的停留秒数映射至“0 与 30 个对数阈值”构成的网格节点。算法通过线性插值融合相邻两行嵌入;针对 2.0 秒哨兵值,系统依据“该帖子是否包含负反馈”映射至两行专用嵌入之一(recsys_feature_prep.py:942-974,DWELL_TIME_DEFAULT_SENTINEL_SEC = 2.0 见 :428)。开源默认参数为:dwell_bucket_cap=240.0、dwell_bucket_log_min_threshold=2.0、dwell_bucket_num_thresholds=30(xrecsys.py:364-367)。

帖龄、互动计数与陈旧帖

  • 帖龄编码:曝光时间减去发帖时间,按 60 分钟划分区间,上限设为 POST_AGE_MAX_MINUTES = 4800(recsys_feature_prep.py:51-70、:644-653)。
  • 互动计数特征:算法对点赞、回复、转发、引用与浏览 5 项计数分别计算 log2(x+1)/32。数值经过隐藏层宽度为 64 的 GELU MLP 投影后累加至 token 向量(recsys_feature_prep.py:32-39、:733-761)。帖子既有公开热度由此直接构成排序输入。
  • 陈旧帖子标记:服务端组装候选序列时,若帖子发布时长超出 STALE_POST_14D_TTL_SEC = 1_213_200 秒(约 14.04 天),系统将 5 项互动计数重置为 0,并设置 is_stale_post 状态位(phoenix/xrex/data/recsys/feature_config.py:87-88、phoenix/crates/common/xai-recsys/src/util.rs:765-790)。系统另提供 30 天配置 STALE_POST_30D_TTL_SEC = 2_595_600,通过 enable_stale_post_30d 控制,开源默认处于关闭状态。

语义 ID(SID)

系统为每条帖子输入 6 层、每层 256 码的残差量化编码(由帖子多模态嵌入计算生成),并追加一层基于帖子哈希派生的“哈希层”。各层查表求和后投影至 D 维空间(phoenix/xrex/models/recsys_model.py:1714-1835)。排序模型旗舰配置设为 sid_cross_attn=False,召回模型配置设为 True(xrecsys.py:343-348)。语义 ID 的生成来源详见 Phoenix 训练。

transformer 与输出头

RecsysAggregatedModel.__call__(phoenix/xrex/models/recsys_model.py:4312-4463):

model_out = self.model(input_embeddings, attn_mask, ..., candidate_start=candidate_start)
out_embeddings = rms_norm_fn(model_out.output, name="final_layer_norm")
out_embeddings = out_embeddings[:, candidate_start_offset:, :]       # 非打包时只取候选段
logits = self.decode(out_embeddings)                                  # [B, C, 64]
continuous_predictions, head_logits_by_index = self.decode_continuous(out_embeddings, product_surface, ...)
  • 模型骨架采用 xrex/models/transformer.py 中的 Transformer 实现。旗舰配置包含 8 层,设置 emb_size=2560、20 个查询头、4 个 KV 头与 key_size=128(xrecsys.py:316-324、:1024-1062)。架构细节详见 Transformer 骨架。
  • 训练阶段启用序列打包时,模型依据 seqpack_layout.candidate_positions 提取候选位置,避免切片错位(recsys_model.py:4394-4406)。
  • 离散动作预测头:输出层包含 64 个槽位,由 ACTION_TYPE_MAP_LEN = 60 向上取整至 OUTPUT_VOCAB_K = 64(xrecsys.py:45-48、:154-156)。训练数据依据 ActionName 枚举值填充多热标签(phoenix/xrex/data/parquet_recsys.py:1925)。第 1 槽位对应点赞(SERVER_TWEET_FAV = 1),第 11 槽位对应“停留达标”(CLIENT_TWEET_RECAP_DWELLED = 11),其余槽位依次类推(recsys.proto:456-570)。端到端测试客户端直接通过 logits[ActionName.SERVER_TWEET_FAV] 读取点赞预测概率(phoenix/reference/retrieve_then_rank.py:152-160)。
  • 连续值预测头:输出层包含 8 个槽位,编号源自 ContinuousActionName:0 为主页访问秒数、1 为停留时长、2 为点击后停留时长、3 为首页视频续看秒数、4 为 bridge 概率,5 至 7 供广告业务使用(recsys.proto:699-708)。CREAD 槽位额外配置一个 [D, 30] 维度的阈值分类头。

训练目标

离散动作:多标签 BCE

模型为 64 个槽位分别计算 sigmoid 二元交叉熵,结合掩码求和后完成归一化(phoenix/xrex/models/loss_recsys.py:21-54,调用位置见 recsys_model.py:5033-5041)。损失权重包含三项调节策略:

  • 浏览类损失权重减半:旗舰配置设 view_loss_weight=0.5,降低浏览动作的损失比重(xrecsys.py:551、recsys_model.py:5011-5016)。
  • log-Q 样本校正:针对批次内采样的负样本,启用 log_q_correction=True,以候选帖子在批次内的出现频次倒数作为样本权重(recsys_model.py:1523-1538、:4485-4495、:4558-4559)。
  • 负样本负反馈屏蔽:负样本上的负反馈类预测头不参与损失计算(mask_neg_feedback_on_negatives=True,recsys_model.py:1312、:4990-4994)。

连续值:停留时长的 CREAD 损失(2026-10-08)

旗舰配置同时登记 5 条停留相关损失,通过权重配置决定实际生效规则(xrecsys.py:862-924):

metric_name 损失类型 适用产品面 norm_scale(秒) 旗舰权重(开源默认值)
dwell-time-tweedie Tweedie p=1.5 仅图集页 300 0.0(xrecsys.py:335)
dwell-time-mae MAE 图集页以外 30 0.0(xrecsys.py:336)
click-dwell-binary 是否超过 10 秒 全部 60 1.0(xrecsys.py:334)
dwell-time-cread CREAD 图集页以外 120 0.1(xrecsys.py:337)
dwell-time-cread-immersive CREAD 仅图集页 240 0.1(xrecsys.py:338)

validate_continuous_action_losses 限制同一槽位与同一产品面不得同时存在两条权重大于 0 的损失配置(recsys_model.py:418-431)。上游提交 35650fb(2026-10-08)将 Tweedie 与 MAE 的固定权重 0.1 改为可配置项并在旗舰配置中置 0,同时引入两条 CREAD 损失。

CREAD 损失计算包含四个步骤(loss_recsys.py:110-187、recsys_model.py:3452-3477):

  1. 生成对数阈值:从 2 秒至 norm_scale,算法按等比数列提取 30 个阈值。当 norm_scale=120 秒时,阈值序列约为 2.0、2.3、2.65 直至 90.48、104.2、120.0 秒(cread_log_thresholds,loss_recsys.py:110-125)。
  2. 多阈值二分类:模型针对每个阈值输出一个 logit,预测“真实停留时长 > 阈值”的概率,计算 30 个二元交叉熵损失的平均值(:156-158)。
  3. 期望积分还原:算法将 30 个 sigmoid 概率乘以对应区间宽度后累加求和,估算连续预测秒数(recsys_model.py:3472-3476)。预测值与截断后的真实值计算 Huber 损失(delta = 5 秒 / norm_scale),权重系数配置为 cread_restoration_weight=0.5(loss_recsys.py:159-185、recsys_model.py:5080-5083)。
  4. 过滤哨兵样本:真实停留为 0 或 2.0 秒哨兵值的样本不计入损失(recsys_model.py:5053-5058)。

在线推理时,连续头直接输出步骤 3 计算的还原秒数;home-mixer 获取的 dwell_time 得分由此生成。

旗舰配置另为首页视频续看秒数(权重 0.013)与主页访问秒数(权重 0.0035)分别配置 CREAD 损失:设置 norm_scale=300,仅在首页两个产品面生效,且仅在前置行为(打开视频或访问主页)触发时计入损失(xrecsys.py:271-313、:552-553)。

预测的行为全集:与上游 README 对照

home-mixer 获取 PhoenixScores 数据结构(xai-value-model/phoenix_scores.rs:1-32)。从 ActionName 槽位到具体字段的映射逻辑封装于 xai_candidate_pipeline 的 phoenix_prediction_client,该文件源码未包含,下表依据字段名称建立对应关系。

README 类别 README 列出的行为 PhoenixScores 字段 核对结果
Engagement favorite · reply · repost · quote · share · share via DM · share via copy link favorite_score reply_score retweet_score quote_score share_score share_via_dm_score share_via_copy_link_score 7 项都在
Clicks post · profile · link · photo expand · video open · quoted post click_score profile_click_score open_link_score photo_expand_score video_open_score quoted_click_score 6 项都在
Attention video quality view · dwell · dwell time · click dwell time · video continuation seconds · profile visit seconds vqv_score dwell_score dwell_time click_dwell_time home_video_continuation_secs home_profile_visit_secs 6 项都在;后两项只在 EnableHomeExcursionScores 打开时从连续头读出,开源默认 true(home-mixer/scorers/phoenix_scorer.rs:131-140、:171-183,home-mixer/params/param.rs:309-313)
Author follow author follow_author_score 在
Negative not interested · mute author · block author · report · not dwelled not_interested_score mute_author_score block_author_score report_score not_dwelled_score 5 项都在
README 未列 — quoted_vqv_score post_unexplored_score post_video_score grok_analyze_score 源码有、README 没写。前两个在打分里有条件权重(xai-value-model/scoring.rs:90-100);后两个按名称对应 SERVER_TWEET_POST_VIDEO = 44、CLIENT_TWEET_CLICK_GROK_ANALYZE = 46 槽位(推测)

各行为预测概率的加权打分机制详见 打分与排序。

home-mixer 怎样调用排序模型

PhoenixScorer(home-mixer/scorers/phoenix_scorer.rs)负责向排序服务发起请求并解析响应:

  • 调用条件:存在缓存候选时不触发排序;开关 disable_home_mixer_phoenix_ranker 开启时整体停用排序调用(phoenix_scorer.rs:25、:73-82)。
  • 产品面划分:关注流请求指定 HomeTimelineRankedFollowing,其余请求指定 HomeTimelineRanking(:90-94)。
  • 集群路由策略:默认路由至 Experiment1Fou;当用户历史动作数量低于 PhoenixRankerNewUserHistoryThreshold 时分流至新用户集群。开源默认阈值为 0,即默认不执行分流(:32-68,param.rs:56-60、:294-298)。
  • 整页解码重排:开启 EnablePageDecodeRerank 时,请求携带 page_decode 参数,模型额外输出整页精选序列(:105-127),开源默认配置为 false(param.rs:327-331)。对应训练实现在 xrex/models/page_decoder.py 与 page_rl.py,仅在 immersive_page_rl* 配置中启用(xrecsys.py:446-487)。
  • 额外结果获取:除 phoenix_scores 外,系统同时读取 backbone_scores 与 served_slate_context(:136-150)。

配置速查(开源默认值)

项 home_direct_packed(旗舰) home_direct_packed_nano(单卡)
emb_size / 层数 2560 / 8 512 / 4
查询头 / KV 头 20 / 4 4 / 2
嵌入表宽 1024 128
历史 / 候选长度 1022 / 64 1022 / 64
只更新候选的末尾层 2 0
用户 / 帖 / 作者 / IP 词表 1 亿 / 1 亿 / 3000 万 / 1000 万 10 万 / 10 万 / 3 万 / 0
每实体哈希数 2 2
稠密优化器 Muon Muon

出处:xrecsys.py:316-387、:532-577、:599-622。

设计决策

决策 理由
三段拼一条序列,候选互相隔离 候选直接注意用户与历史;单条候选的分数不受同批其它候选影响,可缓存
特征“各自投影再相加”成一个 token 加新特征只需加一张表或一个投影,序列长度不变
停留时长改用 CREAD 停留分布长尾,先分类再还原比直接回归秒数更稳。源码未写选型理由,此为推测
陈旧帖清零互动计数 防止老帖靠累计热度占优。源码未写理由,此为推测
末尾层只更新候选 历史在前 6 层后冻结,后 2 层只算 64 个候选 token,省算力

源码锚点

  • phoenix/xrex/models/recsys_model.py:2482 —— RecsysAggregatedModel
  • phoenix/xrex/models/recsys_model.py:4312-4463 —— __call__ 前向与输出头
  • phoenix/xrex/models/recsys_model.py:3452-3477 —— _decode_cread
  • phoenix/xrex/models/recsys_model.py:4950-5090 —— 离散与连续损失
  • phoenix/xrex/models/loss_recsys.py:21-246 —— 多热 BCE、CREAD、阈值二分类、Tweedie
  • phoenix/xrex/models/recsys_feature_prep.py:502-1342 —— 三段 token 的构造
  • phoenix/xrex/configs/xrecsys.py:316-622、:862-924 —— 旗舰与 nano 配置、连续损失清单
  • xai-value-model/phoenix_scores.rs:1-32 —— home-mixer 收到的预测字段
  • home-mixer/scorers/phoenix_scorer.rs:32-183 —— 调用方

相关页面

Type to search…

↑↓ navigate↵ selectEsc close