小周第一天到市立图书馆报到,眼前的场景让他倒吸一口凉气:一亿册藏书,从《三体》到《小王子》挤满了整栋大楼。读者的问题五花八门:“我想找一本和《三体》差不多的书”“有没有类似村上春树的作家”。如果老老实实把一亿本书都翻一遍再回答,读者早就睡着了。 带他的老馆长笑着递给他一张泛黄的“坐标卡”:“别慌。我们不用逐本翻书,只要给每本书办一张'性格坐标卡'——科幻程度、情感浓度、文笔风格……然后用这些数字在'语义地图'上找邻居。相似的书在地图上离得近。这套方法,叫向量召回。” 向量召回,就是把用户和物品都编码成一组数字(即 Embedding 向量),放进同一个“语义地图”里,用“找最近邻居”的方式快速筛出候选。它是现代推荐系统的大门:先快速圈出几百个“可能喜欢的”,再交给后续模型精挑细选。本篇文章我们就跟着小周,从图书馆的坐标卡,一路讲到工业级向量召回的全流程。

岗位关联

掌握向量召回,是成为推荐算法工程师的关键一步。它能让你在大规模数据下高效筛选候选,直接决定推荐系统的效果上限与工程成本。

目录
  1. 可视化演示
  2. 向量召回概述
  3. Embedding 编码
  4. 双塔模型
  5. 负采样策略
  6. ANN 索引
  7. Faiss 实践
  8. 在线服务架构
  9. 召回评估
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

动态演示向量召回的核心机制,通过概览/流程/对比/趋势四种模式展示关键概念与交互流程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

向量召回:Embe Embedding向量编码 ANN近似最近邻索引 核心机制可视化 向量召回整体架构与关键组件

向量召回概述

老馆长带小周熟悉业务流程。“咱们馆有三道工序:圈地、精读、上书架。”他指着前台说:“读者要什么书,我们不会把一亿本都搬出来,而是先圈出最可能对口的几十本——这叫召回;再从几十本里精读细比,排出先后——这叫排序。”

在推荐系统里,也是同样的分工。全量物品可能上亿,排序模型再强也没法对每个物品都做复杂计算。于是系统先用轻量快速的手段,从全量物品里筛出几百个候选,把“大海捞针”变成“碗里挑豆”,这就是召回。召回决定了推荐的天花板——候选里根本没有的,排序再厉害也推不出来。

向量召回是当下最主流的召回方式:把用户和物品各自编码成一个向量(一组数字),放进同一坐标系里,用“找最近邻”代替“逐个翻查”。给定用户向量 $\mathbf{e}_u$,从物品向量集合 $\mathcal{I}$ 中取相似度最高的 $K$ 个:

小周恍然大悟:“所以召回就是拿着读者的坐标卡,在一亿张书卡里圈出最像的那几十张!”老馆长点头:“对,后面排序是精读,但圈地圈得准不准,决定了精读的上限。”

C^u=TopKi∈I  sim(eu,ei)\hat{\mathcal{C}}_u = \underset{i \in \mathcal{I}}{\mathrm{TopK}} \; \mathrm{sim}(\mathbf{e}_u, \mathbf{e}_i)
这个式子的意思是:把用户 $u$ 的向量,跟全库每个物品向量比一遍相似度,取最大的前 $K$ 个,作为候选集合 $\hat{\mathcal{C}}_u$。
  1. 候选集$\hat{\mathcal{C}}_u$ 表示给用户 $u$ 召回的候选物品集合,是“圈地”圈出来的那块地。
    C^u\hat{\mathcal{C}}_u
  2. 全量物品库$\mathcal{I}$ 是全部物品的下标集合,对应图书馆里那一亿本书。
    I\mathcal{I}
  3. TopK 算子$\underset{i \in \mathcal{I}}{\mathrm{TopK}}$ 表示在全体物品中按分数从高到低取前 $K$ 个。
    TopKi∈I\underset{i \in \mathcal{I}}{\mathrm{TopK}}
  4. 用户向量$\mathbf{e}_u$ 是用户 $u$ 的 Embedding 向量,相当于读者的“性格坐标卡”。
    eu\mathbf{e}_u
  5. 物品向量$\mathbf{e}_i$ 是物品 $i$ 的 Embedding 向量,相当于书籍的“性格坐标卡”。
    ei\mathbf{e}_i
  6. 相似度函数$\mathrm{sim}(\cdot, \cdot)$ 计算两张坐标卡的贴合程度,常用内积或余弦相似度。
    sim(eu,ei)\mathrm{sim}(\mathbf{e}_u, \mathbf{e}_i)

