专业书籍精读 · ML System Design Interview · 第 10 章
Machine Learning System Design Interview · Ch 10 · Ali Aminian & Alex Xu · 2023
你早上打开社交 App,第一屏那几条不是按时间排的。你关注的人昨天发了几百条,平台只挑其中几十条给你,顺序还是为你一个人现算的。这一章讲的就是:那个顺序怎么算出来。
最省事的做法是猜「你会不会点开」,然后按这个可能性从高到低排。听着没毛病。
可真这么做,几个月后你的信息流会变成一片标题党。因为模型问的是「会不会点」,而这类标题恰恰点击率最高、看完最后悔。你确实点了,模型觉得自己干得漂亮。问题出在:点击不等于喜欢。
难在这件事没有标准答案。哪条该排第一?没人写得下来。同一条动态,对老同学是想看的近况,对陌生人是噪音。
唯一拿得到的线索,是你做过的动作:点开了、点赞了、评论了、转发了、停下来看了几秒、还是直接点「不感兴趣」。这些动作都是真的,但每一个只说明一小块:点赞很随手,评论说明真被戳到,转发说明你愿意署上自己的名字,「不感兴趣」说明这条根本不该出现。
这一章的办法是:同一条动态,一次问六七个问题——会点开吗?会点赞吗?会评论吗?会转发吗?会停留多久?会不会嫌烦把它划掉?
每个问题有一个小裁判给出可能性,最后把这些可能性各乘一个分数再加起来,就是这条动态的排序分。像体操比赛:不是一个裁判说了算,而是几位裁判分别打分、按事先定好的比重合成总分。「嫌烦」那一项的比重是负的,它会把分往下拽。
这些裁判还共用同一套「对这个人和这条内容的理解」,只在最后一步才分开。好处很实在:转发这种很罕见的动作,单独学学不明白,跟着点赞一起学就能沾光。
模型怎么训是技术问题;评论该值几分、点赞该值几分,是价值判断。把评论的比重调高,创作者第二天就开始追着「引发讨论」做内容;把停留时长的比重调高,那些信息量不大但很能拖时间的内容会浮上来。
代价也直接:这组比重没有正确答案,只能靠一轮轮线上实验试出来,而每改一次,都会重塑所有创作者的行为。
信息流排序不是猜「你会不会点」这一个问题,而是同时猜好几种反应(赞、评、转、停留、嫌烦),再按事先定好的比重合成一个分。模型决定猜得准不准,比重决定这个产品最后长成什么样。
想进到具体机制、记号和示意图? → 切到精读版
这一章把「信息流该怎么排」翻译成一个可训练的问题,答案是多目标排序:不预测单一的点击率,而是为每一种用户反应(点击、点赞、评论、分享、停留时长、隐藏 / 举报)各训练一个预测头,再把这些概率加权求和成唯一的排序分。你以为它在教你训模型,其实真正的结论是:模型负责把概率算准,那组权重负责决定产品长成什么样——前者是工程,后者是产品判断。
ML 系统设计面试 Part II「十道真题」的倒数第二道。它上承 Ch6 视频推荐(召回 + 排序 + 重排的三层漏斗)与 Ch8 广告点击预估(单目标 CTR、且概率必须校准),再往前推一步:Ch8 只有一个目标,这一章有六七个目标,还必须合成一个分。对应现实里的 Facebook Feed、X 的 For You、以及各家内容平台的首页。
先看规模:一个 Facebook 量级的平台,日活以十亿计;单个用户的候选池是「好友 + 关注的群组 / 主页在最近几天发的、你还没看过的内容」,量级在几百到几千条;每次下拉刷新,要在几十毫秒内把它们排好。
但规模不是这一章的难点——漏斗结构 Ch6 已经给过。真正的难点是目标函数写不出来:没有任何标注说「这条该排第一」,唯一的监督信号是用户做过的动作,而每一种动作都只是「价值」的有偏代理。
不解决会怎样?看单目标优化各自的下场:只优化点击,标题党与诱导互动胜出;只优化停留时长,信息量低但能拖时间的内容浮上来;只优化点赞,迎合性内容占满屏幕。更麻烦的是这套东西会闭环:模型改变分发,分发改变创作者行为,新内容再回来当训练数据。所以这一章必须单独存在——它要解决的不是「怎么预测」,而是「该预测什么」。
第一步是把模糊的「用户会喜欢吗」拆成一组能从日志里自动打标签的事件:显式的有点赞、评论、分享、隐藏、举报、点「减少此类内容」;隐式的有点击、停留时长、skip(在若干秒内划过)。除停留时长是回归(或分桶)外,其余都是干净的二分类。
这里有一个容易漏掉的设计动机:被动用户。相当一部分人从不点赞、不评论、不转发,只是安静地刷。如果只拿显式反应当目标,模型对这批人几乎没有监督信号。所以必须补上停留时长与 skip 两个任务,把沉默用户的偏好也接进来——这是本章给多任务清单额外加的两项,也是最实用的一笔。
再看密度:点赞的正样本率通常在个位数百分比,评论低一个量级,分享再低一个量级,举报 / 隐藏往往只有万分之几。同一个网络里的任务,样本密度能差三四个数量级——这直接决定了下面为什么要共享底座。
三族特征。帖子侧:正文用预训练语言模型编码成向量,图 / 视频用预训练视觉模型编码;话题标签数量多、更新快,实践中常用 TF-IDF 或特征哈希这类轻量表示而不是训练专属嵌入(学不动、也追不上新词);再加上已有互动数(取对数压缩)与发布至今的时长。用户侧:人口属性、语言地区,以及设备、时段这类上下文。
真正拉动效果的是第三族——作者与用户的亲密度(affinity):这个人历史上给这位作者点过多少赞、评论过几次、好友关系多久、是否标记为亲密好友、这条内容有没有 @ 他。在信息流里,这族特征往往比换模型结构管用得多,原因很朴素:信息流的本质是社交关系,不是内容检索。
结构很直白:所有特征拼成输入 → 若干层共享的全连接层,学「这个人 × 这条内容」的通用表示 → 每个任务接一个自己的小头 → 每个头用 sigmoid 输出一个概率,各自算二元交叉熵,总损失是各任务损失之和。
为什么要共享?因为分享、举报这类稀疏任务单独训练几乎学不出东西,共享底座让它们借用点击、点赞这些稠密任务学到的表示。这才是多任务在这里的真正理由,不是为了省机器。
代价要诚实写:任务方向不一致时会发生负迁移(negative transfer)——共享的表示被稠密任务主导,稀疏任务反而更差;而且所有任务被绑死,想单独迭代一个头也得重训整网。工业界后来的主流是把「完全共享」放松成「软共享」,代价是参数量与训练成本上升。
最终分是 score = Σ wᵢ·pᵢ——把每种反应的预测概率乘上「这种反应值多少」,加起来。三件事要讲清:
负权重的量级。隐藏、举报、「不感兴趣」的权重是负的,而且绝对值通常远大于正向项:一次举报要能压过成百上千次点赞,否则负反馈在加权和里根本不起作用——因为它太稀疏了。
权重怎么定。没有闭式解。实践路径是:按产品意图人工拍一组初值 → 线上 A/B 用长期指标(次日 / 七日留存、满意度调研)判优 → 迭代。注意这里的错位:训练用的是当下的点击点赞,判优用的是长期留存,那组权重就是把短期信号翻译成长期价值的转换层。
要不要校准。排序只关心顺序,不像 Ch8 的广告分要拿去计费那样必须严格校准;但各任务概率的尺度必须可比,否则同一组权重的含义会随模型迭代悄悄漂移。
在线链路是四段:候选生成(未读 + 时间窗 + 关系边)→ 排序(多任务模型逐条打分)→ 重排 → 展示。
重排层做的是排序分里表达不了的约束:同一作者限流、话题多样性、完整性(integrity)降权(虚假信息、诱导互动)、用户自设的屏蔽。为什么不塞进模型?因为它们是规则与政策,要求可解释、可审计、能一夜之间改;混进模型权重里就既说不清、也改不动。
这一章的灵魂全在「拿什么当目标、怎么合成」。第一张表回答「哪些信号能当训练目标」——注意最后一列,单独优化任何一个都会坏掉,这正是多目标的存在理由。
| 信号 | 密度 | 它代表什么 | 被刷的难度 | 单独优化的下场 |
|---|---|---|---|---|
| 点击 | 高 | 好奇心,与满意度关系很弱 | 极易 | 标题党占满全屏 |
| 停留时长 | 高 | 注意力被占住了 | 容易(拖长、切片) | 信息量低的长内容上位 |
| 点赞 | 中 | 轻度认同,成本很低 | 中 | 迎合性内容上位 |
| 评论 | 低 | 被真正触动——或激怒 | 中(引战最有效) | 争议与对立内容上位 |
| 分享 | 很低 | 愿意用自己的名义背书 | 难 | 信号太稀疏,覆盖不住大盘 |
| 隐藏 / 举报 | 极低 | 明确的负面判断 | 难(用户成本高) | 只能当负项,做不了主目标 |
第二张表是结构选型:任务之间越冲突,越该往软共享走,代价是成本。
| 方案 | 什么场景选它 | 代价 |
|---|---|---|
| 每任务一个独立模型 | 任务很少、彼此无关,且各团队要独立迭代上线 | 稀疏任务学不动;训练与推理成本按任务数翻倍 |
| 共享底座 + 任务头 (本章方案) | 任务相关、有稀疏任务需要「借力」、算力有限——绝大多数信息流的默认起点 | 负迁移:共享表示被稠密任务主导;任务耦合,改一个头要重训整网 |
| 软共享(如 MMoE) | 任务之间明显冲突(「看完」与「点赞」常常不同向),且样本量足够大 | 参数与训练成本上升,门控本身又成了要调的东西 |
最后一个权衡不在模型里:权重该多久调一次。调得太频繁,创作者跟不上、生态反复震荡;调得太少,模型会一路滑向最容易被刷的那个信号。
面试里说一句「上多任务」不值钱,值钱的是能说清三件事:为什么要共享底座(救稀疏任务,不是省机器)、负权重的量级为什么必须大(否则稀疏的负反馈被淹没)、权重靠什么判优(离线指标没法判,只能线上长期指标)。架构评审里同理:一旦有人问「我们到底在最大化什么」,答案就是那一行加权求和。
-20000、负反馈到 -1000,公开印证了「一次举报要压过成千次正向互动」这个量级设计(开源快照里默认值置零,线上取值不在仓库内)。twitter/the-algorithm, GitHub 2023 ↗ · HomeGlobalParams.scala ↗① 一句话:信息流排序的答案是多目标排序——为每种反应各训一个预测头,再加权合成一个排序分。
② 这章的真问题不是「怎么预测」,而是「该预测什么」:没有标注说哪条该排第一,只有用户动作这个有偏代理。
③ 单独优化任何一个信号都会坏掉:点击→标题党、停留→注水长内容、评论→争议内容。
④ 必须把停留时长与 skip 加进任务清单,否则从不点赞的被动用户对模型等于没有信号。
⑤ 用共享底座 + 任务头的理由是救稀疏任务(分享、举报的正样本率比点赞低三四个数量级),代价是负迁移与任务耦合;冲突明显时升级到 MMoE 这类软共享。
⑥ 特征上最能拉动效果的不是内容,而是作者—用户亲密度——信息流的本质是社交关系。
⑦ score = Σ wᵢ·pᵢ 里,负反馈的权重绝对值要远大于正向项(X 的开源代码里举报区间到 -20000),否则稀疏的负信号会被淹没。
⑧ 那组权重没有离线解:只能人工拍初值 + 线上 A/B 用长期留存与满意度判优;它是把短期信号翻译成长期价值的转换层,也是真正的产品决策。