0


拆解推荐系统:候选生成、过滤、排序、多样性的分层设计

推荐系统不是单一算法而是一条流水线,每个阶段在不同约束下解决不同的问题。多数入门实现把所有事情塞进一步:算相似度。但生产级系统需要做关注点分离,分别管控质量、速度和行为。

本文梳理一条可以实际构建并持续扩展的端到端推荐 Pipeline。

动手写代码前,先回答三个问题:Item是什么(电影、商品、帖子),用户行为是什么(点击、观看、购买),衡量成功的指标是什么(CTR、观看时长、留存率)。

定义不清楚,后续所有优化方向都可能跑偏。

数据层(实际用到的数据)

需要两类数据。Item数据:元数据(类型、标签、描述)、时间戳(发布、更新)、热度指标(浏览量、评分)。用户数据:交互历史(点击、观看、跳过)及对应的时间戳。

最小数据结构如下:

 items = {
    "id": 1,
    "genre": ["action", "sci-fi"],
    "popularity": 0.8,
    "timestamp": 2024
}

user = {
    "history": [1, 5, 9],
    "recent": [5, 9]
 }

候选生成(缩小搜索空间)

对全量数据做排序在工程上不可行,所以第一步是把候选集从数千缩减到几百。

常用手段包括基于内容的过滤、关键词匹配、向量相似度检索:

 candidates=get_similar_items(user["recent"])

要求很简单——快、覆盖广,不要求精确。

过滤层(剔除不合适的选项)

排序之前,先去掉明显不该出现的结果:已经看过的、过时的内容、无效条目。

 candidates = [
     i for i in candidates
     if i not in user["history"]
 ]

单独做好过滤这一步,系统整体质量就能有明显改善。

特征工程(准备信号)

每个候选项需要计算多维信号:相似度分数、热度分数、新鲜度分数、用户偏好匹配度:

 def compute_features(user, item):
     return {
         "similarity": sim(user, item),
         "popularity": item["popularity"],
         "recency": 1 / (current_year - item["timestamp"] + 1)
     }

排序层(核心决策引擎)

多维信号需要组合成一个最终分数:

 score= (
     0.5*similarity+
     0.3*popularity+
     0.2*recency
 )

权重不是固定值。它取决于item目标、用户行为模式以及 A/B 实验的结论,需要持续调整。

多样性注入(避免重复)

跳过这一环节,推荐结果会迅速坍缩到单一类别。

所以可以先选出得分最高的物品,之后每选一个新物品,对其与已选集合的相似度施加惩罚:

 adjusted_score = score - λ * similarity_with_selected

最终选择(Top-N 输出)

选出排序后的前 N 个结果:

 final = sorted(candidates, key=lambda x: x["score"], reverse=True)[:10]

这就是最终呈现给用户的推荐列表。

反馈收集

没有反馈,系统就没有迭代的依据。需要追踪的核心事件:点击、跳过、停留时长。

 feedback = {
     "clicked": [item_id],
     "skipped": [item_id]
 }

更新机制(使系统具备自适应能力)

反馈数据用来调整用户画像和特征权重:

 if item in feedback["clicked"]:
     increase_weight("similarity")
 
 if item in feedback["skipped"]:
     decrease_weight("similarity")

完整 Pipeline 流程

用户 → 候选生成 → 过滤 → 特征 → 排序 → 多样化 → 输出 → 反馈 → 更新

每个模块相互独立,可以单独替换和优化。

总结

推荐系统的核心不在于找到相似的物品,而在于在约束条件下选出合适的物品。

一个系统如果能做到有效过滤、基于多维信号排序、根据反馈持续调整,它就不再只是一个练手项目——而是一个可以在生产环境中运行的系统。

“拆解推荐系统:候选生成、过滤、排序、多样性的分层设计”的评论:

还没有评论