Embedding 编码

要给一亿本书办坐标卡,第一件事是定义“性格维度”。老馆长拿出一套标准:科幻浓度、情感浓度、幽默程度、历史厚重感……每种书在这些维度上打分,就得到一张卡。比如《三体》的科幻浓度极高、情感浓度中等;《红楼梦》历史厚重感极高、科幻浓度几乎为零。小周发现,打分的本质就是把“没法直接比较的书”变成“可以比大小的数字”。

这套打分卡,在深度学习里就是 Embedding——把离散的、人类可懂的信息压缩成一组连续数字。关键神奇之处在于:只要训练得充分,语义相似的事物会被映射到空间里相近的位置。科幻小说和太空歌剧靠得近,和菜谱离得远;喜欢烧脑悬疑的读者,他的兴趣向量会落在离推理小说近的区域。

衡量两张卡“远不远”,最常用的是余弦相似度。它只看方向不看长度,天然适合比较“性格倾向”:

小周试着给《三体》和《球状闪电》算余弦:两个科幻浓度都高,夹角很小,相似度 0.87;再给《三体》和《菜谱大全》算,夹角接近直角,相似度只有 0.1。他感叹:“坐标卡一办,整座图书馆就变成了一张地图。”

sim(a,b)=a⋅b∥a∥ ∥b∥=cos⁡θ\mathrm{sim}(\mathbf{a}, \mathbf{b}) = \frac{\mathbf{a} \cdot \mathbf{b}}{\|\mathbf{a}\| \, \|\mathbf{b}\|} = \cos \theta
分子是两个向量对应维度乘积之和,分母是两个向量的“长度”相乘,结果落在 $[-1, 1]$,越接近 1 表示方向越一致。
  1. 点积运算$\mathbf{a} \cdot \mathbf{b}$ 把两个向量对应位置的数字相乘再相加,衡量“同方向上的共鸣”。
    a⋅b=∑d=1Dadbd\mathbf{a} \cdot \mathbf{b} = \sum_{d=1}^D a_d b_d
  2. 向量模长$\|\mathbf{a}\|$ 是向量 $\mathbf{a}$ 的长度,由各维度平方和开根号得到。
    ∥a∥=∑d=1Dad2\|\mathbf{a}\| = \sqrt{\sum_{d=1}^D a_d^2}
  3. 归一化除以两个模长,消除“总分不同”带来的偏差,只看性格方向。
    a⋅b∥a∥ ∥b∥\frac{\mathbf{a} \cdot \mathbf{b}}{\|\mathbf{a}\| \, \|\mathbf{b}\|}
  4. 夹角余弦$\cos \theta$ 中 $\theta$ 是两个向量的夹角,夹角越小余弦越接近 1,表示两张卡越像。
    cos⁡θ\cos \theta

双塔模型

读者对书有偏好,书有性格,那读者也得有卡。老馆长安排了两个“办卡师傅”:师傅 A 专门给读者办卡,输入读者的浏览、点击、借阅历史;师傅 B 专门给书办卡,输入书的文本、标签、封面。两位师傅互不干扰、各学各的,但最后都必须把结果填进同一张地图。小周问:“两人各画各的,怎么保证地图对得上?”

这就是双塔模型的默契来源——训练。用户塔把用户行为序列编码成一个向量,物品塔把物品特征编码成一个向量。训练时让“用户喜欢的物品”这对组合的匹配分尽可能大,两位师傅就在无数次的正反例对账中,学会了同一套坐标系。线上使用时,两塔分别产出向量,存入索引供检索。

两塔打分的核心是内积:

