Skip to content

VM Ranker 重排服务

vm-ranker 服务按特性开关解析权重计算价值模型得分,并利用帖子多模态向量执行行列式点过程(DPP)多样性筛选

Updated

这一页回答什么

本页说明 home-mixer 的 VMRanker 打分器调用的 vm-ranker gRPC 重排服务。内容包括该服务的输入参数、计算流程、基于帖子多模态向量的行列式点过程(Determinantal Point Processes,简称 DPP)多样性筛选机制、配置参数,以及调用失败时的多级降级策略。

加权求和、作者多样性衰减和站外降权的具体公式详见 打分与排序。本页侧重说明各项算法在服务内部的调用机制与执行逻辑。

核心结论

  1. vm-ranker 是独立的 gRPC 服务:服务仅暴露 rank 与 get_carousel_model 两个 RPC 接口(vm-ranker/ranker_service.rs:70-173)。home-mixer 在每次 For You 推荐请求中均调用一次 rank 接口。
  2. 服务先计算价值模型得分,再执行 DPP 筛选:价值模型综合分由加权求和、作者探索奖励、新作者扶持以及作者多样性衰减与站外降权构成。所有算法逻辑均调用共享 crate xai-value-model(vm-ranker/scoring/value_model.rs:159-219)。
  3. 权重参数在服务内部按用户上下文动态解析:服务定期从配置中心同步特性开关(feature switch)。服务根据请求中携带的用户上下文(国家、语言、账号年龄等)为当前用户解析适用的参数组合(vm-ranker/ranking_config.rs:55-61)。
  4. DPP 仅负责候选子集筛选,不修改入选帖子的得分与相对排序:算法从得分最高的前 max_selected_rank 条候选帖子中,依据“高得分且彼此低相似度”准则贪心挑选最多 top_k 条帖子。入选候选保留原始得分,未入选候选得分置为 0(vm-ranker/scoring/dpp_model.rs:196-206;vm-ranker/dpp.rs:155-169)。
  5. 相似度基于帖子多模态向量的余弦相似度计算:向量数据来源于 Phoenix 的 xai-recsys-mm-server 客户端,采用 1024 维 f16 格式。针对转发帖子,服务查询原帖标识符。当缓存缺失向量时,服务生成随机单位向量替代(vm-ranker/scoring/dpp_model.rs:43-51、80-84)。
  6. DPP 在开源配置中的默认参数:theta = 0.65、max_selected_rank = 150(vm-ranker/params.rs:253-259);top_k = 50 取自服务启动参数(vm-ranker/args.rs:18-19)。话题请求默认跳过 DPP 筛选(params.rs:247-252)。
  7. 系统提供三级异常降级保障:服务并发满载时直接拒绝请求;缺少配置数据时透传 home-mixer 的输入得分;home-mixer 远程调用失败时采用本地加权分与新作者扶持结果,此时跳过多样性衰减、站外降权和 DPP 筛选。

在系统中的位置

flowchart LR
    subgraph HM["home-mixer"]
        VMS["VMRanker 打分器<br/>本地加权分 + 新作者扶持"]
    end
    subgraph VR["vm-ranker 服务"]
        RS["VMRankerServiceImpl<br/>信号量限流"]
        CFG["RankingConfig<br/>按观看者解析参数"]
        VAL["value_model::compute<br/>xai-value-model"]
        DPP["dpp_model + dpp<br/>贪心 DPP 筛选"]
        EMB[("EmbeddingStore<br/>帖子多模态 embedding")]
    end
    GIT[("配置仓库<br/>feature switch 文件")] -->|"git 同步 30 秒"| CFG
    VMS -->|"RankRequest"| RS
    RS --> VAL
    CFG --> VAL
    VAL --> DPP
    EMB --> DPP
    DPP -->|"RankResponse"| VMS

调用方 VMRanker 的请求组装和降级逻辑详见 打分与排序 的“VMRanker 打分器与兜底”小节。

调用方式

请求内容

home-mixer 的 RequestShape::build() 负责组装 RankRequest(home-mixer/scorers/vm_ranker_request.rs:27-76):

字段 内容
viewer_id / viewer 观看者 id 与上下文(国家、语言、客户端、角色、now_ms、feature switch 覆盖等),供服务解析参数
candidates[] 每个候选的 tweet_id、author_id、retweeted_tweet_id、本地分 score、in_network、is_reply、is_retweet、is_mutual_follow_author、cold_start_lift_to_rank、视频时长、全部 Phoenix 预测、缓存的 weighted_score、semantic_ids
value_model_id 固定为 "dpp"(vm_ranker_request.rs:8)
compute_value_model 固定为 true,让服务端重算价值模型分
topic_request 是否话题请求,影响站外系数和 DPP 开关
include_top_heads Under the Hood 请求时为真,服务返回每条帖子贡献最大的头
experiment_payload 可选的实验载荷:用户行为序列(去偏用)、内容特征、话题历史等

