【机器学习】深度学习推荐系统(二十七): X 推荐算法rerank机制详解

X 推荐算法分数修正机制详解

前言

在 X(原 Twitter)的推荐系统中,多模型融合后的分数并不是最终分数。融合分数会经过多个步骤的修正,包括启发式规则、多样性调整、Phoenix 重评分等。本文将详细解析这些分数修正机制。


一、分数修正流程概览

1.1 完整流程

复制代码
┌─────────────────────────────────────────────────────────┐
│  步骤1: 多模型融合                                        │
│  WeighedModelRerankingScorer                            │
│  输出: WeightedModelScoreFeature = Σ(score_i × weight_i) │
└─────────────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  步骤2: 启发式重评分                                      │
│  HeuristicScorer                                        │
│  应用多个乘法因子调整分数                                 │
│  输出: ScoreFeature = WeightedModelScore × scaleFactor  │
└─────────────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  步骤3: Phoenix 重评分(可选)                            │
│  PhoenixRescoringFeatureHydrator                        │
│  使用 Phoenix 分数调整                                   │
└─────────────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  步骤4: 多样性重评分(可选)                              │
│  DiversityRescoringFeatureHydrator                     │
│  基于嵌入的多样性调整(MMR算法)                           │
└─────────────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  步骤5: 类别多样性重评分(可选)                          │
│  CategoryDiversityRescoringFeatureHydrator              │
│  基于类别的多样性调整                                     │
└─────────────────────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  最终分数(ScoreFeature)                                 │
│  用于排序推文                                             │
└─────────────────────────────────────────────────────────┘

1.2 关键发现

多模型融合后的分数不是最终分数! 它还会经过:

  1. 启发式重评分:应用多个业务规则
  2. Phoenix 重评分:使用 Phoenix 模型调整
  3. 多样性重评分:确保内容多样性
  4. 类别多样性重评分:确保类别多样性

二、启发式重评分(HeuristicScorer)

2.1 概述

HeuristicScorer 是第一个修正步骤,通过乘法因子调整融合后的分数。

2.2 核心算法

scala 复制代码
val scaleFactor = rescorers.map(_(query, candidate)).product
val updatedScore = scoreOpt.map { score =>
  if (score < Epsilon && noNegHeuristic) score 
  else score * scaleFactor
}

公式

复制代码
finalScore = weightedModelScore × scaleFactor

其中:
scaleFactor = rescore1 × rescore2 × ... × rescoreN

2.3 重评分规则列表

系统应用以下 20+ 个重评分规则

2.3.1 基础重评分规则
规则名称 说明 影响
RescoreOutOfNetwork 外部网络内容调整 降低外部网络推文分数
RescoreReplies 回复内容调整 调整回复推文的分数
RescoreLiveContent 直播内容调整 提升直播内容的分数
2.3.2 归一化规则
规则名称 说明 影响
RescoreMTLNormalization 多任务学习归一化 使用 MTL 归一化器调整分数

MTL 归一化参数

  • AlphaParam: Alpha 参数(除以 100)
  • BetaParam: Beta 参数
  • GammaParam: Gamma 参数
2.3.3 列表式重评分规则(Listwise Rescoring)

这些规则基于整个候选列表进行调整:

规则名称 说明 影响
ContentExplorationListwiseRescoringProvider 内容探索列表式重评分 调整内容探索候选的分数
DeepRetrievalListwiseRescoringProvider 深度检索列表式重评分 调整深度检索候选的分数
EvergreenDeepRetrievalListwiseRescoringProvider 常青深度检索列表式重评分 调整常青深度检索候选的分数
EvergreenDeepRetrievalCrossBorderListwiseRescoringProvider 跨境常青深度检索列表式重评分 调整跨境常青深度检索候选的分数
AuthorBasedListwiseRescoringProvider 基于作者列表式重评分 调整作者多样性
ImpressedAuthorDecayRescoringProvider 已读作者衰减列表式重评分 降低已印象作者的分数
ImpressedMediaClusterBasedListwiseRescoringProvider 已读媒体聚类列表式重评分 降低已印象媒体聚类的分数
ImpressedImageClusterBasedListwiseRescoringProvider 已读图片聚类列表式重评分 降低已印象图片聚类的分数
CandidateSourceDiversityListwiseRescoringProvider 候选源多样性列表式重评分 调整候选源多样性
GrokSlopScoreRescorer Grok Slop 分数重评分 基于 Grok Slop 分数调整
MultimodalEmbeddingRescorer 多模态嵌入重评分 基于多模态嵌入调整
2.3.4 其他重评分规则
规则名称 说明 影响
RescoreFeedbackFatigue 反馈疲劳重评分 降低频繁反馈内容的分数
ControlAiRescorer.allRescorers AI 控制重评分器 各种 AI 控制规则

