游乐游手机版
首页/AI教程/文章详情

推荐系统架构实战:从千人一面到千人千面优化路径

时间:2026-08-15 14:55
推荐系统从热门推荐演变为协同过滤和深度学习,实现从千人一面到千人千面。系统架构分为召回、排序、输出三层,召回层采用多路并行策略,排序层通过特征工程与模型精确打分,提升转化率与长尾商品曝光。
# 推荐系统架构:从千人一面到千人千面

先从一个真实的电商案例讲起。

2019年,我们运营过一个电商平台,当时商品列表完全按照发布时间排序。用户想找到需要的商品,只能一页一页往后翻,整体购物体验非常差。至于转化率?只有1%。

更严重的是,平台里的大量长尾商品几乎没有曝光机会,流量高度集中在少数热门商品上,资源浪费非常明显。

后来我们上线了推荐系统,平台情况才出现了根本性改善。首页开始提供个性化推荐,同时增加了“相关商品推荐”“猜你喜欢”等常见推荐功能。结果也非常直接:转化率提升到3%,几乎增长了三倍。

这背后其实体现了推荐系统最核心的商业价值:它绝不只是一个提升体验的小功能,而是平台的流量分发中枢,也是决定业务能否持续增长的重要能力。

【架构实战】推荐系统架构:从千人一面到千人千面