内积越大,说明用户的“兴趣方向”与物品的“性格方向”越一致。相比余弦,内积保留了模长信息——热门爆款通常模长大,会有天然倾向;很多工业实现会先做 L2 归一化再用内积,等价于余弦。

小周打趣道:“两位师傅虽然不认识,但训练时总把'适合的读者和书'往一处凑,时间久了,读者塔和物品塔自然长出了同一种方言——内积高分。这就是班主任安排好的默契。”

s(u,i)=u⊤vi=∑d=1Dud vi,ds(u, i) = \mathbf{u}^\top \mathbf{v}_i = \sum_{d=1}^{D} u_d \, v_{i,d}
  1. 用户向量$\mathbf{u}$ 是用户塔对用户 $u$ 输出的 Embedding,维度为 $D$。
    u=(u1,u2,…,uD)\mathbf{u} = (u_1, u_2, \ldots, u_D)
  2. 物品向量$\mathbf{v}_i$ 是物品塔对物品 $i$ 输出的 Embedding,同样维度为 $D$。
    vi=(vi,1,vi,2,…,vi,D)\mathbf{v}_i = (v_{i,1}, v_{i,2}, \ldots, v_{i,D})
  3. 转置符号$\top$ 表示把列向量转成行向量,才能做矩阵乘法。
    u⊤\mathbf{u}^\top
  4. 内积求和$\sum_{d=1}^{D} u_d \, v_{i,d}$ 把 $D$ 个维度逐项相乘再加总。
    ∑d=1Dud vi,d\sum_{d=1}^{D} u_d \, v_{i,d}
  5. 匹配得分$s(u, i)$ 越大,代表用户与物品越匹配。
    s(u,i)s(u, i)

负采样策略

训练两位办卡师傅,得有教材。老馆长的教材有两摞:正例教材——“张三借过《三体》,所以张三和《三体》应该靠近”;负例教材——“张三从没碰过《母猪产后护理》,这本书就该离他远一点”。小周问:“那负例怎么挑?总不能把剩下九千多万本都搬来当反例吧。”

这正是负采样的意义。全量负样本参与训练,计算量不可接受,而且绝大多数负样本太容易区分,学不到东西。所以每次训练只随机抽一小批负样本。更高级的做法是“困难负样本”——挑那些看着挺像、但读者确实不喜欢的物品,逼模型学到更细的边界。比如张三喜欢《三体》,但同属科幻的《太空漫游 2001》他反复划走,这一本就比《菜谱大全》更有教学价值。

目标是对每个正样本,让它相对负样本“胜出”。常用样本内 softmax 损失:

其中 $i^+$ 是正样本,$\mathcal{N}$ 是采出的负样本集合。负样本太少模型欠拟合,太多训练变慢;随机负样本保“广度”,困难负样本保“精度”,两者按比例混合是工业界标配。

老馆长总结:“光教对的,师傅只会死记硬背;隔三差五塞几本'看起来像但读者不爱'的难题,师傅才真懂书和人的微妙差别。负样本就是那叠错题集。”

L=−log⁡es(u,i+)es(u,i+)+∑j∈Nes(u,ij)\mathcal{L} = -\log \frac{e^{s(u, i^+)}}{e^{s(u, i^+)} + \sum_{j \in \mathcal{N}} e^{s(u, i_j)}}
  1. 正样本$i^+$ 是用户真实交互过的物品,模型要让它的得分尽量高。
    es(u,i+)e^{s(u, i^+)}
  2. 负样本集合$\mathcal{N}$ 是采样出的负样本集合,通常是随机抽取加困难样本组合。
    N\mathcal{N}
  3. 指数打分$e^{s}$ 把分数映射成正数,分数越高权重越大。
    exp⁡(s(u,ij))\exp(s(u, i_j))
  4. 归一化分母分母把正样本和所有负样本的权重加总,把得分压成概率。
    ∑j∈Nexp⁡(s(u,ij))\sum_{j \in \mathcal{N}} \exp(s(u, i_j))
  5. 负对数损失$-\log$ 让“正样本概率越接近 1”时损失越小,模型以此方向学习。
    −log⁡(⋅)-\log(\cdot)

ANN 索引