2.4 重评分规则的工作原理

每个重评分规则返回一个乘法因子(通常是 0.5 到 2.0 之间):

scala 复制代码
trait RescoringFactorProvider {
  def apply(query: PipelineQuery, candidate: CandidateWithFeatures[TweetCandidate]): Double
}

示例

  • RescoreOutOfNetwork 可能返回 0.8(降低外部网络内容 20%)
  • RescoreLiveContent 可能返回 1.2(提升直播内容 20%)

2.5 完整代码

scala 复制代码
object HeuristicScorer extends Scorer[PipelineQuery, TweetCandidate] {
  override def apply(
    query: PipelineQuery,
    candidates: Seq[CandidateWithFeatures[TweetCandidate]]
  ): Stitch[Seq[FeatureMap]] = {
    val rescorers = Seq(
      RescoreOutOfNetwork,
      RescoreReplies,
      RescoreMTLNormalization(...),
      RescoreListwise(ContentExplorationListwiseRescoringProvider(...)),
      // ... 其他重评分规则
      RescoreLiveContent
    ) ++ ControlAiRescorer.allRescorers

    val updatedScores = candidates.map { candidate =>
      val scoreOpt = candidate.features.getOrElse(ScoreFeature, None)
      
      // 计算所有重评分规则的乘积
      val scaleFactor = rescorers.map(_(query, candidate)).product
      
      // 应用乘法因子
      val updatedScore = scoreOpt.map { score =>
        if (score < Epsilon && noNegHeuristic) score 
        else score * scaleFactor
      }
      
      FeatureMap(ScoreFeature, updatedScore)
    }
    
    Stitch.value(updatedScores)
  }
}

2.6 实际示例

假设一个推文的融合分数是 10.0,应用以下重评分规则:

规则 乘法因子
RescoreOutOfNetwork 0.8
RescoreReplies 1.1
RescoreMTLNormalization 0.95
RescoreListwise(AuthorBased) 1.05
RescoreFeedbackFatigue 0.9

计算

复制代码
scaleFactor = 0.8 × 1.1 × 0.95 × 1.05 × 0.9 = 0.79
finalScore = 10.0 × 0.79 = 7.9

三、Phoenix 重评分(PhoenixRescoringFeatureHydrator)

3.1 概述

PhoenixRescoringFeatureHydrator 使用 Phoenix 模型的分数来调整最终分数。

3.2 使用条件

scala 复制代码
val usePhoenixRescoring =
  query.params(EnablePhoenixScorerParam) &&
  query.params(EnablePhoenixRescoreParam) &&
  !query.params(EnablePhoenixScoreParam)

条件

  • 启用 Phoenix Scorer
  • 启用 Phoenix 重评分
  • 启用 Phoenix 分数(如果已启用,则直接使用 Phoenix 分数)

3.3 重评分算法

scala 复制代码
val score = candidate.features.getOrElse(ScoreFeature, None).getOrElse(0.0)
val weightedModelScore = candidate.features.getOrElse(WeightedModelScoreFeature, None).getOrElse(0.0)
val phoenixScore = candidate.features.getOrElse(PhoenixScoreFeature, None).getOrElse(0.0)

if (score == 0.0 || weightedModelScore == 0.0) {
  0.0
} else if (usePhoenixRescoring) {
  phoenixScore * (score / weightedModelScore)
} else {
  score
}

公式

复制代码
finalScore = phoenixScore × (currentScore / weightedModelScore)

其中:
- currentScore: 当前分数(经过启发式重评分后)
- weightedModelScore: 原始融合分数
- phoenixScore: Phoenix 模型预测分数

3.4 工作原理

Phoenix 重评分通过比例缩放的方式调整分数:

  1. 计算当前分数相对于原始融合分数的比例
  2. 使用 Phoenix 分数乘以这个比例

示例

  • 原始融合分数:10.0
  • 启发式重评分后:8.0(比例 = 0.8)
  • Phoenix 分数:12.0
  • 最终分数:12.0 × 0.8 = 9.6

3.5 使用场景

  • Phoenix 模型更准确:当 Phoenix 模型的预测更准确时
  • A/B 测试:用于测试 Phoenix 模型的效果
  • 渐进式迁移:从旧模型逐步迁移到 Phoenix 模型

四、多样性重评分(DiversityRescoringFeatureHydrator)

4.1 概述

DiversityRescoringFeatureHydrator 使用 MMR(Maximal Marginal Relevance)算法来确保内容的多样性。

