想象你搬进了一座巨大的城市,城市里没有街道编号,却有几十万人和物。你想找一个“性格相似”的邻居,最笨的办法是挨家挨户敲门聊天;聪明一点的办法是先看“街区”,再在街区里挑;更聪明的办法是借助你认识的那几个“远方熟人”,让他们带你抄近道。这里的“城市”就是数据世界,“性格名片”就是 Embedding——把一句话、一张图、一段用户行为,都浓缩成一串坐标数字。坐标越接近,语义就越相似。这篇文章会从“名片怎么画”讲到“找人怎么找得快”,把 Embedding 表示学习的基础脉络讲清楚。
对 AI 从业者来说,Embedding 是连接语义理解与工业级检索的“通用语言”。掌握它,才能设计出真正能落地的高效搜索、推荐与问答系统。
目录
可视化演示
展示HNSW分层图导航搜索路径、向量空间余弦相似度最近邻检索、HNSW/IVF/PQ/LSH四种ANN算法召回率与延迟权衡对比。
本节配有交互动效(进入视口后自动加载,约 3KB)。
HNSW分层导航搜索 顶层稀疏图快速定位入口节点逐层下降 底层密集图贪心搜索找到最近邻 O(logN)搜索复杂度 HNSW通过分层小世界图实现亚线性搜索
稠密向量:给万物发城市坐标卡
城市里原本每个人身上都挂着一张“临时工牌”,牌子上一共有几万个小灯,只有自己对应的那盏灯亮着,其余全灭。这种“词表大小等于灯的数量”的表示就是 one-hot 稀疏向量:简单直接,却没有任何语义信息。人们看到“猫”和“猫科动物”,发现两盏灯离了十万八千里,根本不知道它们是亲戚。Embedding 要做的,是把工牌换成一张低维的“性格坐标卡”,把每个概念映射到 D 个连续数值上。
这张坐标卡就是稠密向量。它不再关心概念在词表里的位置,而关心概念在语义空间中的位置。相似的概念,例如“苹果”和“橘子”,会被画在同一片街区;不相关的“苹果”和“火箭”,则相距很远。于是,判断语义相似度就变成了判断坐标远近的数学问题,最常用的尺子是余弦相似度。
回到城市比喻:给每个人发坐标卡之前,大家靠一张亮灯的工牌找人,效率极低;发坐标卡之后,城市里每一条街都有了语义含义。坐标卡的设计,就是 Embedding 表示学习要解决的核心问题:如何画出这张卡,让相似的人恰好住在附近。
- 分子点积两个向量对应位置相乘后求和,度量它们在每个维度上是否同时“有感觉”。
- 分母模长把每个向量的长度归一化,避免“话多的人”天然占便宜。
- 整体含义夹角越小,余弦值越接近 1,表示两个实体越相似。
Word2Vec:从邻里聊天学词义
有了坐标卡,下一个问题是谁来填写坐标。早期一种聪明的做法是“看邻居”:一个词的含义,可以由它经常一起出现的词决定。比如你总在“喝”、“热”、“提神”附近看到“咖啡”,不用别人解释,你也能猜出咖啡大概是什么。Word2Vec 正是把这种邻里观察变成训练目标。它有两种经典姿势:CBOW 用周围的词预测中心词,Skip-gram 用中心词预测周围的词。
这两种方式本质上都在做同一件事:让出现在相似上下文中的词,学到相似向量。训练时,词表可能非常大,每次都要把所有词的得分加起来做归一化,计算量难以承受。于是人们引入负采样:随机抽几个不在上下文中的词当“噪音邻居”,只让模型学会区分“真邻居”和“假邻居”,从而把训练速度提上来。
不过,Word2Vec 只负责单张“单词名片”。回到城市里,它虽然知道“咖啡”和“茶馆”很近,却不知道“这家店晚上七点还营业”这样的整句含义。想描述一整句话甚至一段对话,我们需要把散落的单词名片合成一张“家庭合影”,这就是句子级编码。
- 中心词名片$w_t$ 是当前中心词,向量 $\mathbf{v}_{w_t}$ 是它的坐标卡。
- 上下文词得分用点积计算中心词与候选上下文词的相似度,得分越高越像“真邻居”。
- 概率归一化用 softmax 把得分变成概率,分母遍历词表所有词 $V$。
句子级编码:从单词名片到家庭合影
单词向量擅长描述一个词,但句子是一个有顺序、有结构的家庭。把“我喜欢小狗”和“小狗喜欢我”中每个词向量简单相加,得到的结果可能几乎一样,这显然不合理。Sentence-BERT 类模型的做法是:用两个共享参数的 BERT 编码器分别处理句子,得到每个词的隐藏状态后,再通过池化策略合成一个固定长度的句向量。
这里的池化策略有点像“给家庭合影选代表”。最常用的 mean pooling 是把所有家庭成员的位置平均一下,得到家庭中心;也有人用 [CLS] 标志位或 max pooling。实践里 mean pooling 通常最稳定,因为它不放过任何一个人的信息。
在城市里,Word2Vec 是单人名片,Sentence-BERT 则是家庭合影。双塔模型的另一个好处是:两条句子可以分别编码成向量后,再做点积或余弦计算。这样就能提前把库里所有句子向量化,查询时只需要算一次新句子的向量,再去城市里找邻居。
- 词级别隐藏向量$\mathbf{h}_i$ 是句子中第 $i$ 个词经过编码器后得到的向量。
- 求和把 $n$ 个词的隐藏向量逐维相加。
- 取平均除以句子长度 $n$,得到与长度无关的固定维度句向量。
对比学习:用相亲法则训练通用向量
有了词向量和句向量,怎么让它们更“通用”?对比学习给出了一套很像相亲配对的训练法则:给同一个句子制造两个不同视角的“正样本”,比如把原句稍微加噪声、遮掉几个词、做数据增强;同时把其他无关句子当作“负样本”。模型的任务是学会把正样本拉到身边,把负样本推远。
这套法则的核心是 InfoNCE 损失。公式里分母会把当前批次所有样本都当作负样本,温度系数 $\tau$ 则控制“眼光苛刻程度”。$\tau$ 小时,模型更看重那些最难分辨的负样本;$\tau$ 大时,所有样本的区分压力都会变小。$\tau$ 的设置直接影响学到向量的分布形态。
对比学习训练出来的向量,有两个理想性质:对齐和均匀性。对齐是指同类的正样本彼此靠近;均匀性是指所有向量尽量铺满整个空间,不要挤成一团。就像相亲现场,志同道合的人坐在一起,但每桌又不能拥挤到失去辨识度。这样的通用向量,才能成为下游检索与排序的坚实底座。
- 正样本得分$\mathbf{z}_i^+$ 是与锚点 $\mathbf{z}_i$ 配对的增强样本,相似度越大越好。
- 负样本总和分母把批次里其他样本 $k$ 的得分都算进来,相当于让模型在整条街上找人。
- 负对数似然把这个比值变成损失,值越小,说明正样本排在越前面。
检索加速:从敲门到社交导航
当城市里只有几十个人时,挨家挨户敲门很可靠;当数据量变成百万、千万,暴力扫描就慢得无法接受。暴力扫描也叫精确最近邻,必须把查询向量与库中每个向量都算一遍相似度。为了提速,工程师们开始给城市画分区、建人脉网,这类技术统称 ANN(近似最近邻)。
IVF 是“先分街区再搜”。它先用聚类把所有向量分成若干桶,查询时只选离查询最近的几个桶,把候选集缩小,再在这些候选里精确排序。HNSW 则更进一步,它像一张多层社交网络:顶层只有少数“社交达人”,底层才是全部住户。查询时先从顶层认识一位熟人,顺着关系链逐层下钻,每层都只走一小段路,就能很快摸到目标附近。
回到城市找人:暴力扫描是把每扇门都敲一遍;IVF 是你先确认目标大概在哪个街区,再只敲那几条街的门;HNSW 则是让你的老朋友先带你到附近,再一层层缩小范围。速度换来了,但代价是“近似”:有时候可能会漏掉真正的最近邻居。因此,召回率与延迟之间的权衡,成了向量检索系统设计的关键课题。
- 查询到聚类中心计算查询向量 $\mathbf{q}$ 与每个聚类中心的距离。
- 选出最近桶取距离最小的 $m$ 个桶,而不是遍历所有桶。
- 合并候选把这些桶里的向量合并成候选集合,再在候选集里计算精确相似度。
工程优化:控制城市规模与精度
向量做出来后,还要面对存储和计算成本。一个 128 维的 Float32 向量占 512 字节,一亿条就是 50 GB,再加上索引结构,规模非常惊人。工程上常见的三板斧是归一化、量化与维度取舍。
归一化把向量拉到单位球面上。这样做不仅让余弦距离和欧氏距离的排序等价,还能避免某些向量因为“太长”而主导相似度。量化则是把原本 4 字节的浮点数压成 1 字节整数,甚至用乘积量化压到更少,用损失一点点精度换取大幅存储下降。维度取舍则像城市规划:路太窄会堵车,路太多又会浪费土地,常见文本向量维度在 128 到 768 之间。
城市里的路、人和存储空间都有限。Embedding 工程优化的本质,是在“找得准”和“找得快”之间画一条可调曲线。理解了坐标卡如何生成、如何压缩、如何检索,你就能为自己的业务设计一套既懂语义又扛得住规模的搜索系统。
- 存储公式每个向量占用的比特数等于维度乘以每个维度使用的比特数。
- 量化步长$\Delta$ 是量化时每个档位代表的数值范围,档位越粗,误差越大。
- 误差上界每个维度的舍入误差不超过 $\Delta/2$,累加到 $D$ 维后误差随 $\sqrt{D}$ 增长。
AI 应用场景
企业知识库智能问答
阶段:语义召回与 RAG 知识检索
解决的问题:传统关键词搜索只能匹配字面词,员工问“年假没用完怎么办”,系统往往搜不到“未休年假折算”这类语义相近的文档。用 Sentence-BERT 把问题和文档都编码成向量后,再用 HNSW 索引召回 TopK,再交给大模型组织答案,就能实现理解语义的企业问答。
具体案例:某互联网公司把内部制度文档切成段落,离线生成 Embedding 并建好 HNSW 索引;在线问答时,先把用户问题编码,再通过向量检索召回最相关的 5 个段落。员工再也不用记术语,问口语也能找到制度原文。
电商以图搜图
阶段:多模态向量召回
解决的问题:用户看到一件衣服,想知道哪里买同款,但不能用文字描述清楚。把商品图片通过视觉编码器变成 Embedding,再把用户上传的图片编码到同一语义空间,就能用向量距离找到外观最像的商品。IVF 分区策略可以支撑千万级商品库的毫秒级响应。
具体案例:某电商平台上线“拍图找同款”,商品库约 3000 万条图片向量。系统先用 256 维 Embedding 表达图片,再用 IVF+HNSW 混合索引完成召回,最终把候选交给精排模型融合价格、销量信号。用户拍摄一张模糊照片也能搜出同款。
推荐系统向量召回
阶段:粗排召回
解决的问题:推荐系统要从百万商品里先筛出几百个候选,再交给复杂模型排序。直接对所有商品打分太慢,用用户行为序列编码成用户向量、商品属性编码成商品向量后,向量检索就成了最自然的召回方案。
具体案例:某视频平台用双塔模型把用户和视频映射到同一向量空间,用户每次刷新时算一次用户向量,再用 HNSW 索引快速找到几百个高相似视频,最后精排把短期兴趣和长期偏好融合起来。向量召回把候选规模从百万级缩到百级,延迟保持在 10ms 左右。
Python 演示
import numpy as npfrom collections import defaultdict rng = np.random.default_rng(42)N, D, K = 2000, 8, 10vectors = rng.normal(size=(N, D)).astype(np.float32)vectors /= np.linalg.norm(vectors, axis=1, keepdims=True) class BruteForceIndex: def __init__(self, vecs): self.vecs = vecs def query(self, q, topk=5): scores = self.vecs @ q return np.argsort(scores)[::-1][:topk] class IVFIndex: def __init__(self, vecs, nlist=10): self.vecs = vecs self.nlist = nlist self.centroids = vecs[rng.choice(len(vecs), nlist, replace=False)] self.inverted_lists = defaultdict(list) for i, v in enumerate(vecs): dists = ((self.centroids - v) ** 2).sum(1) self.inverted_lists[int(dists.argmin())].append(i) def query(self, q, topk=5, probes=2): dists = ((self.centroids - q) ** 2).sum(1) candidates = [] for c in np.argsort(dists)[:probes]: candidates.extend(self.inverted_lists[int(c)]) scores = self.vecs[np.array(candidates)] @ q order = np.argsort(scores)[::-1][:topk] return [candidates[i] for i in order] if __name__ == "__main__": q = vectors[42] print(BruteForceIndex(vectors).query(q, 5)) print(IVFIndex(vectors, K).query(q, 5, probes=2))
vectors /= np.linalg.norm(vectors, axis=1, keepdims=True)先归一化到单位球面,让相似度排序更干净。scores = self.vecs @ q暴力扫描用矩阵乘法一次性算完所有余弦得分。dists = ((self.centroids - v) ** 2).sum(1)训练时把每个向量分配到最近的聚类中心。self.inverted_lists[int(dists.argmin())].append(i)倒排列表记录每个桶里有哪些向量下标。for c in np.argsort(dists)[:probes]:查询时只挑最近的几个桶,这就是 IVF 的“探针”思想。candidates.extend(self.inverted_lists[int(c)])把选中桶里的所有下标合并成候选集。order = np.argsort(scores)[::-1][:topk]在候选集内部做精确排序,取前 TopK 返回。
常见误区
误区很多人觉得维度越大,能装下的语义信息越多,所以一次性把向量设成 1024 维甚至更高,效果一定更好。
正解维度太高会带来“维度灾难”:存储和计算成本线性增长,而小数据量下模型容易过拟合,检索时还会出现高维空间“距离趋同”的现象。就像城市里修太多路,司机反而容易迷路。文本向量的常见维度是 128、256、768,具体要根据数据规模、任务难度和硬件条件一起定。
为何常见:深度学习模型普遍“参数越多效果越好”的印象被错误迁移到了表示维度上,忽略了检索系统对存储和延迟的约束。
误区既然词向量能表示词义,那我把一个句子里所有词向量加在一起或取平均,不就能得到句向量了吗?
正解简单平均会丢失词序、否定关系和语义重点。“张三打了李四”和“李四打了张三”的词向量平均几乎一样。Sentence-BERT 这类模型会用整个句子的上下文交互来生成每个词表示,再配合池化,才能综合理解整句含义。平均词向量只能算一个非常粗糙的基线。
为何常见:Word2Vec 的词向量加和在某些简单任务上看起来有效,让人误以为这种“免费句向量”可以通用。
误区不少初学者以为 HNSW、IVF 这些加速工具只是跑得更快,返回的邻居和暴力搜索完全一样。
正解ANN 的本质是“用精度换速度”。它可能漏掉真正的最近邻居,也可能混入不相关结果,所以业务上通常用召回率、精确率配合延迟一起评估。就像让熟人指路,通常能到目的地,但偶尔也会走错街区。因此高价值场景常常再加一层精排兜底,而不是完全依赖 ANN。
为何常见:这些索引的名字里没有“近似”二字,使用者容易被“智能导航”的感觉误导,忽略了算法本身是有损的。
误区看到 FP32 变成 INT8,很多人第一反应是精度损失太大,向量搜索质量会崩掉。
正解对已经归一化的向量做 8 位标量量化,通常只会带来几个百分点的召回下降,却能把存储降到原来的四分之一。尤其在海量向量场景,量化后索引可以更多放进内存或显存,反而提升整体效果。真正需要关注的是量化前是否需要归一化,以及量化步长是否覆盖了数据分布。
为何常见:人们习惯用图像压缩类比向量压缩,忽略了检索任务对“相对顺序”更敏感,而顺序在量化后往往变化不大。
面试题
Q1. 关于稠密向量 Embedding,最准确的理解是?
- A.用几十万维稀疏向量表达语义
- B.用低维稠密向量表达语义关系← 正确答案
- C.只能给单词编码不能给句子
- D.必须与原始词表维度完全一样
答案:B。Embedding 的核心是把语义压缩到低维稠密向量中,让相似概念在向量空间里彼此靠近。one-hot 是稀疏向量,它无法表达语义;Embedding 也不只限于单词,图像、句子都能编码;维度与词表大小无关。
Q2. Skip-gram 模型的主要学习任务是?
- A.用上下文词预测中心词
- B.用中心词预测上下文词← 正确答案
- C.直接生成整段文字的向量
- D.对词表做矩阵分解降维
答案:B。Skip-gram 用中心词预测周围上下文词;反过来,用上下文词预测中心词是 CBOW。两者都属于 Word2Vec 的邻居观察法,目标是让共同出现的词获得相近的向量。
Q3. Sentence-BERT 与 Word2Vec 相比,最大优势是?
- A.能编码整句并学习句间关系← 正确答案
- B.不需要大规模预训练语料
- C.把每个汉字变成独立向量
- D.输出结果一定是单位向量
答案:A。Word2Vec 只能给单个词生成向量,而 Sentence-BERT 通过双塔编码器和池化得到整句向量,还能建模句子对之间的相似关系。它同样依赖大规模预训练,也不保证输出一定是单位向量。
Q4. HNSW 能加速向量检索的关键在于?
- A.用多层图结构逐层下钻← 正确答案
- B.把所有向量压缩成一位
- C.对全部候选做暴力排序
- D.用哈希表直接定位原始文本
答案:A。HNSW 的全称是“分层可导航小世界图”,它把向量组织成多层图:高层跳得远,低层走得细。查询时从顶层熟人开始逐层下钻,因此不用遍历所有向量。它不是哈希,也不是压缩,更不是暴力排序。
Q5. 对比学习中的“正样本”通常指?
- A.与锚点语义相同的数据← 正确答案
- B.与锚点向量完全相同的副本
- C.随机采样得到的噪声数据
- D.训练集中所有无标注数据
答案:A。对比学习通过数据增强构造同一个语义对象的多个视角,这些视角与锚点构成正样本,例如同一句话加噪声后的变体。随机噪声是负样本,所有无标注数据更不能都当成正样本。
Q6. 以下哪些因素会影响 ANN 检索的精度与效率平衡?
- A.候选集合大小与探针数量← 正确答案
- B.向量的量化位宽← 正确答案
- C.文本中标点符号的数量
- D.向量是否经过归一化← 正确答案
答案:ABD。IVF 的探针数决定桶的数量,候选集越大越准但越慢;量化位宽越少存储越低但误差越大;归一化影响距离度量的语义稳定性。文本标点数量是文本层面的属性,和向量索引结构没有直接关系。
Q7. 对比学习预训练中,为了让通用向量好用,通常关注哪些设计?
- A.构造困难的正负样本对← 正确答案
- B.调节温度系数控制难度← 正确答案
- C.直接把输出送到词表分类
- D.让同类对齐、异类均匀分布← 正确答案
答案:ABD。困难样本能逼模型学到更精细的边界;温度系数控制模型对难负样本的关注程度;对齐与均匀性是衡量向量分布质量的两个重要性质。直接做词表分类是预训练语言模型的做法,不是对比学习的目标。
Q8. 向量归一化之后,下列哪项关于欧氏距离的描述是正确的?
- A.与余弦相似度排序等价← 正确答案
- B.与点积相似度排序完全相反
- C.从此完全不会发生维度灾难
- D.所有向量模长都会变为零
答案:A。单位球面上,欧氏距离的平方等于 2 减两倍余弦相似度,所以两者排序等价。点积排序只有在所有向量模长都相同时才与余弦一致,“完全相反”显然不对;归一化也无法消除维度灾难,更不会让模长变为零。
Q9. 关于 Embedding 维度选择,合理的认识包括?
- A.维度太低会限制表达能力← 正确答案
- B.维度越高一定检索越准
- C.维度越高存储和计算越大← 正确答案
- D.768 维永远优于 128 维
答案:AC。维度太低,语义区分度不足;维度太高,存储与计算成本上升,而且小数据下容易过拟合。不能断言越高越准,也不能断言某个固定维度永远最好,768 和 128 各有适用场景。
Q10. 一个 128 维 Float32 向量被 8 位量化,单个向量存储从多少字节降到多少字节?
- A.从 512 字节降到 128 字节← 正确答案
- B.从 256 字节降到 64 字节
- C.从 1024 字节降到 256 字节
- D.从 128 字节降到 32 字节
答案:A。Float32 每个维度占 4 字节,128 维共 512 字节;8 位量化后每个维度占 1 字节,共 128 字节。因此存储量降到原来的四分之一。这也是海量向量系统中量化如此常见的原因。