home-mixer 请求默认不设置 dpp_params 与 seed_tweet_id,两项字段在服务中采用默认空值。

客户端与超时

home-mixer 设定客户端超时时间为 400 毫秒(home-mixer/clients/vm_ranker_client.rs:21)。目标集群由 VMRankerClusterId 配置,默认值为 "Experiment1"(home-mixer/params/param.rs:560-565)。xDS 寻址与 DNS 寻址通道的切换机制见调用方页面说明。

服务入口

方法 VMRankerServiceImpl::rank()(vm-ranker/ranker_service.rs:72-149)按以下三步执行:

  1. 并发限流:服务通过 try_acquire 获取信号量许可。许可耗尽时立即返回 RESOURCE_EXHAUSTED 错误,不进入排队(ranker_service.rs:85-96)。最大并发许可数 max_concurrent_requests 默认值为 256(vm-ranker/args.rs:12-13)。
  2. 执行打分:组装包含 DPP 上下文、去偏模型与配置信息的 RankContext,随后调用 scoring::rank()(ranker_service.rs:111-129)。
  3. 返回结果:向调用方返回各候选帖子的 score、weighted_score 以及可选的 top_heads 贡献列表。

服务支持 gzip 与 zstd 格式压缩,单条 gRPC 消息大小上限为 20 MB(ranker_service.rs:59-67)。

参数来源与同步

当配置项 config_sync_enabled 为真时,服务在启动阶段从配置仓库拉取特性开关文件,随后每隔 config_sync_interval_secs(默认 30 秒)自动同步一次(vm-ranker/main.rs:69-98;vm-ranker/args.rs:48-61)。同步流程通过 git fetch 与 git reset --hard 执行(vm-ranker/config_sync.rs:99-125)。

每次接收到推荐请求时,RankingConfig::resolve() 利用用户上下文构建受众标识符(recipient),匹配实验规则并叠加请求自带的 fs_overrides 覆盖参数(vm-ranker/ranking_config.rs:55-61)。受众信息包含根据用户标识符 snowflake 时间戳推导出的账号年龄(天数、分钟数)与回流时间等特征字段(ranking_config.rs:70-103)。

文件 vm-ranker/params.rs 中定义的数值为代码默认值,仅在配置文件缺失对应键时生效。该文件与 home-mixer/params/param.rs 使用相同的配置键名,但部分代码默认值存在差异,具体对照见 打分与排序 的权重表。

rank() 的执行流程

flowchart TD
    A["RankRequest"] --> B["resolve_params<br/>按观看者解析参数"]
    B --> C{"DPP 上下文存在<br/>且 dpp_enabled?"}
    C -- 否 --> D["value_model::compute"]
    D --> E["返回价值模型分"]
    C -- 是 --> F["spawn_blocking"]
    F --> G["value_model::compute"]
    G --> H["dpp_model::rank_with_memo<br/>贪心选子集"]
    H --> I["入选保留原分<br/>未入选置 0"]

上述流程对应 vm-ranker/scoring/mod.rs:40-94。DPP 筛选属于纯 CPU 密集型核矩阵运算,因此服务将其派发至 spawn_blocking 专用线程池执行。

函数 dpp_enabled() 判定是否启用 DPP(scoring/mod.rs:96-98):当未解析到配置参数(例如未开启配置同步)时默认判定为开启;解析到参数时,要求 DppEnabled 为真,且必须是非话题请求或配置项 TopicDppEnabled 为真。参数解析成功后,配置中的 theta 与 max_selected_rank 会覆盖启动参数,启动参数设定的 top_k 保持不变(scoring/mod.rs:55-62)。

价值模型分

方法 value_model::compute()(vm-ranker/scoring/value_model.rs:48-70)按以下逻辑计算:

  1. 当请求字段 compute_value_model 为假且参数 ComputeValueModel 同样为假时,服务直接透传请求自带的 score。
  2. 当未成功解析到配置参数时,服务记录 fallback 监控指标并同样透传输入得分。
  3. 其余情况调用 compute_or_fallback() 计算得分(value_model.rs:159-219):
    • weights_from_params() 根据参数构建权重,并通过 WeightPerturbationSigma 执行针对用户的扰动(value_model.rs:72-121);
    • scoring_context() 选取适用的站外降权系数(value_model.rs:123-147);
    • 根据作者实验分组注入作者探索奖励(value_model.rs:243-262);
    • 调用 xai_value_model::compute_value_scores() 计算加权分与最终得分;
    • 当启用去偏逻辑时,服务通过用户去偏模型校准各行为概率并重新计算得分(value_model.rs:196-218)。

