专业书籍精读 · ML System Design Interview · 第 2 章
Machine Learning System Design Interview · Ch 2 · Ali Aminian & Alex Xu · 2023
你在街上看见一双鞋,说不出牌子、也说不清款式,只好拍张照片丢进购物 App,它就给你找出一堆长得几乎一样的。这就是以图搜图。这一章从零讲怎么把这样一个系统造出来——不是造某个模型,是造整套东西。
你大概以为,机器是「看懂」了这张图:哦,这是一双白色运动鞋。其实它根本不做这件事。它做的是:把每张图压成一串数字——就像给每张图发一个「坐标」。然后「找相似的图」这个模糊问题,就变成了一道小学几何题:在坐标里,谁离它最近?
过去搜东西全靠文字。可你手上这张图,没有文字。「浅灰色、鞋头有三道斜杠、鞋底波浪纹」——这句话你打得出来吗?打得出来,搜索框也未必认。视觉上的像,翻不成词。更麻烦的是数量:一个正经的图库有几亿甚至几十亿张图。哪怕比一张图只要一眨眼的功夫,一张张比过去,也得等到明年。
三件事,一件比一件关键。
为了快,它放弃了「保证找到最像的那张」——只在几个片区里翻,真正最像的那张偶尔就躺在没翻的那个片区里,被漏掉了。
以图搜图的秘密不是让机器看懂图,而是把每张图变成一个坐标点,让「像不像」变成「近不近」——剩下的活儿,就是在几十亿个点里又快又省地找最近的那几个。
想进到具体机制、记号和示意图? → 切到精读版
全书第一道真题,示范的是一次问题转化:以图搜图表面是「让模型判断两张图像不像」,真正的解法是表示学习(representation learning)——把每张图编码成一个定长向量,让「视觉相似」等价于「向量距离近」。一旦转化完成,这道题的主战场就从模型挪到了检索:怎么在十亿个向量里毫秒级找到最近的几十个。
这是《ML System Design Interview》Part II 十道真题的第一道,也是 Ch1 七步框架的第一次完整演练。它开了全书最重要的一条主线——「编码成向量 + 近似最近邻检索 + 重排」,Ch4(YouTube 视频搜索)、Ch9(相似房源)都是同一副骨架换个模态。现实对应 Pinterest Lens、Google Lens、淘宝拍立淘这类产品。
用户手上有一张图,想找「长得像的」,却说不出关键词。文本搜索索引的是词,而视觉相似翻不成词——「鞋头三道斜杠」这种描述既写不准也匹配不上。
第二个痛点是量级,而且它比第一个更硬。假设图库 10 亿张、每张编码成 256 维 float32 向量(float32 = 每个数 4 字节):光向量就是 10⁹ × 256 × 4B ≈ 1 TB;一次精确检索要把查询向量和这 10 亿个逐一算距离,约 2.56 × 10¹¹ 次乘加。哪怕单机每秒能做 10¹¹ 次,一次查询也要好几秒——而产品要的是 p99 < 100ms。不解决这件事,你做出来的只是一个在十万张图上跑得动的 demo。
新手最自然的建模是:训一个二分类模型,输入两张图,输出「像 / 不像」。它的致命伤不在准确率,在算力形状——查询时要对图库里每张图做一次前向推理,10 亿次。这条路在工程上根本不存在。
书里的做法是把它拆成两半:训一个编码器,图库里的图离线各编码一次、存成向量;查询时只编码 1 张,然后做向量检索。同一个语义问题,换个建模就从「每次查询 10 亿次推理」变成「每次查询 1 次推理 + 一次向量检索」。这才是这一章真正教的东西:ML 系统设计里最大的性能收益,常常来自问题转化,而不是模型优化。
对比学习需要的是一条 (查询图, 1 个正样本, n 个负样本)。负样本好办——从库里随机抽就行。难的是正样本,书里给了三条路,各有代价(见下节表 1):人工标注准但贵;数据增强(把同一张图旋转、裁剪、调色当正样本)免费且无限,但模型学到的是「同一张图的不同形态」,不是「同一类东西」;用户交互(用户搜完这张图点了哪张,这两张就算一对)量大、贴业务,是书里推荐的主力,代价是带噪声和曝光偏差。
机制其实很朴素:查询图和所有候选图(1 正 + n 负)走同一个编码器、共享同一套权重,各得一个向量;算查询向量和每个候选向量的相似度(通常就是点积);把这 n+1 个相似度过一次 softmax 变成概率,再用交叉熵把「正样本那一个」的概率往 1 上推。
直觉:每训一步,正样本被拉近一点,n 个负样本被推开一点。反复上亿次之后,整个向量空间就被「梳」成了按视觉语义聚团的形状。负样本数 n 是个重要旋钮——n 越大,每步的「推开」信号越丰富,训得越好,代价是显存和计算随 n 线性涨。
系统落地成两条彼此独立的管道,这是全章的架构骨架。
索引管道(离线):把图库全量过一遍编码器,向量灌进索引;之后新增图片走增量。它是批处理,慢一点没关系,但有个隐藏约束——编码器一换版本,全库向量必须重算,10 亿张图的重编码是以天计的作业,所以模型迭代频率被这件事直接卡住。
预测管道(在线):查询图(支持用户框选裁剪)→ 预处理(缩放到统一尺寸如 224×224、像素值归一化)→ 编码器出向量 → ANN 检索出 top-k 候选 → 重排服务。重排这一步最容易被跳过,但它是产品能不能上线的关键:ANN 返回的「最像的 k 张」里,混着完全重复的图、违规内容、用户设了私密的图、以及被当前用户屏蔽的账号发的图。这些用向量距离一个都挡不住,只能在重排层用业务规则过滤。
精确检索是 O(N×D)(N 条向量、D 维),在十亿级上出局。书里把 ANN 分成四类:树(把空间递归切成小块,如 Annoy)、局部敏感哈希 LSH(设计一种哈希,让近的向量更容易撞进同一个桶)、聚类 / 量化(先把全库 k-means 成几万个簇,查询只扫最近的若干簇;再把向量压成几十字节的码来省内存)、图(把向量连成一张可导航的邻居图,从一个点出发一路「往更近的邻居走」,代表是 HNSW)。书里点名的现成库:Meta 的 Faiss、Google 的 ScaNN、Spotify 的 Annoy。
面试里最该说清的一句:ANN 的指标永远是三元组——召回率、QPS / 延迟、内存占用,任何单独一个都没意义。「我的召回率 99%」如果代价是扫一半的库,那就等于没做 ANN。
书里把候选指标一个个否掉,这段本身就是很好的指标课:MRR(只看第一个命中项的排名倒数)浪费了后面所有命中的信息;recall@k 需要知道「全库里到底有多少张是相关的」——视觉相似里这个分母根本不可知;precision@k 只数前 k 里对了几个,完全不管对的那几个排在第 1 还是第 10;nDCG 能处理「有点像 / 很像」的分级相关性,但要求有分级真值标注,贵。剩下 mAP:相关性是二元的(像 / 不像)时它既看命中数也看排得靠不靠前,是这道题的合适选择。
在线指标则回到业务:推荐结果的点击率、用户在推荐图上的平均停留时长。
表 1 · 正样本从哪来(决定模型学到的「相似」是什么意思)
| 来源 | 怎么造 | 代价 / 风险 |
|---|---|---|
| 人工标注 | 标注员判定两张图是否相似 | 质量最高;贵且慢,规模上不去,百万级对已是大投入 |
| 数据增强 | 把查询图旋转、裁剪、调色,变体当正样本 | 免费、无限量;但学到的是「同一张图的形变」,不是「同一类物体」,单靠它做出的系统只会找近似重复图 |
| 用户交互 | 用户搜这张图之后点了哪张,即视为一对 | 量大、天然对齐业务;代价是噪声(误点)与曝光偏差——没被展示过的图永远拿不到正标签,系统越用越固化 |
表 2 · 四类近似最近邻,各自拿什么换什么
| 方法 | 机制一句话 | 换来什么 / 代价 |
|---|---|---|
| 树 Annoy | 递归把向量空间切成小块,查询只下钻到相关的块 | 构建快、实现简单;维度一高就退化(高维下「切块」几乎切不开),256 维已经吃力 |
| LSH | 设计哈希函数,让距离近的向量更容易落进同一个桶 | 有理论保证、易分布式;要达到高召回得查很多张哈希表,内存与延迟都上去 |
| 聚类 / 量化 Faiss IVF-PQ | 全库 k-means 成几万簇,只扫最近的若干簇;再把向量压成几十字节的码 | 内存压缩最狠(1 TB 可压到几十 GB),十亿级单机可行;代价是量化误差损失精度,且簇的划分随数据漂移要定期重建 |
| 图 HNSW | 把向量连成分层邻居图,从入口点贪心地「往更近的邻居走」 | 同等召回下延迟通常最低;代价是内存占用最大(要存邻接边),且增删改代价高,不适合高频更新的库 |
表 3 · 离线排序指标怎么选(书里逐个否掉的过程)
| 指标 | 在看什么 | 为什么不适合 / 适合这道题 |
|---|---|---|
| MRR | 第一个命中项排名的倒数 | 只用了一个命中项,后面的全浪费了——而以图搜图本来就该返回一整屏相似图 |
| recall@k | 前 k 个里覆盖了多少相关项 | 分母是「全库相关项总数」,视觉相似里这个数不可知 |
| precision@k | 前 k 个里对了几个 | 完全不管顺序,排第 1 和排第 10 同等对待 |
| nDCG | 带分级相关性的排序质量 | 能表达「很像 / 有点像」;但要分级真值标注,成本高 |
| mAP | 各位置精确率的平均 | 相关性是二元时,既看命中数也看排得靠前与否——书里选的就是它 |
三张表指向同一句话:这道题的每个选择都在拿「更准」换「更贵、更慢或更难改」。面试真正考的是你能不能在给定的库大小、延迟预算和内存预算下,说清为什么选这一种。
这套「编码成向量 → ANN 检索 → 重排」的骨架,早已经不只属于以图搜图:语义搜索、推荐系统的召回层、RAG 的检索层、去重与风控的相似匹配,全是同一副骨架换个编码器。学会这一章,等于拿到了现代检索系统的通用模板。
① 核心转化:以图搜图不是「判断两张图像不像」,而是把每张图编码成向量,让视觉相似等价于向量距离近——建模一变,算力从 10 亿次推理降到 1 次推理加一次检索。
② 模型是表示学习 + 对比学习:查询、正样本、n 个负样本走同一个共享权重编码器,点积算相似度、softmax 后用交叉熵把正样本拉近、负样本推开。
③ 正样本三种来源:人工标注(准但贵)、数据增强(只会学到近似重复)、用户交互(书里的主力,代价是噪声与曝光偏差)。
④ 架构是两条管道:离线索引管道全量编码入索引,在线预测管道只编码查询图;两者靠同一个编码器耦合,换模型 = 全库重算。
⑤ 精确检索 O(N×D) 在十亿级出局;ANN 四类——树、LSH、聚类 / 量化、图(HNSW),代表库是 Faiss、ScaNN、Annoy。
⑥ ANN 的指标永远是三元组:召回率、延迟 / QPS、内存,缺一个都能被刷高。Google Cloud 公开的量级参照是召回 95–98% 配 p90 < 10ms。
⑦ 离线指标选 mAP:MRR 浪费信息、recall@k 分母不可知、precision@k 不看顺序、nDCG 要分级标注;在线看点击率与停留时长。
⑧ 重排层不能省:去重、过滤违规与私密内容、屏蔽关系,这些向量距离一个都挡不住。