## 推荐系统演进 推荐系统的发展并不是一步到位的,它的演进路径,实际上反映了推荐算法、系统架构与业务需求持续博弈和升级的过程。 ### 阶段一:热门推荐 这是最早期、也最容易落地的推荐方式。 ```text ┌─────────────────────────────────────────────────────────────────┐ │推荐系统 - 阶段一 │ │ │ │所有用户 → 相同推荐结果 │ │ │ │推荐逻辑: │ │1. 按销量排序 │ │2. 取Top 100 │ │3. 随机打乱展示 │ │ │ │问题: │ │- 千人一面,无个性化 │ │- 马太效应,热门越热 │ │- 长尾商品无曝光 │ │ │ └──────────────────────────────────────────────────────────────────┘ ``` 这个阶段的推荐逻辑非常直接:所有用户看到的内容都一样,系统先按照销量排序,再取Top 100,最后做一次随机打散展示。问题也很突出:没有任何个性化能力,热门商品会越来越热门,长尾商品则几乎彻底失去展示机会。 ### 阶段二:协同过滤 冷启动问题,几乎是每个推荐系统工程师都会遇到的经典难题,而协同过滤正是在这样的背景下成为主流方案之一。 ```text ┌─────────────────────────────────────────────────────────────────┐ │推荐系统 - 阶段二 │ │ │ │用户A → 相似用户 → 推荐用户A未购买的商品 │ │ │ │User-based CF: │ │1. 找到和用户A相似的用户 │ │2. 推荐相似用户喜欢的商品 │ │ │ │Item-based CF: │ │1. 找到用户A喜欢的商品 │ │2. 推荐和这些商品相似的商品 │ │ │ │优点: │ │- 千人千面 │ │- 推荐效果提升 │ │ │ │问题: │ │- 冷启动问题 │ │- 数据稀疏问题 │ │ │ └──────────────────────────────────────────────────────────────────┘ ``` 协同过滤让推荐系统真正迈入“个性化推荐”阶段。基于用户的协同过滤,本质上是先找到与你行为相似的用户,再把他们喜欢的商品推荐给你;基于物品的协同过滤,则是从你已经喜欢的商品出发,继续推荐相似商品。虽然推荐效果明显提升,但冷启动和数据稀疏这两个关键问题也随之暴露——新用户和新商品缺少行为数据,系统往往难以准确推荐。 ### 阶段三:深度学习推荐 随着深度学习技术成熟,推荐系统进入了更智能、更复杂的新阶段。 ```text ┌─────────────────────────────────────────────────────────────────┐ │推荐系统 - 阶段三 │ │ │ │用户 → 特征工程 → 深度学习模型 → 推荐结果 │ │ │ │模型: │ │- Wide & Deep │ │- DeepFM │ │- DIN/DIEN │ │ │ │特征: │ │- 用户特征:年龄、性别、历史行为 │ │- 商品特征:类目、品牌、价格 │ │- 上下文特征:时间、地点、设备 │ │ │ │优点: │ │- 推荐效果大幅提升 │ │- 自动学习特征组合 │ │ │ └──────────────────────────────────────────────────────────────────┘ ``` 深度学习推荐模型,例如Wide & Deep、DeepFM、DIN、DIEN等,不再高度依赖人工规则,而是通过大规模数据自动学习复杂的特征交互关系。用户特征、商品特征、场景上下文特征都可以输入模型,从而显著提升推荐精度和点击转化表现。不过,与效果提升相伴而来的,是更高的系统复杂度、计算成本以及模型训练与维护成本。 ## 推荐系统架构 ### 整体架构 一个成熟的推荐系统架构,通常可以分为三层:召回层、排序层和输出层。 ```text ┌─────────────────────────────────────────────────────────────────┐ │推荐系统整体架构 │ │ │ │┌─────────────────────────────────────────────────────────┐ │ ││用户请求 │ │ │└─────────────────────────────────────────────────────────┘ │ │↓ │ │┌─────────────────────────────────────────────────────────┐ │ ││召回层 │ │ ││协同过滤 → 向量召回 → 热门召回 → 规则召回 │ │ ││ ↓ │ │ ││召回1000个候选商品 │ │ │└─────────────────────────────────────────────────────────┘ │ │↓ │ │┌─────────────────────────────────────────────────────────┐ │ ││排序层 │ │ ││粗排 → 精排 → 重排 │ │ ││ ↓ ↓ ↓ │ │ ││500 100 20 │ │ │└─────────────────────────────────────────────────────────┘ │ │↓ │ │┌─────────────────────────────────────────────────────────┐ │ ││输出层 │ │ ││去重 → 过滤 → 打乱 → 返回 │ │ │└─────────────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────────┘ ``` 当用户发起请求后,首先进入召回层。召回层的核心目标是“查全”,也就是从海量商品库中快速找出用户可能感兴趣的一批候选商品,例如1000个。接着进入排序层,重点解决“查准”问题——粗排使用轻量模型快速筛选,精排利用更复杂的模型做精准打分,重排则进一步加入多样性、去重、业务规则等策略,最后输出大约20个推荐结果。输出层则负责最终的结果整理、过滤与返回。 ### 召回层设计 “多路召回”是当前推荐系统架构中最常见、也最实用的方案。不同召回策略并行执行,各自返回一部分候选商品,最后统一合并。这样做的优势是能够从多个维度覆盖用户兴趣,提高召回覆盖率。 ```ja va @Service public class RecallService { @Autowired private CollaborativeFilteringRecall cfRecall; @Autowired private VectorRecall vectorRecall; @Autowired private HotRecall hotRecall; public List recall(Long userId, int size) { List>> futures = new ArrayList<>(); // 1. 协同过滤召回 futures.add(CompletableFuture.supplyAsync(() -> cfRecall.recall(userId, 200))); // 2. 向量召回 futures.add(CompletableFuture.supplyAsync(() -> vectorRecall.recall(userId, 300))); // 3. 热门召回(兜底) futures.add(CompletableFuture.supplyAsync(() -> hotRecall.recall(userId, 500))); // 4. 规则召回(新品) futures.add(CompletableFuture.supplyAsync(() -> ruleRecall.recall(userId, 100))); // 5. 合并结果 Set productIds = new LinkedHashSet<>(); for (CompletableFuture> future : futures) { try { productIds.addAll(future.get(100, TimeUnit.MILLISECONDS)); } catch (Exception e) { log.warn("召回失败", e); } } return new ArrayList<>(productIds).subList(0, Math.min(size, productIds.size())); } } ``` 这段代码比较直观地展示了多路召回的典型实现方式:并行执行、超时容错、统一合并。协同过滤负责挖掘个性化兴趣,向量召回负责语义相似性匹配,热门召回承担兜底作用,规则召回则可以满足新品曝光或运营干预等需求。 ### 排序层设计 完成召回之后,下一步就是排序。排序层通常会使用更复杂的推荐模型,对候选商品做精细化打分和排序。 ```ja va @Service public class RankService { @Autowired private RankModelService rankModelService; public List rank(Long userId, List productIds) { // 1. 获取特征 List features = featureService.getFeatures(userId, productIds); // 2. 模型预测 List rankedProducts = new ArrayList<>(); for (ProductFeatures feature : features) { float score = rankModelService.predict(feature); rankedProducts.add(new RankedProduct(feature.getProductId(), score)); } // 3. 排序 rankedProducts.sort((a, b) -> Float.compare(b.getScore(), a.getScore())); return rankedProducts; } } ``` 排序层的核心在于特征工程与模型预测。特征越全面,模型能力越强,推荐排序通常也越精准。但同时要注意性能问题:排序阶段往往需要对数百个候选商品逐个计算分数,因此接口延迟必须被严格控制,通常要在几十毫秒内完成。 ## 推荐算法详解 ### 协同过滤 协同过滤是推荐算法中最经典的方案之一。这里重点看一下基于用户的协同过滤实现思路。 ```ja va @Service public class UserCFRecall { public double userSimilarity(Long userA, Long userB) { Set itemsA = getUserItems(userA); Set itemsB = getUserItems(userB); // Jaccard相似度 Set intersection = new HashSet<>(itemsA); intersection.retainAll(itemsB); Set union = new HashSet<>(itemsA); union.addAll(itemsB); return (double) intersection.size() / union.size(); } public List findSimilarUsers(Long userId, int topK) { Map similarities = new HashMap<>(); for (Long otherUserId : getAllUsers()) { if (!otherUserId.equals(userId)) { similarities.put(otherUserId, userSimilarity(userId, otherUserId)); } } return similarities.entrySet().stream() .sorted(Map.Entry.comparingByValue().reversed()) .limit(topK) .map(Map.Entry::getKey) .collect(Collectors.toList()); } public List recommend(Long userId, int size) { List similarUsers = findSimilarUsers(userId, 50); Set userItems = getUserItems(userId); Set recommendItems = new HashSet<>(); for (Long similarUser : similarUsers) { Set items = getUserItems(similarUser); items.removeAll(userItems); recommendItems.addAll(items); } return new ArrayList<>(recommendItems).subList(0, Math.min(size, recommendItems.size())); } } ``` 这个实现思路比较清晰:先使用Jaccard相似度计算用户之间的相似程度,再找出Top 50相似用户,最后把这些相似用户买过、但当前用户还没有买过的商品作为推荐结果。虽然实现不复杂,但在行为数据较丰富的场景下,协同过滤的效果依然很有竞争力。 ### 向量召回 向量召回已经成为当前推荐系统中最主流的召回方式之一。它的本质,是为每个用户和商品生成向量表示,再通过向量检索快速找到最相似的商品集合。 ```ja va @Service public class VectorRecallService { @Autowired private VectorStore vectorStore; // Faiss/Milvus public List recall(Long userId, int size) { float[] userVector = getUserVector(userId); List results = vectorStore.search( userVector, size, "product_index"); return results.stream() .map(SearchResult::getId) .collect(Collectors.toList()); } private float[] getUserVector(Long userId) { // 方案1:从缓存获取 float[] vector = userVectorCache.get(userId); if (vector != null) { return vector; } // 方案2:实时计算 List beha viors = getUserBeha viors(userId); vector = userModel.predict(beha viors); // 缓存 userVectorCache.put(userId, vector); return vector; } } ``` 向量召回的关键点在于:如何生成高质量的用户向量和商品向量。通常需要借助深度学习模型,根据用户历史行为序列、商品内容特征等信息进行编码。向量存储与检索层则通常依赖Faiss、Milvus等高性能ANN近似最近邻检索引擎。 ## 实时推荐 推荐系统不能只依赖离线计算,实时推荐能力往往决定了最终体验上限。 ### 实时特征更新 用户行为是持续实时发生的,比如点击、加购、收藏、购买等,这些信号最好能在秒级内反馈到推荐结果中。 ```ja va @Service public class UserBeha viorProcessor { @Autowired private FeatureStore featureStore; @KafkaListener(topics = "user-beha vior") public void onMessage(UserBeha viorEvent event) { RealTimeFeatures features = featureStore.get(event.getUserId()); switch (event.getType()) { case CLICK: features.addClick(event.getProductId(), event.getTimestamp()); break; case PURCHASE: features.addPurchase(event.getProductId(), event.getAmount()); break; case CART: features.addCart(event.getProductId()); break; } featureStore.put(event.getUserId(), features); // 如果用户正在浏览推荐页,触发重排 if (isUserBrowsingRecommend(event.getUserId())) { recommendService.refreshRecommend(event.getUserId()); } } } ``` 通过Kafka消费用户行为事件流,系统可以实时更新用户特征,并在必要时主动刷新推荐结果。这类实时特征更新机制,是构建实时推荐系统的重要基础设施。 ### 实时推荐API 一个典型的推荐接口,通常需要串联召回、排序和重排等多个核心环节。 ```ja va @RestController @RequestMapping("/api/recommend") public class RecommendController { @Autowired private RecommendService recommendService; @GetMapping("/home") public Result> homeRecommend( @RequestHeader("X-User-Id") Long userId, @RequestParam(defaultValue = "20") int size) { UserFeatures features = featureService.getFeatures(userId); List productIds = recallService.recall(userId, 1000); List ranked = rankService.rank(userId, productIds); List products = rerankService.rerank(userId, ranked, size); return Result.success(products); } @GetMapping("/related/{productId}") public Result> relatedRecommend( @PathVariable Long productId, @RequestParam(defaultValue = "20") int size) { List products = recommendService.getRelatedProducts(productId, size); return Result.success(products); } } ``` 首页个性化推荐和商品详情页相关推荐,是推荐系统最常见的两类业务场景。虽然整体流程都包括召回、排序和重排,但两者在召回策略、特征设计和排序模型上通常会有所区别。 ## 踩坑实录 推荐系统在工程落地过程中,往往比算法本身更容易踩坑。下面分享几个最常见的问题。 ### 坑1:冷启动问题 **问题**:新用户和新商品缺少行为数据,系统难以给出有效推荐。 **解决方案**: ```ja va // 新用户冷启动 public List coldStartUserRecommend(Long userId) { // 1. 基于注册信息推荐 UserInfo user = userService.getUser(userId); List products = productMapper.selectByCategory(user.getInterestCategory()); // 2. 热门推荐兜底 if (products.size() < 20) { products.addAll(hotRecall.recall(userId, 20 - products.size())); } return products; } // 新商品冷启动 public void handleNewProduct(Product product) { // 1. 计算商品向量(基于类目、品牌、标题) float[] vector = productEncoder.encode(product); // 2. 找相似商品 List similarProducts = vectorStore.search(vector, 10); // 3. 推荐给相似商品的购买用户 for (Long similarProductId : similarProducts) { List users = getBuyers(similarProductId); recommendNewProductToUsers(users, product.getId()); } } ``` 冷启动的核心思路是:先利用注册信息、热门商品、商品属性等非行为数据做基础推荐,等用户或商品逐渐积累起足够的行为数据后,再切换到协同过滤、向量召回或深度学习模型等更精准的推荐策略。 ### 坑2:数据稀疏问题 **问题**:用户行为数据过于稀疏,导致协同过滤推荐效果不稳定。 **解决方案**:混合推荐 ```ja va public List hybridRecommend(Long userId, int size) { double cfWeight = 0.4; // 协同过滤 double vectorWeight = 0.3; // 向量召回 double hotWeight = 0.2; // 热门推荐 double ruleWeight = 0.1; // 规则推荐 Map scores = new HashMap<>(); // 加权合并 for (Long productId : cfRecall.recall(userId, 200)) { scores.merge(productId, cfWeight, Double::sum); } for (Long productId : vectorRecall.recall(userId, 200)) { scores.merge(productId, vectorWeight, Double::sum); } // ... 其他路 return scores.entrySet().stream() .sorted(Map.Entry.comparingByValue().reversed()) .limit(size) .map(Map.Entry::getKey) .collect(Collectors.toList()); } ``` 混合推荐并不是把多个策略简单拼接,而是通过合理的权重设计,让不同推荐算法彼此补充。协同过滤擅长挖掘个性化兴趣,向量召回更偏向内容与语义相似,热门推荐提供稳定兜底,规则推荐则适合满足运营和业务控制需求。 ### 坑3:实时性要求高 **问题**:用户行为一旦变化,推荐结果就需要尽快同步更新。 **解决方案**:流式处理 ```ja va // Flink实时处理用户行为 DataStream beha viorStream = env .addSource(new KafkaSource("user-beha vior")) .keyBy(UserBeha vior::getUserId) .process(new ProcessFunction() { @Override public void processElement(UserBeha vior beha vior, Context ctx, Collector out) { // 更新用户特征 userFeatureStore.update(beha vior); // 触发推荐刷新 out.collect(new RecommendUpdate(beha vior.getUserId())); } }); ``` 使用Flink等流式计算框架处理用户行为数据,可以实现秒级甚至更低延迟的特征更新和推荐刷新能力。不过,这也意味着系统需要投入更多基础设施建设成本与运维成本。 ### 坑4:推荐多样性差 **问题**:推荐结果过度集中在少数类目,用户容易产生审美疲劳,体验下降。 **解决方案**:重排多样性控制 ```ja va public List diversityRerank(List products, int size) { Map categoryCount = new HashMap<>(); List result = new ArrayList<>(); for (RankedProduct rp : products) { String category = rp.getProduct().getCategory(); int count = categoryCount.getOrDefault(category, 0); if (count < size / 3) { result.add(rp.getProduct()); categoryCount.put(category, count + 1); } if (result.size() >= size) break; } return result; } ``` 多样性控制的核心,是在原始排序结果的基础上增加约束,例如限制单一类目展示数量,避免推荐结果过于同质化。虽然这样做可能会牺牲一部分短期点击率,但通常能显著提升用户长期体验与平台内容生态健康度。 ## 总结 推荐系统核心要点: | 阶段 | 方法 | 目的 | |------|------|------| | 召回 | 多路召回 | 查全率 | | 排序 | 精排模型 | 查准率 | | 重排 | 多样性控制 | 用户体验 | **血的教训:** 推荐系统本质上是平台的流量分发器。好的推荐系统,能够精准连接用户需求与商品供给,显著提升点击率、转化率和用户留存;差的推荐系统,则可能让用户陷入信息茧房,甚至导致体验持续下滑。做推荐系统架构与推荐算法,技术当然重要,但真正决定效果上限的,始终是对用户和业务的深度理解。 *个人观点,仅供参考*
来源:https://cloud.tencent.com.cn/developer/article/2718682
上一篇KubeCon 2019现场亮点回顾:开发者必读指南 下一篇AI表格导入Excel数值变化原因及类型推断与Power Query验证
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
CAD零基础入门教程:坐标输入、图层管理与基础绘图命令
AI教程 · 2026-09-01