坐标卡有了,接下来是“怎么找”。一亿张卡,读者来了再一张张算余弦,等算完读者都走了。老馆长说:“咱们得建导航系统。就像图书馆先分楼层、再分书架,先把一亿本书粗分成一批片区,读者来了先判断该去哪个片区,再在片区内细找。”

这个思路叫近似最近邻检索(ANN,Approximate Nearest Neighbor)。它不追求每次都是全局最优解,而是用合理的“分区+剪枝”策略,用极少量的精确度损失换回几十倍的速度提升。全量逐卡比对叫暴力检索(brute force),只作为正确性基准。

主流的索引方法有四类:哈希索引(LSH)把相近向量散列到同一桶;倒排索引(IVF)先聚类再只查最近几个簇;乘积量化(PQ)把向量压缩成短码、用查表近似距离;图索引(HNSW)把向量连成“熟人网络”,从任意点出发沿着近邻跳。复杂度对比最能说明问题:

其中 $N$ 是物品数,$D$ 是向量维度,$k$ 是聚类数。把一亿本书分成一万个片区后,查询量直接缩小一万倍。

小周感叹:“这就像读者说要找科幻书,管理员不会带着他把整栋楼翻一遍,而是直接领到三楼科幻书架。'先分区、再细找',就是 ANN 的灵魂。”

T暴力=O(N×D),TIVF=O ⁣(Nk×D)T_{\text{暴力}} = O(N \times D), \qquad T_{\text{IVF}} = O\!\left(\frac{N}{k} \times D\right)
  1. 暴力检索复杂度$O(N \times D)$ 表示每个向量、每一维都要算一遍,一亿本书 × 128 维 = 128 亿次运算。
    T暴力=O(N×D)T_{\text{暴力}} = O(N \times D)
  2. 分区数$k$ 是把向量空间切成的簇个数,相当于楼层和书架的分区数。
    kk
  3. IVF 查询量$\frac{N}{k}$ 表示只需扫描一个片区的向量数量,$k$ 越大剪枝越狠。
    Nk\frac{N}{k}
  4. IVF 检索复杂度$O\!\left(\frac{N}{k} \times D\right)$ 是找到片区后,在片区内暴力检索的代价。
    TIVF=O ⁣(Nk×D)T_{\text{IVF}} = O\!\left(\frac{N}{k} \times D\right)

Faiss 实践

导航系统图纸有了,但真建起来不容易。老馆长干脆请来一支专业施工队——Faiss,这是 Meta 开源的向量检索库,相当于“智能检索柜”的成品方案。但施工队进场不等于万事大吉,小周发现,柜子的隔板怎么分、探头伸多深,全都要人调。

Faiss 提供各种索引实现(IndexFlatIP、IndexIVFFlat、IndexIVFPQ、IndexHNSW 等),各有精度、速度、内存的取舍。工程上最重要的两个旋钮是 nlist 和 nprobe:nlist 是建索引时把空间切成多少份;nprobe 是查询时探测最近的几个分区。nlist 定“分区粒度”,nprobe 定“搜索深度”,二者共同决定延迟与召回。

选择经验有章可循。Faiss 官方推荐:

nlist 决定了索引的粒度。实践中先按公式起步,再在线上用真实流量压测微调。内存不够就上 PQ 量化压缩向量,延迟吃紧就上 GPU 批量检索,单机扛不住就把索引分片部署到多机。

小周在工地上看着施工队把一亿张卡分进四万个格子:“原来 Faiss 不是魔法,它只是把'分区导航'这个朴素思路做成了标准化产品。选对隔板、调好探头,才用得顺手。”

nlist=4N,nprobe=nlist\text{nlist} = 4\sqrt{N}, \qquad \text{nprobe} = \sqrt{\text{nlist}}
  1. nlist 经验值$4\sqrt{N}$ 是 Faiss 文档建议的初始分区数,$N$ 为向量总数,一万本书时 nlist 约 400。
    nlist=4N\text{nlist} = 4\sqrt{N}
  2. nprobe 经验值$\sqrt{\text{nlist}}$ 是推荐的初始探测分区数,比如 nlist=400 时 nprobe 约 20。
    nprobe=nlist\text{nprobe} = \sqrt{\text{nlist}}
  3. 调参方向nprobe 越大,扫描分区越多,召回越准但延迟上升,需要压测找平衡点。
    nprobe↑⇒Recall↑,  Latency↑\text{nprobe} \uparrow \quad \Rightarrow \quad \text{Recall} \uparrow, \; \text{Latency} \uparrow