当所有候选帖子均包含缓存加权分且调用方未请求 top heads 时,服务直接返回首轮结果,跳过去偏计算(value_model.rs:180-194)。

用户去偏(默认关闭)

配置项 UserDebiasEnabled 默认值为 false(vm-ranker/params.rs:260-265)。开启该功能后,服务加载针对用户行为序列训练的轻量模型,估计当前用户在各项行为上的个人基线 logit,比对全局基线并将 Phoenix 输出概率向全局均值修正(vm-ranker/scoring/debias.rs:125-152、221-233)。校准强度由参数 UserDebiasAlpha(默认值为 0.0)与截断阈值 UserDebiasClipLogit(默认值为 2.0)控制(params.rs:266-277)。当模型权重文件超过 UserDebiasMaxArtifactAgeSecs(3600 秒)未更新时,服务跳过去偏步骤(params.rs:296-301)。去偏模型的权重存储路径与训练代码未包含在开源仓库中。

DPP 多样性筛选

输入数据

函数 build_dpp_inputs()(vm-ranker/scoring/dpp_model.rs:54-100)按以下步骤准备数据:

  1. 按照价值模型得分降序排列候选列表,截取前 max_selected_rank 条候选(dpp_model.rs:78)。
  2. 查询各帖子的向量表示:转发帖子使用被转发的原帖标识符,其余帖子使用自身标识符(dpp_model.rs:80-84)。
  3. 向量查询缺失时,生成随机单位向量并记录 embedding_missing 标记(dpp_model.rs:43-51、23-37)。

向量数据由 EmbeddingStore 提供,底层调用 xai_recsys_mm_server 的 MmEmbeddingsClient(vm-ranker/embedding_store.rs:19-22)。客户端在宿主机共享内存中维护按帖子标识符索引的 f16 向量缓存。服务启动时从对象存储拉取快照,并在内存中保留最近 2 天的向量条目(phoenix/crates/serving/xai-recsys-mm-server/src/mm_embedding_client.rs:24-30)。在向量快照预加载完成前,服务保持未就绪状态(vm-ranker/main.rs:143-151)。向量的生成流程详见 多模态帖子嵌入器。

核矩阵构建

函数 dpp::rescore()(vm-ranker/dpp.rs:35-221)构建 L 核矩阵:

s_max  = 池内最高分
q_i    = exp(α · s_i / s_max),   α = θ / (2·(1 − θ))
L_ii   = q_i²
L_ij   = q_i · q_j · cos(e_i, e_j)

上述公式实现于 dpp.rs:92-133。参数 θ 被限制在 [0, 1) 区间内。θ 越大,α 越大,分数差异在子集选择中的权重越高;θ 越小,算法越倾向于挑选语义正交的内容。在开源默认配置下 θ = 0.65,对应 α ≈ 0.929,此时得分最高的候选帖子的质量项 q 约为零分候选帖子的 2.53 倍。

贪心求解

函数 greedy_dpp()(vm-ranker/dpp.rs:223-314)基于增量 Cholesky 分解执行贪心最大后验概率(MAP)推断:

  1. 优先选取对角线元素 L_ii 最大的候选帖子(即质量分最高项;若请求包含种子帖子,则固定优先纳入种子)。
  2. 在每轮迭代中,为剩余候选计算加入当前集合后的行列式增益(条件方差),选取增益最大的候选帖子。
  3. 当增益低于 1e-6(表明候选与已选集合高度线性相关)或选满 top_k 条候选时终止迭代(dpp.rs:288-292)。

候选帖子与已选帖子的向量相似度越高,其条件方差越小,入选概率随之降低。该算法实现了兼顾高得分与内容差异性的目标。

输出结果

筛选出的候选集合按原始价值模型得分重新排序(dpp.rs:155-169)。函数 rank_with_memo() 保持入选帖子的原始分数值,未入选帖子的得分全部置为 0(dpp_model.rs:196-206)。DPP 筛选不重排入选帖子的相对顺序,主要将语义高相似度的低分候选排挤出展示队列。分数为 0 的候选帖子将在下游选择阶段被排至队列末尾。

种子帖子(seed_tweet_id)仅用于压制与其语义相似的候选,本身不进入输出列表,亦不占用 top_k 筛选配额(dpp_model.rs:102-125)。home-mixer 发起的 For You 推荐请求不传递种子帖子。