CAD零基础入门教程:坐标输入、图层管理与基础绘图命令

本文面向CAD零基础学习者,系统讲解坐标输入、图层管理与基础绘图命令的核心用法。通过分步实操与常见问题排查,帮助新手建立精确绘图习惯,掌握规范出图的基础能力。

CAD从入门到项目交付:绘图、标注、图块与实战工作流
AI教程 · 2026-09-01

CAD从入门到项目交付:绘图、标注、图块与实战工作流

掌握CAD的核心在于建立“画得准、标得清、复用快、交付稳”的工作流。本文提供从环境设置、高频命令组合、标注规范、图块标准化到项目分阶段交付的完整路径,帮助初学者避免常见返工陷阱,独立完成可检查、可复用、可打印的工程图纸。

Claude Code 登录指南:个人、Teams 与企业账号区分与授权步骤
AI教程 · 2026-09-01

Claude Code 登录指南:个人、Teams 与企业账号区分与授权步骤

本文详细解析 Claude Code 登录前的账号类型区分方法,涵盖个人订阅、Teams 席位与企业 Enterprise 席位的授权路径差异。提供终端登录命令、环境变量排查及常见异常处理步骤,帮助用户快速完成正确授权并避免登录路径混淆。

Claude Code 文件修改前的权限模式配置与命令审批指南
AI教程 · 2026-09-01

Claude Code 文件修改前的权限模式配置与命令审批指南

本文详细介绍Claude Code在修改文件前的权限模式配置方法,包括defaultMode可选值、permissions allow与deny规则设置、多层级配置文件管理以及 status验证技巧,帮助开发者安全高效地使用AI编程助手。

Claude Code接入VS Code后先测扩展和终端命令
AI教程 · 2026-09-01

Claude Code接入VS Code后先测扩展和终端命令

在VS Code中接入Claude Code后,建议优先验证扩展面板与集成终端两条入口。本文提供标准检查顺序、关键命令与常见故障排查路径,帮助你快速确认环境就绪,避免后续开发受阻。