在线服务架构

万事俱备,小周终于站上值班台。读者涌进来,高峰期一秒来一千个问题。老馆长的要求很硬:“从读者开口到我们给出候选书单,最多 50 毫秒。”小周把流程写在便签上:取读者卡 → 查导航系统 → 拿回 TopK 书卡 → 过滤掉已借/违规 → 把候选交给下一个环节。

在线向量召回服务就是这个值班流程的工程化。索引要提前加载进内存(常驻服务),不能现问现建;用户向量要实时从画像服务取;返回候选要经过规则过滤(去重、下架、品类限制)才能交给排序。新书上架走增量更新通道,只插入新向量,不重建全量索引。

容量规划是上线前必做的功课。服务能扛多少并发,取决于延迟和线程数,核心关系是利特尔定律:

如果平均延迟 50ms、目标 QPS 1000,就需要约 50 个并发请求同时在途。另外要做好预案:高峰时段降级直接返回热门榜兜底,对超时请求快速失败,保证系统不雪崩。

值班到晚上,小周看着监控面板上稳定的绿线松了口气。老馆长拍拍他:“记住,召回这班岗,慢一毫秒读者都可能走人。架构的意义就是让'找书'这件事,快得像本能。”

并发数=QPS×Latencyavg\text{并发数} = \text{QPS} \times \text{Latency}_{\text{avg}}
  1. QPS 指标QPS 是每秒查询数,相当于前台一秒接入多少读者。
    QPS\text{QPS}
  2. 平均延迟$\text{Latency}_{\text{avg}}$ 以秒为单位,指一次查询从发出到返回的平均耗时。
    Latencyavg\text{Latency}_{\text{avg}}
  3. 并发数关系并发数 = QPS × 平均延迟,表示同一时刻服务中在途的请求数。
    并发数=QPS×Latencyavg\text{并发数} = \text{QPS} \times \text{Latency}_{\text{avg}}

召回评估

一周过去,老馆长拿来一张“读者满意度统计表”:本周推荐的书,读者点开借走多少?想找的书被找回来几成?小周发现,评价召回做得好不好,不能只看感觉,要有一串数字。老馆长说:“指标就是体检报告,哪一项红了,就往哪个方向调理。”

离线评估最常见的是召回率(Recall@K):在用户真实交互过的正样本中,有多少被候选集覆盖了。但光看召回率不够,还要看命中率(有多少用户至少命中一个候选)、覆盖率(推荐是否只集中在爆款)、以及延迟(全链路耗时是否达标)。

召回率公式:

$G_u$ 是用户真实喜欢的物品集合,$R_u(K)$ 是系统召回的前 $K$ 个候选。如果用户真正喜欢 10 本书,系统召回 100 个候选里包含了其中 5 本,召回率就是 50%。实践中还要结合分位数看延迟(p50/p95),以及做 A/B 实验观察线上点击率、停留时长等业务指标。

小周看着统计表若有所思:“召回率是'想找的书找回来几成',命中率是'多少读者没白等',覆盖率是'推荐别总盯着畅销榜'。指标和数据,才是召回系统最诚实的老师。”

Recall@K=∣Gu∩Ru(K)∣∣Gu∣\text{Recall@}K = \frac{|G_u \cap R_u(K)|}{|G_u|}
  1. 真实正样本集$G_u$ 是用户 $u$ 真实交互过的物品集合,比如实际借过的书。
    GuG_u
  2. 召回候选集$R_u(K)$ 是系统为 $u$ 返回的前 $K$ 个候选物品集合。
    Ru(K)R_u(K)
  3. 求交集$G_u \cap R_u(K)$ 表示“系统找到了哪些读者真的喜欢的书”。
    Gu∩Ru(K)G_u \cap R_u(K)
  4. 集合元素数$|\cdot|$ 表示集合里元素的个数,即数一数里面有多少本书。
    ∣Gu∣|G_u|
  5. 比例归一交集大小除以正样本总数,得到 0~1 的召回率,越高说明找得越全。
    ∣Gu∩Ru(K)∣∣Gu∣\frac{|G_u \cap R_u(K)|}{|G_u|}