4.2 MMR 算法

MMR 算法在相关性和多样性之间取得平衡:

复制代码
score = relevance + diversityWeight × minDistance

其中:
- relevance: 当前候选的相关性分数(ScoreFeature)
- diversityWeight: 多样性权重
- minDistance: 与已选择候选的最小距离

4.3 算法实现

scala 复制代码
def mmr(
  query: PipelineQuery,
  candidates: Seq[CandidateWithFeatures[TweetCandidate]],
  distanceMatrix: DenseMatrix[Double]
): Seq[Double] = {
  val diversityRatio = query.params(TwhinDiversityRescoringRatioParam)
  val diversityWeight = query.params(TwhinDiversityRescoringWeightParam)
  val selected = scala.collection.mutable.Set[Int]()
  val newScores = Array.fill(n)(0.0)

  for (i <- 0 until n) {
    var maxScore = Double.NegativeInfinity
    var bestCandidateIndex = -1

    for ((candidate, index) <- candidatesWithIndex) {
      if (!selected.contains(index)) {
        val relevance = candidate.features.getOrElse(ScoreFeature, None).getOrElse(0.0)
        val minDistance = {
          if (selected.isEmpty || selected.size < (1 - diversityRatio) * n) 
            None
          else
            selected.map(j => distanceMatrix(index, j)).reduceOption(_ min _)
        }
        val score = relevance + diversityWeight * minDistance.getOrElse(2.0)
        if (score > maxScore) {
          maxScore = score
          bestCandidateIndex = index
        }
      }
    }

    if (bestCandidateIndex != -1) {
      selected += bestCandidateIndex
      newScores(bestCandidateIndex) = maxScore
    }
  }

  newScores.toSeq
}

4.4 距离计算

使用嵌入向量的欧氏距离

scala 复制代码
// 1. 获取嵌入向量
val embeddings = candidates.map { candidate =>
  candidate.features
    .getOrElse(TransformerPostEmbeddingJointBlueFeature, EmptyDataRecord)
    .getTensors
    .get(PostTransformerEmbeddingsJointBlueFeature.getFeatureId)
    .getFloatTensor.floats
    .map(_.doubleValue)
    .toSeq
}

// 2. 归一化嵌入向量
val denseEmbeddingsNormalized = embeddings.map { seq =>
  val denseVector = DenseVector(seq.toArray)
  val normVal = norm(denseVector)
  if (normVal != 0) denseVector / normVal
  else defaultDenseVector
}

// 3. 计算距离矩阵
for (i <- denseEmbeddingsNormalized.indices; j <- denseEmbeddingsNormalized.indices) {
  distanceMatrix(i, j) = norm(denseEmbeddingsNormalized(i) - denseEmbeddingsNormalized(j))
}

4.5 参数配置

参数名称 说明 默认值
TwhinDiversityRescoringWeightParam 多样性权重 可配置
TwhinDiversityRescoringRatioParam 多样性比例 可配置

4.6 工作流程

  1. 计算嵌入向量:获取每个候选的嵌入向量
  2. 归一化嵌入:归一化嵌入向量
  3. 计算距离矩阵:计算所有候选之间的欧氏距离
  4. MMR 选择:使用 MMR 算法重新计算分数
  5. 更新分数 :更新 ScoreFeature

4.7 实际示例

假设有 3 个候选推文:

候选 原始分数 嵌入向量
A 10.0 [0.8, 0.6, ...]
B 9.0 [0.7, 0.5, ...]
C 8.0 [0.9, 0.7, ...]

距离矩阵

复制代码
    A    B    C
A  0.0  0.2  0.3
B  0.2  0.0  0.4
C  0.3  0.4  0.0

MMR 选择(假设 diversityWeight = 0.5):

  1. 选择 A(分数最高)
  2. 选择 C(虽然 B 分数更高,但 C 与 A 距离更远)
  3. 选择 B

最终分数

  • A: 10.0 + 0.5 × 0.0 = 10.0
  • C: 8.0 + 0.5 × 0.3 = 8.15
  • B: 9.0 + 0.5 × 0.2 = 9.1

五、类别多样性重评分(CategoryDiversityRescoringFeatureHydrator)

5.1 概述

CategoryDiversityRescoringFeatureHydrator 基于内容类别来确保多样性。

5.2 算法原理

使用惩罚机制来降低重复类别的分数:

复制代码
score = max(relevance - weight × penalty, 0.00001)

其中:
penalty = Σ log2(count(category) + 1)

- relevance: 当前候选的相关性分数
- weight: 多样性权重
- count(category): 已选择候选中该类别的数量

