专业书籍精读 · ML System Design Interview · 第 5 章
Machine Learning System Design Interview · Ch 5 · Ali Aminian & Alex Xu · 2023
你每天在社交平台上刷过成百上千条内容,几乎不会撞见血腥、辱骂或诈骗。这不是因为没人发,是因为每一条内容在送到你眼前之前,都过了一道看不见的安检。这一章讲的就是这道安检怎么造。
有一类内容,把它拆开看,每一块都干干净净。一张臭鼬的照片——没问题。一句「今天你身上的味道真好闻」——也没问题。可这两样贴在一起发给一个人,就是一次羞辱。
恶意不在图里,也不在字里,在两者的拼接里。所以一台「先让看图的人看一眼、再让看字的人看一眼、两人都说没事就放行」的安检机,对这类内容是彻底的睁眼瞎。这一章一半的设计,都是被这个事实逼出来的。
过去靠三样东西,三样都不够。
三步。
关键不在模型多聪明,在那两条分档线画在哪。往松了画,有人受到伤害;往紧了画,正常说话的人被无故删帖。这条线没有「正确」的位置——而且不同类别该画在不同地方:漏掉一条自残内容和误删一条措辞粗鲁的帖子,代价根本不是一个量级。
把最不确定的那一档交给人,等于给系统绑了一条产能固定的尾巴:阈值一松,队列就爆,最该被人看的那些内容反而排到了几小时之后。
有害内容检测真正难的不是「认出坏东西」,而是两件事:信息必须早合并——因为恶意常常只存在于图与字的组合里;模型只出分、不下判决——把最没把握的那一档留给人,并接受人的产能就是这套系统的上限。
想进到具体机制、记号和示意图? → 切到精读版
这一章表面是「做一个有害内容识别器」,内核是两个判断。其一,融合必须早做:有害往往不在图里也不在字里,而在两者的组合里,任何「各模态先各判一次再投票」的结构从原理上就看不见它。其二,输出必须是分数而不是判决:在正样本只占千分之几的分布上,任何一根阈值都是在「漏掉伤害」和「误删正常言论」之间硬选一边,所以真实系统把分数切成几档,把最不确定的那一档交给人。难点从来不在模型结构,在低底率下的阈值与人审产能。
本章是《ML 系统设计面试》Part II 十道真题的第 4 道。上承 Ch4 视频搜索——那里多模态是为了检索(把文字和画面钉进同一个空间好比距离),这里是为了判定(把多路信号汇成一个决策);下启 Ch6 视频推荐。现实对应各大平台的 Trust & Safety 系统、评论区治理,以及今天每个大模型产品都要挂的那层内容安全过滤。
书设定的场景是一个数亿日活的社交平台,每天新增内容按十亿条计,帖子可同时含文字、图片、视频和外链。要挡的是暴力、色情裸露、自残、仇恨言论等若干类有害内容——注意书把它和另一个问题切开了:坏账号(机器人、诈骗、骚扰、刷量)是行为层面的问题,靠账号图谱与行为序列来打,不在本章范围。
为什么值得单独一章?因为前面几题都能靠「更好的模型」往前推,这一题不能。三条旧路各有各的死法:用户举报是事后的——代价按触达算不按条数算,一条爆起来的违规视频在被举报前就可能触达几十万人,而举报本身既滞后又稀疏;违禁词表挡不住对抗——发布方会主动规避(拆字、谐音、变体拼写、把话写进图片里),词表每更新一次规避方式就变一次;纯人工的产能算不过来——十亿量级的日增内容,就算每条只看两秒、一人一天不吃不睡看四万条,也要几万人同时在线才追得平。
所以这一章真正要回答的是:怎么在内容被大量看到之前给出一个可行动的判断,同时把它必然会犯的两类错误——漏检与误删——分别安置到能承受它们的地方去。
一条帖子有文字也有图,两条信息怎么汇到一起?晚期融合是文本、图像各判一次,最后合并两个分数;早期融合是各模态先编码成向量、拼接成一条完整特征,再交给一个分类器。晚期融合工程上诱人——可独立训练、复用现成模型、分别扩容。书仍选早期融合,两条理由,第二条更硬:
第一,晚期融合看不见跨模态的恶意。图无害、字无害、合起来才有害的内容(业界叫「良性混淆样本」,benign confounder),在晚期融合里两路都返回低分,合并之后仍是低分。这不是模型不够强,是结构性盲区——信息在能相遇之前,就已经各自被压成一个标量了。
第二,标签根本不是按模态打的。标注给出的是「这条帖子违反了哪几条政策」,没有分模态的标签。晚期融合要求每个模态各有一个能独立训练的分类器,也就要求额外造一份分模态标注——这笔账在十亿量级上压根不成立。
早期融合的代价也要认:交互空间大,要明显更多训练数据才学得动;吃不到各模态现成分类器的红利;某条模态缺失时整条特征还要补默认值。
要挡的不是一类内容,是一组政策:暴力、裸露、自残、仇恨言论……而且一条内容可以同时违反好几条。四种形态,书按序淘汰:
多任务赢在三处,第三处最实用:数据效率——罕见类蹭到常见类练出来的表示,正是「每类一个模型」补不上的那块;服务成本——一次前向出全部类别的分;每个头可以独立定阈值——不同品类的代价结构差着数量级,漏掉一条自残内容和误删一条措辞粗鲁的帖子不该共用一根线。代价是任务间可能负迁移,且改一类政策要重训整个骨干。
书把特征分三类,后两类常被面试者整块漏掉:
评估的全部困难来自同一件事:底率极低。算一遍就清楚。假设 100 万条帖子里有 1000 条真违规(底率 0.1%),一个召回率 99%、假阳性率 1% 的分类器——听起来相当能打——实际捞回 990 条真的,同时误伤约 9990 条正常帖。精确率不到 10%:每删十条,九条是冤枉的。
而它的 ROC-AUC 会非常漂亮:ROC 的横轴是假阳性率,99 万条负样本被 1% 误伤,横轴上才挪动 0.01。低底率下 ROC-AUC 会系统性高估模型,该看的是 PR 曲线——纵轴直接就是精确率。
线上换一套口径,书给的三个最该记住:违规触达率(prevalence,有害内容的展示次数占总展示的比例——按被看见多少次算而不是按条数算,才对得上伤害的真实分布)、主动发现率(proactive rate,在用户举报之前就被系统发现的比例)、有效申诉率(被删内容里申诉成功的比例,误删代价唯一可直接观测的量)。
表 1 · 融合发生在哪一层
| 晚期融合 · 各判各的 | 早期融合 · 先拼再判 | |
|---|---|---|
| 结构 | 每模态一个分类器 → 合并分数 | 每模态编码成向量 → 拼接 → 一个分类器 |
| 工程便利 | 高:独立训练 / 扩容,复用现成模型 | 低:整体训练,改一处牵全身 |
| 标签要求 | 要分模态标签(现实中没有) | 只要帖子级标签(现实中就是这个) |
| 致命短板 | 看不见跨模态恶意,且不报错 | 交互空间大,要更多数据才学得动 |
| 适用 | 各模态独立成罪的品类 | 本章的默认选择 |
表 2 · 四种模型形态
| 形态 | 能给出什么 | 代价 / 为什么被淘汰 |
|---|---|---|
| 单个二分类 | 一个有害分 | 说不出违了哪条 → 无法分类定阈值、解释、接申诉 |
| 每类一个二分类 | 逐类的分 | N 份训练 / 上线 / 监控;罕见类各自数据不够 |
| 单个多标签 | 一次出全部类别的分 | 便宜,但一份共享表示摊给差异极大的任务 |
| 多任务 | 共享表示 + 每类一个头 | 书的选择;代价是负迁移、改一类要重训骨干 |
表 3 · 分数怎么变成动作(阈值带 → 处置 → 谁付代价)
| 分数带 | 处置 | 这一档的代价落在谁身上 |
|---|---|---|
| 很高 | 自动删除 + 通知作者违了哪条 | 误删:直接生成申诉,伤的是正常表达 |
| 中间 | 限制分发(降权)+ 进人审队列 | 人审产能:阈值一松队列就爆,延迟即伤害 |
| 很低 | 放行 + 少量随机抽样送审 | 漏检:抽样是唯一能发现这一档漏了什么的手段 |
三档之外还有一条实操判据:人审队列不该按分数排,该按「分数 × 预计触达」排。一条 0.72 分但正在爆的视频,比一条 0.88 分却没人看的帖子更该先被人看到——代价是按触达算的。
「多模态早融合 + 多任务 + 分数分档 + 人审回流」这套骨架,今天就是各大平台 Trust & Safety 系统的通用形状,也被整段搬进了大模型产品的内容安全层——输入过一遍、输出再过一遍,同样是多类别打分而非一刀切拒绝。面试高频追问三处:为什么不用晚期融合(跨模态恶意的结构性盲区,加上标签本来只打在帖子级)、ROC-AUC 0.99 为什么不敢上线(底率千分之一,精确率可能不到 10%)、人审队列怎么排(按分数 × 触达)。
① 内核两件事:信息必须早融合,输出必须是分数而不是判决。
② 问题本质:举报是事后的、词表挡不住对抗、纯人工在十亿量级日增内容前算不过来;代价按触达算。
③ 早融合 vs 晚融合:图无害、字无害、组合有害的「良性混淆样本」在晚期融合里是结构性盲区;何况标签只打在帖子级,分模态分类器连训都训不出来。代价是要更多数据。
④ 多任务 > 每类一个模型 > 单个二分类:共享骨干让罕见类蹭到常见类的数据,一次前向出全部类别的分,每个头还能独立定阈值。
⑤ 特征别只想内容:作者的历史违规与账号年龄、帖子的浏览/分享/举报与评论区都是强信号;举报数是特征,不能当触发器。
⑥ 低底率算术要能当场算:底率 0.1%、召回 99%、假阳性率 1% → 精确率不到 10%。所以看 PR 曲线,不看 ROC。
⑦ 线上三个指标:违规触达率(按展示算)、主动发现率(跑在举报前面多少)、有效申诉率(误删代价)。
⑧ 分数分三档:高分自动删(代价=误删)、中间降权加人审(代价=人审产能)、低分放行加抽样(代价=漏检);队列按分数 × 预计触达排,人审产能倒推阈值。