AI 应用场景

电商首页“猜你喜欢”的亿级候选召回

阶段:候选召回

解决的问题:电商平台上架商品数以千万计,排序模型无法逐一精算。传统的类目+关键词召回覆盖面窄、个性化弱。向量召回将用户行为序列编码成用户向量、商品图文特征编码成商品向量,请求到来时在毫秒级从千万商品中筛出几百个候选,再交给排序模型精排。这就像图书馆先圈出几十本“可能对口”的书,而不是把整栋楼搬给读者挑。

具体案例:某电商 App 的用户浏览了 5 双运动鞋后,双塔模型实时算出用户向量,Faiss 在 2000 万商品向量索引中 5ms 内返回 Top200 候选,其中既包含相似款跑鞋,也包含搭配的袜子、运动裤,点击率相比老规则召回提升 18%。

短视频搜索的语义召回

阶段:召回(query 与视频双向编码)

解决的问题:用户搜“减压”时,传统文本匹配召回不到无字幕、无文本标签的视频。用向量召回把 query 和视频(画面特征+音频特征)编码到同一空间,语义相关的视频即使没有文字也能被找到。这相当于给视频也办了“性格坐标卡”,按性格找而不是按书名找。

具体案例:某短视频平台搭建双塔语义召回,query 塔输入搜索词,视频塔输入多模态特征,线上命中率提升 30%,长尾视频获得更多分发机会。

在线音乐的相似歌曲推荐

阶段:item2item 召回

解决的问题:用户在听歌时想找“更多类似歌曲”,用歌曲 Embedding 做最近邻召回,比人工打标签覆盖面更大、风格更细腻。人工标签只能分到“民谣”“摇滚”这种粗粒度,向量空间却能捕捉“带点爵士味的民谣”这种微妙气质。

具体案例:某音乐 App 用歌向量索引 + HNSW 检索,用户播放一首民谣后,50ms 内返回同风格 50 首候选,其中大量是标签系统从未覆盖过的小众曲目,歌曲收藏率提升 12%。

Python 演示

python49 行
import numpy as np  def brute_force(query, items, k=10):    """暴力检索: 全量算内积, 返回 TopK 下标"""    sims = items @ query    return np.argsort(sims)[::-1][:k]  def build_ivf(items, nlist=32, iters=15):    """建索引: 用简化 k-means 把物品分成 nlist 个簇"""    rng = np.random.default_rng(42)    n = len(items)    centers = items[rng.choice(n, nlist, replace=False)]    labels = np.zeros(n, dtype=int)    for _ in range(iters):        dists = np.linalg.norm(items[:, None, :] - centers[None, :, :], axis=2)        labels = dists.argmin(axis=1)        for c in range(nlist):            members = items[labels == c]            if members.size > 0:                centers[c] = members.mean(axis=0)    return centers, labels  def ivf_search(query, items, centers, labels, nprobe=2, k=10):    """近似检索: 只看最近的 nprobe 个簇, 返回 TopK 下标"""    d = np.linalg.norm(centers - query, axis=1)    cells = np.argsort(d)[:nprobe]    cand = np.where(np.isin(labels, cells))[0]    sims = items[cand] @ query    return cand[np.argsort(sims)[::-1][:k]]  np.random.seed(0)items = np.random.randn(20000, 32)query = np.random.randn(32) exact = brute_force(query, items, k=10)centers, labels = build_ivf(items, nlist=32)approx = ivf_search(query, items, centers, labels, nprobe=2, k=10) hit = len(set(exact) & set(approx))scanned = int(np.sum(np.isin(labels, np.argsort(    np.linalg.norm(centers - query, axis=1))[:2])))print("暴力检索 Top10:", exact)print("IVF 检索 Top10:", approx)print("召回重合:", hit, "条, 召回率:", hit / 10)print("扫描向量数: 暴力=20000, IVF=", scanned)
目的:用 NumPy 手写一个最简“向量召回”:先暴力检索出 Top10 作为正确答案,再用 k-means 构建 IVF 倒排索引做近似检索,对比两者的召回重叠率和扫描向量数,直观感受 ANN 为什么快。
  1. sims = items @ query一次矩阵乘算出用户向量与所有物品向量的内积,等价于算出所有相似度。
  2. dists.argmin(axis=1)把每个物品归到最近的簇中心,完成“分片区”。
  3. members.mean(axis=0)重新计算簇中心,k-means 迭代的核心一步。
  4. cells = np.argsort(d)[:nprobe]查询时只挑最近的 nprobe 个簇,决定只搜哪些片区。
  5. cand = np.where(np.isin(labels, cells))[0]取出这些片区内的物品下标,候选量骤减。
  6. len(set(exact) & set(approx))用集合交集数计算近似检索与精确检索的重合程度,即召回率。