5.3 算法实现

scala 复制代码
private def diversityPenalty(
  query: PipelineQuery,
  candidates: Seq[CandidateWithFeatures[TweetCandidate]],
): Seq[Double] = {
  val weight = query.params(CategoryDiversityRescoringWeightParam)
  val k = query.params(CategoryDiversityKParam)
  val selected = scala.collection.mutable.Set[Int]()
  val newScores = Array.fill(n)(0.0)
  val clusterCntMap = scala.collection.mutable.Map[String, Int]()

  val topKCategories = getCandidateCategory(k, candidates)

  for (i <- 0 until n) {
    var maxScore = Double.NegativeInfinity
    var bestCandidateIndex = -1

    for ((candidate, index, categories) <- candidatesWithIndexWithCategories) {
      if (!selected.contains(index)) {
        val relevance = candidate.features.getOrElse(ScoreFeature, None).getOrElse(0.0)
        
        // 计算惩罚
        var penalty = 0.0
        categories.foreach { category =>
          val cnt = clusterCntMap.getOrElse(category, 0)
          penalty += math.log(cnt + 1) / math.log(2)  // log2
        }

        val score = math.max(relevance - weight * penalty, 0.00001)

        if (score > maxScore) {
          maxScore = score
          bestCandidateIndex = index
          candidateCategories = categories
        }
      }
    }

    if (bestCandidateIndex != -1) {
      selected += bestCandidateIndex
      newScores(bestCandidateIndex) = maxScore
      // 更新类别计数
      candidateCategories.foreach { category =>
        clusterCntMap.put(category, clusterCntMap.getOrElse(category, 0) + 1)
      }
    }
  }

  newScores.toSeq
}

5.4 类别提取

从 SimClusters 特征中提取 Top-K 类别:

scala 复制代码
private def getCandidateCategory(
  k: Int,
  candidates: Seq[CandidateWithFeatures[TweetCandidate]]
): Seq[Seq[String]] = {
  candidates.map { candidate =>
    val topKClustersMap = candidate.features
      .getOrElse(SimClustersLogFavBasedTweetFeature, new DataRecord())
      .getSparseContinuousFeatures
      .get(SimclustersSparseTweetEmbeddingsFeature.getFeatureId)
    
    topKClustersMap
      .asScala
      .toSeq
      .sortBy(-_._2)  // 按分数降序
      .take(k)
      .map(_._1)  // 提取类别名称
  }
}

5.5 参数配置

参数名称 说明 默认值
CategoryDiversityRescoringWeightParam 多样性权重 可配置
CategoryDiversityKParam Top-K 类别数 可配置

5.6 实际示例

假设有 3 个候选推文:

候选 原始分数 类别
A 10.0 [sports, tech]
B 9.0 [sports, news]
C 8.0 [tech, science]

选择过程(假设 weight = 0.5):

  1. 选择 A(分数最高)

    • 分数:10.0 - 0.5 × (log2(1) + log2(1)) = 10.0 - 0.5 × (0 + 0) = 10.0
    • 更新计数:sports=1, tech=1
  2. 选择 C(虽然 B 分数更高,但类别更不同)

    • B 惩罚:0.5 × (log2(2) + log2(1)) = 0.5 × (1 + 0) = 0.5,分数 = 9.0 - 0.5 = 8.5
    • C 惩罚:0.5 × (log2(2) + log2(1)) = 0.5 × (1 + 0) = 0.5,分数 = 8.0 - 0.5 = 7.5
    • 选择 B(分数更高)
    • 更新计数:sports=2, news=1
  3. 选择 C

    • 惩罚:0.5 × (log2(3) + log2(2)) = 0.5 × (1.58 + 1) = 1.29,分数 = 8.0 - 1.29 = 6.71

六、分数修正流程总结

6.1 完整流程

复制代码
原始融合分数 (WeightedModelScoreFeature)
    ↓
启发式重评分 (HeuristicScorer)
    ↓ ScoreFeature = WeightedModelScore × scaleFactor
Phoenix 重评分 (可选)
    ↓ ScoreFeature = PhoenixScore × (ScoreFeature / WeightedModelScore)
多样性重评分 (可选)
    ↓ ScoreFeature = MMR算法重新计算
类别多样性重评分 (可选)
    ↓ ScoreFeature = ScoreFeature - penalty
最终分数 (ScoreFeature)

6.2 分数修正的影响

修正步骤 影响范围 主要目的
启发式重评分 所有候选 应用业务规则
Phoenix 重评分 所有候选 使用更准确的模型
多样性重评分 所有候选 确保内容多样性(嵌入)
类别多样性重评分 所有候选 确保类别多样性

