首页 / 资讯中心 / 文章详情

实时推荐系统架构拆解:从实时特征到增量训练的完整链路

实时推荐系统架构拆解:从实时特征到增量训练的完整链路 ★ FEATURED ARTICLE
最近组里在折腾推荐链路把实时推荐这一块的论文和技术方案翻了不少。有一篇论文让我印象很深讲的是某家头部互联网公司自研的一套实时推荐系统。这套系统的核心不是某个特别复杂的模型而是把实时链路从行为采集、特征计算、召回、排序到样本回流完整地串了起来工程味道非常重。很多做推荐的同学应该都读过类似的论文但真正能在自己的业务里复刻出一整套实时链路的其实不多。下文为了方便叙述我用 RRec 代称这套系统聊一聊它解决什么问题、架构是怎么拆的以及我照着类似思路落地时踩过的坑。如果你正在做信息流、短视频、电商或者直播这类对实时反馈比较敏感的业务这篇文章应该能给你一些可落地的参考。就算你目前只是做离线推荐看完也会明白为什么实时推荐不是简单加一个“实时特征”就完事。1. 实时推荐系统到底在解决什么问题1.1 离线推荐为什么不够用很多推荐系统一开始都是离线链路白天跑数凌晨出结果第二天用户打开 App 看到的推荐列表就是头一天算好的。这套方案在用户兴趣相对稳定的场景下问题不大但在短视频、新闻、直播这类场景里用户的兴趣变化非常快。举个很典型的例子一个用户白天可能一直在刷职场类内容晚上临睡前随手点了一个萌宠视频又连着看了好几个类似视频。这时候如果系统还在用“昨天的兴趣画像”做推荐下一刷大概率还是职场内容用户可能直接划走。但如果有实时链路系统能立刻感知到“他刚看了一组萌宠视频”并在下一个请求里把候选集往这个方向倾斜。把离线推荐想成是昨天熬夜写好的菜单用户今天想吃什么你只能靠猜实时推荐是你站在用户旁边看到他刚尝了一口菜皱了下眉马上把下一道菜换掉。前者在捕获长期兴趣后者在捕捉即时意图两者缺一不可。1.2 实时推荐与在线学习的边界一个常见误区是实时推荐等于在线训练模型。但实际拆开看实时链路至少包含五个环节行为采集、特征计算、候选召回、模型排序、样本回流。真正需要“每一分钟都在更新模型参数”的只是最后一个环节的一部分工作。RRec 给我的最大启发是它没有要求所有环节都做到毫秒级而是把“实时”拆成不同粒度。比如用户最近一次点击行为要秒级生效用户长期行为画像可以分钟级更新item 侧的分类统计甚至可以小时级更新。只要端到端延迟可控每一环都不必追求极致。这个设计思路非常实用。因为真正把系统全部推到毫秒级代价极高存储要换、计算引擎要换、运维复杂度也指数上升。大多数业务并不需要那么极致需要的只是“用户上一个动作能影响下一个请求的推荐结果”。1.3 系统设计上的三个关键决策RRec 这类系统在设计上通常会做三个关键决策理解这三个决策基本就理解了整套实时推荐系统的骨架。第一个是端到端实时而不是单点实时。如果只是让用户行为实时入库但推荐结果还是每天算一次那毫无意义。实时推荐需要的是一个闭环行为实时进入、特征实时更新、候选实时生成、结果实时返回、样本实时回流。第二个是近线更新而不是纯在线更新。纯在线学习对工程要求非常高参数更新频繁模型行为不稳定出了问题也很难追查。RRec 的做法更接近近线更新每 5 到 10 分钟增量训练一次加载全量模型作为初始参数用最近新增的样本做增量微调。这样既能捕捉最新兴趣变化又能保证模型稳定性。第三个是存储分层。全量特征和历史行为放在离线数仓在线服务只依赖高并发读取的实时特征缓存和近线更新的索引。不会出现“一个表同时服务训练和线上推理”这种互相拖累的情况。2. 核心架构拆解从数据到结果的完整链路2.1 行为采集与实时特征计算实时推荐的第一步是把曝光、点击、播放、收藏、分享等行为日志以尽可能低的延迟送到计算引擎。行为日志通常会上报到消息队列再用流计算引擎做窗口聚合。在实际操作里最容易出问题的不是“数据有没有到”而是时间对齐。行为日志必须按事件发生时间做窗口计算不能按服务收到日志的时间计算。否则流量高峰时日志积压窗口统计就会莫名偏掉看起来像是用户兴趣突变其实是数据延迟导致的时间错位。实时特征主要分三类滑动窗口统计特征最近 5 分钟点击某类目次数、最近 1 小时播放时长等。行为序列特征用户最近点击的 item id 序列、类目序列、作者序列。实时上下文特征当前时间、星期几、是否节假日、当前城市等。RRec 在论文里强调过一个点离线训练用的特征和在线推理用的特征必须同源同构。也就是说训练时用的“最近 5 分钟点击次数”和线上实时算出来的“最近 5 分钟点击次数”必须是一个定义。最理想的做法是同一套特征配置同时给离线和实时两套引擎使用而不是离线一套代码、实时另一套代码。2.2 多路召回如何做到实时推荐系统通常不会只靠一路召回而是多路并行热门召回、协同过滤召回、向量召回、语义召回、同城召回等。实时推荐把这里变成了最容易出问题的地方。比如向量召回用户 embedding 需要根据他的最新行为实时变化不可能每天算一次。RRec 的做法是把用户侧向量放到实时计算链路里行为一产生就更新在线用户向量。而 item 侧向量相对稳定可以近线更新周期性重建向量索引。在这个环节里关键不是模型而是索引。线上请求进来之后系统需要在毫秒级从几十万甚至几亿个 item 里找回和用户最相关的几百个候选。大多数场景用的是近似最近邻检索比如 HNSW 这类索引结构。实时推荐要求索引支持增量更新不然新产生的高热 item 无法立刻进入召回候选。一个让我印象很深的细节是RRec 会把召回结果分层缓存。第一层是本用户最近几分钟的召回结果第二层是用户实时向量的召回结果第三层是兜底热门召回。这样既不影响召回精度又能避免把实时计算压力全部压到每次请求上。2.3 排序层特征拼接与模型推理性能召回之后是排序排序层往往是整个链路里计算压力最大的地方。因为每一个候选 item 都要拼上用户特征、item 特征、交叉特征然后过一遍模型打分。如果有几百个候选一个请求就要做几百次特征拼接和模型推理。RRec 在排序层的优化思路也很典型能缓存就缓存能减少特征就减少特征。用户特征变化频率低可以放在本地缓存item 特征变化频率略高可以放在远程特征服务里并做成批量拉取而不是一个一个查。模型推理一般会做这几类优化请求级别 batch 推理把一批候选 item 合并成一个 batch 送进模型避免单条推理的开销。算子融合和 int8 量化减少显存占用降低单次推理延迟。超时保护设置 P99 延迟目标超过阈值直接返回上一个可用结果不阻塞请求。排序阶段宁可效果略差也不能因为超时而拖垮接口。实时推荐的排序目标通常比离线推荐更激进因为用户已经产生了明确的新行为系统应该把这个信号尽快用上。2.4 结果闭环与实验回收推荐系统返回结果不是终点。用户看到推荐列表之后点没点、看了多久、有没有划走这些信息都必须回到数据链路里。RRec 的闭环核心在于“实时样本生成”和“效果回收”。很多团队做实时推荐做到排序就结束了样本回流还是走离线 T1。这会带来一个尴尬实时链路把用户最新行为用于推荐但模型训练还在用昨天的数据。用户的实时行为没有被模型学习到效果自然打折。所以闭环一定要打通。曝光和点击日志要实时进入样本拼接模块拼出训练样本再送到增量训练模块。如果这一步没有做好前面的实时召回和实时排序就只能起到“特征级实时”的作用模型本身还是滞后的。做 AB 实验时也要特别注意实时链路引入的变量很多不能只和旧版本对比点击率。至少要保留一个稳定版本做对照否则实时模型一抖动实验组效果忽高忽低很难判断到底是模型问题还是链路问题。3. 关键参数与落地实操参考3.1 时间窗口与特征时效性的权衡实时特征最重要的参数就是时间窗口。窗口太短特征稀疏一个行为就能导致特征剧烈波动窗口太长实时性变差和离线推荐没什么区别。我在实际项目里一般会按业务行为密度来定窗口类型推荐时长适用场景风险超短窗口1 到 5 分钟直播、短时热点特征波动大易抖中窗口10 到 30 分钟短视频、资讯流平衡较好长窗口1 到 24 小时电商、长决策内容响应慢接近离线除了滑动时间窗口还可以给行为序列加时间衰减权重。简单方案是w_i exp(-α * (t_now - t_i))其中 t_i 是第 i 个行为的发生时间α 控制衰减速度。α 越大越老的行为权重掉得越快。短视频场景 α 可以设大一些电商场景则要小一些因为用户购物的决策周期更长。有个坑是窗口切换时特征会跳变。比如从“最近 5 分钟”切到“最近 10 分钟”原本被截断的旧行为大量进入窗口特征值突然变大。这种跳变会影响排序模型的稳定输出建议用平滑计数器或者多重时间窗口叠加而不是简单硬切。3.2 实时样本生成与延迟反馈处理实时推荐上线后一个最容易踩的坑是样本标签怎么打。用户曝光了一条内容很快划走了是不是就一定是负样本不一定。用户可能看到了但还没决定点也可能点了之后又看了同类的其他内容。最粗暴的做法是曝光后 5 分钟没点击直接标记为负样本。这样做的问题在于很多点击发生在 10 分钟甚至 30 分钟之后。过早打负样本会让模型学到错误信号。比较稳妥的做法是设置一个延迟反馈窗口# 伪代码延迟反馈样本生成 def generate_sample(exposure_ts, click_ts, wait_window): if click_ts is None: # 超时未点击判定为负样本 return negative_sample() delay click_ts - exposure_ts if delay wait_window: # 在窗口内点击正样本 return positive_sample() # 延迟点击更新样本 return positive_sample_with_delay(delay)实际中还会遇到一个更麻烦的问题曝光样本先被标负然后用户在窗口外点击了。如果不能回填修正模型就会出现“负样本里其实有正样本”的脏数据。所以在 RRec 的思路里点击日志要持续回流一段时间把之前标记为负的样本修正回来。等待窗口设多长可以通过线上点击延迟分布来确定。一般是取 P50 或 P90 的点击延迟时间我这边常用的初始值是 15 到 30 分钟再根据数据分布动态调整。3.3 模型更新频率与全量/增量训练配合模型更新频率是整个实时链路里最不需要“拍脑袋设得很高”的参数。很多同学一听到实时推荐就觉得模型最好每秒更新。但实际上高频率更新会带来稳定性问题。RRec 的常见做法是“全量 增量”配合全量模型每天凌晨训练提供稳定基线增量模型每 10 到 30 分钟训练一次使用最近新增样本做微调。增量训练不是从零开始而是加载全量模型作为初始权重再用小学习率更新。增量训练的常见参数配置学习率全量训练的 1/10 到 1/5。训练数据量最近 30 到 60 分钟样本。冻结底层特征层只更新上层全连接层。更新后的模型做线上 shadow 验证再切成主链路。为什么不能盲目提高更新频率因为样本量不够的时候模型参数会被少数热点行为带偏。比如某个突发事件让一类内容被大量点击增量模型可能把这一类内容的评分整体抬高。等热度消退以后模型还没恢复过来推荐结果就会持续偏向已过时的内容。4. 踩坑记录与排查技巧4.1 特征穿越比想象中更容易发生特征穿越也叫特征泄漏是实时推荐里最隐蔽的问题。表现是离线训练时模型效果特别好线上效果却很差。原因往往是训练样本里用了“未来信息”。举个例子训练样本是用户点击了某条视频特征里包含“该视频是否被收藏”。这个收藏行为如果发生在点击之后而离线训练时我们直接用整张表最新状态去拼特征就等于把未来信息喂给了模型。线上推理时这个收藏还没发生模型自然拿不到这个特徵效果就会崩。排查特征穿越不能只靠看代码更有效的办法是检查特征时间戳和样本时间戳。我在排查时一般会写一个统计任务计算特征更新时间减去样本生成时间的差值差值为正的比例越高穿越越严重。修复方法也很直接拼特征时强制要求特征时间戳不晚于样本生成时间。4.2 延迟反馈导致样本分布偏移实时推荐刚开始上线时点击率预估往往会偏低。原因很简单大量曝光样本还没到点击延迟窗口就被标成负样本了。模型看到的正负比例严重失真自然会把点击概率预测得很低。解决这个问题需要结合延迟反馈但还有一个额外技巧负样本降采样或加权。如果延迟窗口内大部分负样本是“假负”可以对负样本做降采样或者按延迟时间给负样本降低权重。RRec 的经验是不能只靠算法兜底最好在样本生成阶段就把标签置信度记录下来比如标记为“强负样本”和“弱负样本”模型训练时区别对待。4.3 缓存穿透与结果趋同为了低延迟实时推荐服务一定会大量使用缓存。但如果缓存设计不好会出现两个典型问题。第一个是缓存穿透。用户第一次请求缓存里没有数据只能现算导致响应时间特别长。高并发下一旦穿透服务容易被打垮。解决方法是热点用户预热、空结果缓存和布隆过滤器兜底。第二个问题更隐蔽结果趋同。热门 item 往往会被主动缓存多路召回都优先把高热度内容返回结果大量用户看到的是同一个热门榜单实时推荐就名存实亡了。要解决这个问题需要给召回结果增加多样性约束并且每个用户的实时推荐结果要有独立的用户行为前缀作为缓存 key避免互相命中。4.4 监控指标体系延迟、新鲜度、覆盖率实时推荐链路比离线链路复杂得多没有一套完整的监控体系出了问题只能干瞪眼。我建议至少盯住这几类指标指标含义参考报警线端到端延迟行为发生到推荐结果生效的时间超过 5 分钟报警特征新鲜度用户最新行为进入特征缓存的时间差超过 10 分钟报警召回覆盖率多少用户请求能被实时召回覆盖低于 5% 报警模型更新延迟增量模型参数发布时间差超过 30 分钟报警样本回填率延迟点击回填比例偏低说明等待窗口短这些指标不是独立看的要结合业务一起看。比如端到端延迟很低但特征新鲜度很高说明数据虽然传得快但特征计算或者缓存更新慢了链路仍然不实时。5. 在真实业务里的落地思路5.1 从单点实时逐步演进出完整闭环不少人看到 RRec 这类系统第一反应是要把整套架构复制下来。但我个人强烈建议小团队或者业务初期不要一上来就做全链路实时推荐。工程投入太大一旦效果没起色非常难收场。更稳妥的路线是先把“行为实时接入”做起来让用户的最近点击能实时体现在召回和特征里。这一步单独上线通常就能带来明显的效果提升因为信息流场景里用户最近几轮的交互信号本身就非常强。稳定之后再补“模型增量更新”和“样本实时回流”这两块。增量训练对工程稳定性要求高上线前一定要做好回滚方案。如果模型回滚不及时一个劣化版本可能把线上流量带崩。5.2 小团队的建议配置如果你手上的业务已经跑了一段时间推荐结果对实时性开始有要求可以参考下面这个最小配置行为日志通过消息队列实时接入保留原始行为明细。用流计算引擎计算用户最近 N 个行为、最近 M 分钟类目统计等实时特征。召回层增加一路“用户实时行为相似召回”召回用户刚看过内容的相似 item。排序层暂不追求模型实时更新先确保实时特征能参与模型打分。等到基线稳定后再引入增量训练模块用回流样本做微调。我在实际项目里发现很多时候效果提升最大的不是模型而是“用户上一轮的点选行为能够立刻出现在下一轮推荐里”。这一步做扎实了实时推荐的价值就已经体现了一大半。之后再上模型更新更多是锦上添花。踩过几次坑之后我的体会是实时推荐系统更像一个工程问题不是模型问题。你可以有很多先进的模型但链路里任何一环延迟高了模型再强也体现不出来。反过来说只要把链路理顺哪怕模型不算很新体验提升也会非常明显。RRec 这类论文的价值就在于它把整个链路讲清楚了照着这个思路在自己的业务里逐步落地比单纯追求新模型要有用得多。
阅读完成 · 觉得有帮助?
咨询建站