常见误区

误区我以为向量召回就是把用户向量和所有物品向量逐一算相似度取 TopK。一亿个向量也就一会儿,慢点没事。

正解一亿个 128 维向量全量算一次内积,在 CPU 上要几百毫秒,完全无法支撑线上 QPS。真实的向量召回靠的是预先构建的 ANN 索引:Faiss 把向量分区、量化、建图,查询时只访问一小部分,把延迟压到几毫秒。全量算只是暴力检索基准,不是线上方案。

为何常见:小数据集 demo 里全量算完全跑得动,容易把“能跑通”当成“能上线”,忽略了规模放大后的计算墙。

误区维度越高信息越丰富,模型效果一定越好,所以我直接把 Embedding 设成 1024。

正解维度增加带来内存和计算开销线性上升:一亿个 1024 维 float 向量需要约 400GB 内存。而且高维度容易过拟合,训练也更难收敛。工业界常用 32~128 维,按数据量和任务复杂度选择,通常先小后大,收益不明显就停下来。

为何常见:“更多的信息 = 更好的效果”听起来很合理,忽略了工程成本和边际收益递减。

误区反正模型主要从正样本学习,负样本只是公式里的分母,随机抽几条凑数就行。

正解双塔是判别式模型,“像但不对”的负例才是它学会细分的教材。全随机负样本只能让模型分清“科幻”和“菜谱”,遇到“科幻和太空歌剧”这种近邻就懵。加入困难负样本(相似但用户不喜欢的物品)能逼模型学习细粒度边界,采样比例通常随机与困难按 5:1 到 20:1 之间调优。

为何常见:“正确答案教得好就够”是应试教育直觉,忽略了判别式模型必须在正反对照中学习。

误区评估只看 Recall@K,追求 100%,觉得召回率最高就是最牛的系统。

正解召回率只是输入排序阶段的“候选池”质量。召回率往 100% 冲,通常意味着候选集膨胀,排序压力、存储成本、延迟全线上涨,而业务收益可能早已饱和。工业界综合召回率、命中率、覆盖率、p95 延迟和线上业务指标一起看,目标是“够用”而非“最高”。

为何常见:单一指标 KPI 的思维惯性,忽略了推荐系统是端到端协作的整体,局部最优不等于全局最优。

面试题

Q1. 向量召回的核心思想是什么?

单选题 · easy

  • A.编码成向量后检索最近邻居← 正确答案
  • B.用关键词精确匹配数据库
  • C.对全部物品做全量精排
  • D.用规则人工筛选出候选

答案:A。向量召回把用户和物品编码到同一向量空间,用最近邻检索筛候选。B 是传统关键词检索,C 是排序阶段,D 是规则召回,都不属于向量召回的“编码+近邻”范式。

Q2. 双塔模型中用户塔与物品塔的关系是?

单选题 · mid

  • A.各自编码到同一个向量空间← 正确答案
  • B.用户塔的输出输入物品塔
  • C.两塔共享完全相同的权重
  • D.物品塔只做离线阶段训练