6.3 修正顺序的重要性

修正顺序很重要,因为:

  1. 启发式重评分:应用基础业务规则
  2. Phoenix 重评分:基于启发式重评分后的分数
  3. 多样性重评分:基于前面的分数,使用 MMR 算法
  4. 类别多样性重评分:基于前面的分数,使用惩罚机制

6.4 实际示例

假设一个推文的完整修正过程:

步骤1:多模型融合

复制代码
WeightedModelScoreFeature = 10.0

步骤2:启发式重评分

复制代码
scaleFactor = 0.8 × 1.1 × 0.95 = 0.836
ScoreFeature = 10.0 × 0.836 = 8.36

步骤3:Phoenix 重评分(假设启用)

复制代码
PhoenixScore = 12.0
ScoreFeature = 12.0 × (8.36 / 10.0) = 12.0 × 0.836 = 10.032

步骤4:多样性重评分(假设启用)

复制代码
使用 MMR 算法重新计算
ScoreFeature = 9.5(假设)

步骤5:类别多样性重评分(假设启用)

复制代码
penalty = 0.3
ScoreFeature = 9.5 - 0.3 = 9.2

最终分数9.2


七、关键代码位置

7.1 启发式重评分

  • 文件home-mixer/server/src/main/scala/com/twitter/home_mixer/product/scored_tweets/scorer/HeuristicScorer.scala
  • 关键方法apply

7.2 Phoenix 重评分

  • 文件home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/feature_hydrator/PhoenixRescoringFeatureHydrator.scala
  • 关键方法apply

7.3 多样性重评分

  • 文件home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/feature_hydrator/DiversityRescoringFeatureHydrator.scala
  • 关键方法mmr

7.4 类别多样性重评分

  • 文件home-mixer/server/src/main/scala/com/twitter/home_mixer/functional_component/feature_hydrator/CategoryDiversityRescoringFeatureHydrator.scala
  • 关键方法diversityPenalty

八、总结

8.1 核心要点

  1. 多模型融合后的分数不是最终分数
  2. 启发式重评分:应用 20+ 个业务规则
  3. Phoenix 重评分:使用 Phoenix 模型调整
  4. 多样性重评分:使用 MMR 算法确保多样性
  5. 类别多样性重评分:使用惩罚机制确保类别多样性

8.2 修正公式总结

复制代码
最终分数 = 融合分数 
         × 启发式因子1 × 启发式因子2 × ... × 启发式因子N
         × (PhoenixScore / WeightedModelScore)  [可选]
         × MMR调整  [可选]
         - 类别惩罚  [可选]

8.3 设计理念

  1. 模块化:每个修正步骤独立,易于调整
  2. 可配置:通过 Feature Switches 控制是否启用
  3. 灵活性:支持 A/B 测试和渐进式迁移
  4. 多样性:确保推荐结果的多样性
相关推荐
LDG_AGI2 小时前
【机器学习】深度学习推荐系统(二十五): X 推荐算法特征系统详解:230+ 特征全解析
人工智能·分布式·深度学习·算法·机器学习·推荐算法
子午2 小时前
【2026原创】鱼类识别系统~Python+深度学习+CNN卷积神经网络算法+模型训练+图像识别
图像处理·python·深度学习·cnn
地理探险家2 小时前
【YOLOv8实战】15组衣物类深度学习数据集分享|附加载+标签管理代码
人工智能·python·深度学习·yolo·模型训练·电商视觉
LDG_AGI2 小时前
【机器学习】深度学习推荐系统(二十八):X 推荐算法listwiseRescoring(同刷多样性降权)机制详解
人工智能·分布式·深度学习·算法·机器学习·推荐算法
学术小白人2 小时前
2026 年人工智能与社交网络系统国际学术会议暨智能与网络安全研讨圆满落幕
大数据·人工智能·科技·物联网·机器学习
白日做梦Q2 小时前
GAN入门到精通:从DCGAN到StyleGAN3
人工智能·深度学习·计算机视觉
机器学习之心2 小时前
GA-TCN-Transformer组合模型回归+SHAP分析+新数据预测+多输出!深度学习可解释分析MATLAB代码
深度学习·回归·transformer·shap分析
格林威2 小时前
Baumer相机最新SDK开发_下载_封装
人工智能·数码相机·opencv·机器学习·计算机视觉·视觉检测·halcon
笑脸惹桃花2 小时前
目标检测YOLO26教程:YOLO26(Ultralytics)环境配置,适合零基础纯小白,超详细快速上手
人工智能·深度学习·yolo·目标检测·计算机视觉