把推理预算花在还有收益的地方
把推理预算花在还有收益的地方
一个排序服务要在很短的时间内决定先看哪些候选,再用什么方法评估它们,最后返回有限的结果。当模型变大、候选变多,工程团队很容易把问题简化为两道选择题:是不是该换更快的机器,是不是该给每次请求用更便宜的模型。还有第三道题:即使现有预算与机器都不变,能不能把一部分计算从追加收益较低的地方,挪到还有较大改善空间的地方?这个问题看似像资源调度,真正做起来却要求系统知道什么叫改善,并理解一次局部调整会怎样改变别处的负载、质量和延迟。
公开原文:Intelligent Delivery: Making Every Unit of Compute Serve Better Ads。原文发表于十一月十一日,本次精读选题日期为十一月十四日。
原文提供了什么证据
Meta 的工程文章介绍了一个广告排序服务如何分配线上算力。文章指出,不同请求对额外计算的反应不同,同一请求在不同排序阶段、硬件或负载水平下也可能得到不同回报。系统用共享模型提取请求特征,为多种资源设置预测结果、计算成本和延迟,再由不同层的控制机制做决定。区域之间重新分配流量,工作负载匹配不同机器,机器内部调整单请求的处理预算。工程团队还借助能够阅读实验记录和设计材料的智能体寻找改进机会,最终生产变更由人审核。
文中称,从二零一七年到二零二六年上半年,这套方法在主要场景为排序表现贡献了约一成的额外改善,另有离线实验衡量识别高收益请求的能力。两项数字衡量不同对象:一个是系统在长期迭代里的排序收益,一个是预测器在特定离线评价中的进步。原文未公开让外部团队复算全量结果的实验数据,也没有说节省了同等比例的硬件开支。这里可以提炼控制方法和实验纪律,不能据此为另外一种产品承诺收益,更不能把排序收益解释成用户满意度或长期营收的等量变化。智能体参与优化的比例同样不代表智能体独立上线变更的比例,也无法单独说明其投入产出。
下面讨论的是从这个案例出发的独立工程分析。文中的假设场景用于说明设计和验证方法,不对应任何已运行服务,也不提供虚构的测量结果。
从平均成本转向边际收益
系统通常知道一批请求平均用了多少算力,却不一定知道最后那一小段算力买到了什么。对已经找到明确匹配项的查询,多评估一批候选可能只是反复确认同一个答案;对几个结果难分高下的查询,额外的重排可能改变首位结果。不过,难处理并不等于值得多花:当查询缺少关键约束,或索引里根本没有对应对象时,再精密的评分也补不出缺失的材料。要预测的是提高预算会带来什么变化,而非用一个笼统的难度分数描述当前请求。
可以把一次预算调整当成有成本的小实验。先约定同一目标下的两档动作,例如取较小或较大的候选集合,然后问:在当前条件下,后一档比前一档平均改善多少?两档之间的成本差也应算清。新增一次计算可能占据加速器显存,使后续批次更难装满;跨区域多一次取数,可能改变网络费用和排队形态;更深的模型会增加某类请求超时的概率。把成本只写成浮点运算次数,系统就会把瓶颈转移到没有计价的地方。
有些成本能折合成统一的资源影子价格,这个价格可以随负载上升而提高。把同一台机器在空闲和排队时的成本视为相同,会诱使服务在高峰仍进行低收益扩展,也会让空闲期缺乏使用剩余容量的途径。在真正的系统里,资源还有粒度:空闲几毫秒的计算不总能拼出完整批次,分散的显存不等同于连续可用的显存。影子价格可以帮助比较两个动作,不能将物理上不可拼接的资源变成可以随意交换的额度。
哪怕只关心一项业务指标,也要定义不可被交易的约束。延迟上限、地域数据约束、最低服务质量和故障隔离属于这类边界。给它们一个成本权重看起来灵活,却可能在优化器找到足够高的预测收益时突破底线。实际调度应先枚举当前可行的动作,再比较它们的收益。小流量、低置信的请求尤其需要保留基本服务水位,否则策略会不断把预算分给已有较好数据的群体,同时让欠采样的群体越来越难提供可用训练标签。
假设案例:同样的搜索结果数,处理预算不同
设想一个公开文档搜索服务。用户输入精确的函数名时,词法检索常能得到足够明确的候选。用户描述错误现象时,匹配文档分散在多个版本,检索系统可能需要补充语义候选,再由重排模型比较版本与上下文。还有第三种请求:索引不包含用户问到的版本。系统无法在入口处直接知道正确答案,但能看到首轮候选之间的分差、版本匹配强弱、结果去重后有多少独立页面,以及请求还剩多少时间。
一种朴素的做法是为每个请求固定返回同样多的结果,处理路径也一样。它便于测量和复现,长期负载稳定时完全可能足够。自适应方案可以先以同一档预算产生粗排结果,再根据余下的时间和候选证据决定是否追加重排。大部分精确查询会走短路径,模糊查询并不自动获得扩展:如果召回的额外候选都是旧版本副本,增加计算可能只让错误更有信心。此时明确展示索引覆盖范围,可能比再排序一次更有用。即使入口能预测复杂度,仍应保留阶段性决策,因为有些信息要到粗排以后才能观察到。
假设这个系统选择在深夜增加重排实验,白天负载升高后允许主动退回。若只比较白天与深夜的点击差异,结论会混入不同用户、文档和查询任务。较好的试验是在相近时间、同类请求和相同版本候选上做小比例随机分配,同时控制升级动作的频率。对于用户可见的实验,既看首项相关性,也看完成任务所需的查询次数、空结果、超时或者主动改写查询的变化。不要只把点击当成真值:用户点击一个标题吸引人的结果,有可能只是为了发现它不适用。
再看一个更难的情形:服务用快速模型处理多数请求,偶尔用重模型作为对照。两个模型的速度不仅取决于各自的计算量,还受到共用机器上的批次和缓存影响。如果把对照流量塞入原有队列,它可能影响基线流量,破坏随机化的独立性。团队可以先在隔离资源池验证行为,再以小流量检查共享状态下的真实相互作用;比较时同时记录各池负载和尾部延迟。隔离实验与生产试验回答不同问题,二者的数据不能互相替代。
用反事实校准决策,避免策略把偏差写回训练集
现有系统一般只记录先前策略所选动作的结果。一个请求走过短路径,历史记录只能告诉我们短路径发生了什么,无法告诉我们同一时刻如果多做一步会怎样。拿高预算组和低预算组的历史结果训练,模型容易把请求构成的差异当成算力的效果:高预算组本来就较难,预测器可能因此认为高预算有害;反过来,若只有高价值请求能进高预算组,也可能夸大收益。
反事实估计要有可比较的样本。有条件时,可以在严格预算约束下为一小批合格请求随机选择动作,并保存抽样概率、决策时的特征及排队状态。这样以后有人回看实验,不会把一次临时降级错误归因为预测模型的偏好。若完全无法随机,至少应说明比较依赖的假设,并用分层统计、不同时间段的复测以及人工审阅来限定说法。对未观察过的动作组合,最保守的归宿是标记为未知,交给有边界的试验,而不是让一个输出很整齐的模型填补真实证据的缺口。
用于训练的信号也应该分时间尺度。即时点击能快速反馈,但可能反映位置偏差;任务完成率更贴近结果,日志却未必能完整观测;事后质量评估可以区分某些系统性错误,但覆盖较少、判定滞后。可以把调度目标明确限制在一组近期可验证的指标,同时把长期指标作为扩大上线的条件。若短期指标上升而人工评估下降,要先判定是抽样差异、策略改变了可见对象,还是代理指标真的被优化坏了。
新模型或新硬件上线后,原有预测也会漂移。一个旧预测器可能仍然准确识别大多数请求,却错误排列最稀缺时刻的优先级。与其只考核平均误差,不如在配额即将用满时专看排序前沿:被升档的请求是否真的比被降档的请求受益更多?如果对这个问题没有答案,可以先运行只观察不执行的候选策略,再将它与固定预算和简单阈值两种基线同时对照。复杂模型要赢过能够长期维护的对手,才有资格接管热路径。
多层调度要有明确的边界
区域之间挪流量,机器内部削减低收益计算,看起来各自合理,叠加起来却会形成反馈。假设甲区发现排队升高,把流量发往乙区;乙区因突发量增加把每个请求的候选预算降到最低。此时如果只看吞吐,乙区似乎处理了更多工作;如果用户看到了更差的结果,重试还可能制造额外流量。区域控制器随后再次移动负载,造成两个地区相互推送压力。局部控制器纪录的每一步都可能符合自己的指标,整体体验仍然变差。
可把调度拆成几个速度不同且责任清楚的回路。请求内控制在明确的剩余时间内选择可行的复杂度;机器控制限住排队时间和并发量,并给出可用额度;区域层只在观测到稳定的负载差异且目的地有余量时转移流量。上线前要测试动作边界:上游决策失效时下游是沿用旧配额、回到固定预算,还是拒绝新增高成本任务?跨区开关能否独立回退?区域负载缓慢变化与同一时刻大规模失联是否共用一条响应规则?如果没有明确答案,控制机制可能在演练还未结束时就覆盖故障的原始因果关系。
看起来有一点浪费的冗余容量也应单独核算。日常完全吃满容量能够提高账面利用率,但限流、节点故障和故障转移都需要缓冲。若日常优化消耗掉应急容量,事故时负载接手会变成新的事故。一种做法是让区域预算公开区分日常可调额度、试验额度和故障储备,只有在各自的条件满足时才动用。储备不是一成不变的数值:高风险部署窗口、跨区复制积压和已知依赖降级都可能需要扩大余量。把它从可普通竞价的资源池中隔离,至少可以避免在系统正常时被零星的预测高收益无声挤占。
延迟必须看端到端分布,不能把阶段分位点简单相加。不同阶段慢的请求可能重合,也可能是不同批;同一请求在地理转移后还会遭遇缓存冷却、跨地域取数或额外鉴权。每层都应该清楚自己用掉了多少总预算,还要让追踪工具把排队、计算和等待下游分开。那样,当尾部延迟突然上升时,有人能回答是模型变慢、调度改变了批次,还是流量到了容器虽空闲却没有对应缓存的地方。
异构硬件改变的,是可选动作的集合
给设备贴上快慢标签很自然,却容易误导。对于读大量索引的任务,主要限制可能是内存容量和访存效率;对于可以批处理的重模型,输入长度分布、批次填充率和显存比单样本峰值更有解释力;对于低延迟的小模型,在目的设备上排队等待一个完整批次反而可能超过直接使用通用处理器。真正要测量的是某类负载在指定设备和运行设置上的性能,并说明这个矩阵适用于怎样的请求分布和软件版本。
假设两个服务公用一个加速器池:一个服务的任务大小相近,可以稳定凑批,另一个任务长度跨度大且必须迅速返回。按单次测量值看,两者都适合加速器,于是调度把所有请求一股脑送入同一个池。混合流量使某些批次中的短任务等待最长的样本,还可能因为临时内存峰值减少可并发批数。给相近长度建立不同队列,或允许低延迟短任务在高峰转走,往往比再微调单位算力的成本系数更有意义。这里说的是可验证的机制,不等于所有框架都会表现相同,是否成立取决于推理引擎怎么填充和调度。
在设备迁移时,把不确定性当作约束往往比当作噪声好。新设备没有覆盖某些算子,或者依赖不成熟的编译路径,回退成本可能很高;性能矩阵里的未测试项应该独立标出。首次使用时应逐个检查正确性、数值差异、错误回退和集群生命周期,再扩大负载范围。当新旧硬件共存,基准测试还应包括混合部署如何处理老版本模型。最重要的不是找到每张卡的最佳演示参数,而是能够解释某项工作为何被分配到具体设备、何时升级这一规则,以及策略出错时谁会承担剩余流量。
智能体适合帮助寻找实验,生产决策仍要有明确责任
原文描述智能体读取代码、文档和历史实验,缩小人工需要检查的方案范围。这里值得借鉴的是工程笔记的可检索性,而不是把调整生产参数当作自然的下一步。失败实验往往比成功实验少写一层说明:某个开关被拒绝,究竟是因为效果为负、观测失真、依赖尚未准备好,还是生产迁移成本太高?没有记录,智能体会反复把这个方案当作新机会。实验库如果缺少原始指标含义、对照条件和停止原因,即便检索很准也只能复述半截结论。
可以让智能体输出可检查的实验提案。提案至少说明它依赖怎样的证据,预计哪条因果链发生变化,准备改变什么,怎样判断假设失败,以及谁有权结束测试。对于联动范围不清楚的开关,先要求列出已知读写路径,再由工程师或受约束的工具确认。运行权限则按阶段拆分,读取历史、提交实验方案、在隔离环境执行、在生产放量分别有独立边界。运行报告应该提供可复用的查询条件和版本号,并标识哪些是实际观察,哪些是推断。这样即便日后替换负责分析的模型,系统也能复查当时为什么做出那个选择。
智能体可能降低试验准备时间,也可能增加无效试验的数量。若可用容量固定,过多提案会挤占符合条件的验证机会。因此评价智能体不能只数提了多少改动,而应看多少提案进入实测后提供了可用信息,多少结果被独立复现,以及是否让人工审查负荷更低。对规模较小的团队,先整理一份可查询的实验账本、做可回退的流量切分,通常比引入自动分析平台更能解决缺乏有效证据的问题。等到第一批机制证明有收益,再逐步自动化那些边界稳定的实验步骤。
什么时候应该维持简单方案
如果请求相似、预算宽裕,或者观测数据不足以测出动作的差异,自适应层可能得不偿失。对于访问量少的服务,一条固定预算路径能让故障更容易重现;对于实时性要求很严格的服务,复杂决策本身占用的时间可能超过所节省的时间;对于高波动但偶发的活动流量,限流、缓存与明确的降级顺序也许已经足够。分层控制是解决耦合问题的一种方式,不是每套系统都应该具备的基础设施清单。
可以按控制对象逐一寻找最低成本的试验。第一步测出哪些请求消耗最不成比例;第二步验证换动作是否影响可见质量;第三步为新动作设置硬约束、回退和日志;最后才决定是否为这个动作训练预测器。假如一个简短且可解释的规则能取得大部分收益,余下差距就应与额外运维成本比较。这个判断也要可逆:业务流量变化、模型调用成本下降或设备算力变得充足之后,曾经值得维护的一条优化机制可能该被撤销。
这个原理并不依赖广告业务。任何在有限时间内处理候选、批量推理或共享设备的系统,都会碰到类似问题:局部优化能否被验证为全局改善?能否定位额外资源真正产生价值的环节?快速决策与慢速容量规划能否在压力下共存?答案来自能够被重复验证的边际收益、不会被优化目标吞没的约束,以及故障时可以回退的控制路径。先解释为何某一份追加计算值得花,再决定如何把计算交到那里。即使最终选择维持固定预算,这个过程也能够指出下一次扩大容量或更换模型真正应检验什么。