参数配置

参数 开源默认值 来源
DppEnabled true vm-ranker/params.rs:241-246
TopicDppEnabled false vm-ranker/params.rs:247-252
DppTheta 0.65 vm-ranker/params.rs:253
DppMaxSelectedRank 150 vm-ranker/params.rs:254-259
--dpp-enabled(启动参数) false vm-ranker/args.rs:15-16
--dpp-top-k 50 vm-ranker/args.rs:18-19
--dpp-theta 0.5 vm-ranker/args.rs:21-22
--dpp-max-selected-rank 100 vm-ranker/args.rs:24-25
--embedding-dim 1024 vm-ranker/args.rs:27-28

DPP 生效要求同时满足两个前提条件:启动参数 --dpp-enabled 处于开启状态(否则服务不初始化 DPP 上下文,main.rs:31-52),且请求级参数 DppEnabled 为真。开源仓库未包含生产环境的部署启动参数。当特性开关解析成功时,theta 与 max_selected_rank 采用配置值(0.65 与 150),top_k 维持启动参数配置(50)。

失败与兜底

失败点 结果 位置
服务并发已满 返回 RESOURCE_EXHAUSTED;home-mixer 用本地分 vm-ranker/ranker_service.rs:85-96
未启用配置同步或未带观看者上下文 价值模型直通,返回 home-mixer 带来的本地分 vm-ranker/scoring/value_model.rs:159-165、vm-ranker/scoring/mod.rs:126-134
帖子 embedding 缺失 用随机单位向量参与 DPP,近似视为与其他帖不相似 vm-ranker/scoring/dpp_model.rs:43-51
DPP 的阻塞任务异常 rank 返回错误;home-mixer 用本地分 vm-ranker/scoring/mod.rs:65-79
home-mixer 调用失败或超时 用本地加权分 + 新作者扶持;无多样性、站外降权、DPP home-mixer/scorers/vm_ranker.rs:107-113
响应缺少某个候选 该候选保留本地分 home-mixer/scorers/vm_ranker.rs:122-139

本地得分未包含作者多样性衰减和站外降权调整。因此在服务降级期间,推荐流中同一作者的帖子密度相对上升,站外帖子的排序位次相对靠前。该现象依据源码逻辑推导得出,开源仓库未提供生产环境的实测数据。

视频轮播模型

RPC 接口 get_carousel_model 根据名称返回 JSON 格式的模型配置文件。文件从 --carousel-model-dir 目录读取,服务每隔 60 秒自动重新加载(vm-ranker/ranker_service.rs:151-172;vm-ranker/carousel_model.rs:10-40;vm-ranker/args.rs:45-46)。未配置该目录时服务返回 UNAVAILABLE 状态码。开源仓库未包含模型具体内容与调用端细节的详细说明。

设计决策

决策 理由
价值模型分在服务端按观看者重算 实验参数按用户解析,可在不改 home-mixer 的情况下调权重
DPP 只筛选、不改分 保留价值模型对入选帖的排序,DPP 只负责把相似内容挤出前列
用多模态 embedding 余弦衡量相似 能识别文字不同但内容相近的帖子(同一张图、同一段视频),作者多样性做不到这一点
缺 embedding 时用随机向量 不让缺数据的帖子整体出局;随机向量与其他向量近似正交
话题请求默认不做 DPP 话题页本来就要同类内容,按相似度去重会伤害相关性
try_acquire 立即拒绝 调用方有本地兜底,排队等待只会拉长尾延迟
embedding 预加载完才就绪 冷启动时缺 embedding 会让 DPP 退化成随机筛选

源码锚点

  • vm-ranker/main.rs:31-151 —— 启动:DPP 上下文、去偏、配置同步、就绪门控
  • vm-ranker/args.rs:12-64 —— 启动参数默认值
  • vm-ranker/params.rs:235-301 —— 价值模型、DPP、去偏参数
  • vm-ranker/ranker_service.rs:72-149 —— rank RPC 入口与限流
  • vm-ranker/ranking_config.rs:55-103 —— 按观看者解析参数
  • vm-ranker/scoring/mod.rs:40-134 —— rank() 流程与 DPP 开关
  • vm-ranker/scoring/value_model.rs:48-219 —— 价值模型计算与直通
  • vm-ranker/scoring/dpp_model.rs:43-206 —— DPP 输入、embedding 查询与输出
  • vm-ranker/dpp.rs:35-314 —— 核矩阵与贪心 DPP
  • home-mixer/scorers/vm_ranker_request.rs:27-76 —— 调用方请求组装

相关页面

Type to search…

↑↓ navigate↵ selectEsc close