答案:A。双塔的核心是两塔分别编码、共享向量空间、内积打分。B 会把双塔退化成串行结构,C 会让两塔输出恒等失去意义,D 错误——物品塔在离线训练,但线上也要参与推理。

Q3. 训练双塔模型时为什么要做负采样?

单选题 · mid

  • A.需要反例让模型学会区分好坏← 正确答案
  • B.为了减少正样本的训练数量
  • C.因为负样本太多根本存不下来
  • D.为了缩短离线训练总时长

答案:A。负采样提供“对比项”,模型在正负对比中学习边界,这也是 sampled softmax 损失的原理。B 与正样本无关;C 陈述了现象但没回答“为什么需要”;D 只是附带好处,不是目的。

Q4. 下列哪一项最符合“困难负样本”?

单选题 · mid

  • A.随机抽取的完全不相关物品
  • B.相似但用户无交互的物品← 正确答案
  • C.全站点击量最高的爆款商品
  • D.用户历史点击过的热门商品

答案:B。困难负样本是“像正样本但用户不感兴趣”的物品,能逼模型学细粒度差异。A 是普通随机负样本;C 高热度但未必相似;D 甚至可能是正样本。

Q5. ANN 近似最近邻检索相比暴力检索的核心特点是?

单选题 · easy

  • A.牺牲少量召回率换取高速度← 正确答案
  • B.需要消耗更多内存来存索引
  • C.每次返回的结果绝对精确
  • D.只支持低维向量的检索

答案:A。ANN 的本质是“近似”:通过分区、量化、图剪枝减少计算量,换来毫秒级延迟,代价是偶尔漏掉真近邻。B 未必成立(PQ 反而省内存),C 是暴力检索的特征,D 无依据。

Q6. 以下哪些属于主流的 ANN 索引方法?

多选题 · hard

  • A.倒排分区的 IVF 索引方法← 正确答案
  • B.乘积量化的 PQ 压缩方法← 正确答案
  • C.层级可达的 HNSW 图方法← 正确答案
  • D.顺序扫描的 FLAT 全量法

答案:ABC。IVF 分区、PQ 量化、HNSW 图是三种主流 ANN 方案。FLAT/IndexFlatIP 是暴力检索基准,不属于近似检索方法。

Q7. 在 Faiss 的 IVF 索引中增大 nprobe,效果是?

单选题 · hard

  • A.召回率提高而查询延迟增加← 正确答案
  • B.召回率降低但延迟也降低
  • C.召回率与延迟都保持不变
  • D.索引构建速度会显著变快

答案:A。nprobe 是查询时探测的分区数。探测越多,覆盖真近邻的概率越大,召回率越高,但扫描的向量也越多,延迟上升。nprobe 不参与建索引,D 错误。

Q8. 在线召回服务中查询延迟常用什么量级衡量?

单选题 · easy

  • A.十毫秒到百毫秒级的快速响应← 正确答案
  • B.秒级响应就满足大部分场景
  • C.小时级批量处理完全足够
  • D.天级任务离线跑完即可

答案:A。在线召回是用户请求链路的第一个环节,通常要求 p95 在几十毫秒内,为后续排序留出时间预算。秒级会让用户体感卡顿,小时/天级只适合离线批处理。

Q9. 在线向量召回服务常用哪些手段保障性能与稳定性?

多选题 · hard

  • A.索引常驻内存避免磁盘访问← 正确答案
  • B.对高频查询结果做结果缓存← 正确答案
  • C.高峰时段启用降级返回热榜← 正确答案
  • D.每次请求重新构建最新索引

答案:ABC。索引预热进内存、结果缓存、降级兜底是工业界标配。D 建索引是重操作,毫秒级请求不可能实时重建,通常用增量更新加周期性全量重建。

Q10. 评价向量召回质量常用的离线指标有哪些?

多选题 · mid

  • A.召回率 Recall@K← 正确答案
  • B.命中率 HitRate@K← 正确答案
  • C.覆盖率 Coverage← 正确答案
  • D.用户画像完整度

答案:ABC。Recall@K、HitRate@K、Coverage 都是标准召回评估指标,同时还要关注延迟分位数。D 是数据质量指标,不属于召回效果评估。