ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Embedding 技术从原理到工程落地:向量检索与模型选型实战指南

Embedding 技术从原理到工程落地:向量检索与模型选型实战指南

Embedding 技术这几年几乎成了AI落地的标配,不管你做的是语义搜索、推荐系统、知识图谱还是 RAG 问答,最后都绕不开把文字、图片、用户行为变成一串向量。很多刚入门的朋友一听到“Embedding”就头大,觉得这是数学和算法岗才需要搞明白的东西,其实它的核心逻辑非常简单:把现实世界里的对象映射到一个低维稠密空间,用坐标表示语义。这篇文章我会从最基本的概念开始讲,一直讲到真实的工程落地、向量检索、模型选型和踩坑经验,全程不贴数学公式推导,尽量用大白话把“为什么这么做”讲清楚。

无论你是只写过几行 Python 的初学者,还是已经在业务里对接过文本 Embedding API 的开发者,这篇文章都会帮你把这些零散的知识串成一条线。看完之后,你至少能搞明白三件事:Embedding 模型到底在学什么、向量检索里的参数应该如何选择和调优、以及一套完整的 Embedding 应用系统在真实业务里会遇到哪些问题。

1. 先从最朴素的问题说起:Embedding 到底是在做什么

1.1 让计算机理解“苹果”和“水果”的关系

我经常跟朋友打一个比方:Embedding 其实就是给每个对象发一张“身份证”,只不过这张身份证上的信息不是姓名和住址,而是一串数字坐标。比如“苹果”这个词,在向量空间里大概会在水果类词汇聚集的区域;“苹果手机”则会在数码产品的区域附近。计算机不识字,但它懂坐标,所以只要把词汇、句子、图片变成坐标,它就能“理解”语义之间的远近。

这里有一个容易被忽略的核心点:Embedding 不是一个固定的技术,而是一类方法的统称。早期的 one-hot 编码也是把词变成向量,但它是个“高维稀疏”的向量,比如词表里有 5 万个词,每个词就是一个 5 万维的向量,只有自己对应的那个位置是 1,其余全是 0。这种表示方式有两个致命伤:维度爆炸和无法表达语义相似度。“苹果”和“水果”在 one-hot 空间里没有任何关系,距离永远是 1.414。

而 Embedding 的本质是让模型根据上下文学习每个对象应该落在空间哪个位置,所以它学出来的是一个低维稠密向量,像 128 维、768 维、1536 维。同样的语义空间里,“苹果”和“水果”的距离很近,“苹果”和“榴莲”也不远,但“苹果”和“业绩下滑”就隔着十万八千里了。这个“距离远近”就是后续所有应用的基础。

1.2 从词到句子、图片和用户行为:万物皆可向量化

很多人以为 Embedding 只是处理文本的技术,其实不是。俗话说“万物皆可 Embedding”,这在工程上是真的成立。你可以把一套完整的商品信息(标题+描述+类目)拼成一段文本然后生成向量,也可以把用户过去一周的点击行为序列(一串 item id)转成向量,甚至可以把图片切块之后用视觉模型生成向量再进行比对。

理解这一点对做系统设计特别重要。比如你要做一个“找相似商品”的功能,最直接的做法是拿商品标题和描述算向量相似度;但如果你想做的是“推荐用户可能感兴趣的商品”,那就不是文本向量的活儿了,而是用户行为序列向量与商品向量的交互问题。所以一个成熟的项目里,可能会同时存在多个 Embedding 模型和多种向量表。

我见过很多团队把“Embedding”和“深度模型”混为一谈,实际上 Embedding 只是一个表示学习的结果,它背后的训练方式可以是 Word2Vec、BERT、双塔模型、多模态模型等等,差别非常大。这也是为什么我总跟人说:选 Embedding 方案前,先想清楚你的数据是什么形态、目标是什么,不要上来就套一个大模型 API。

2. Embedding 是怎么训练出来的:几种主流模型思路拆解

2.1 从 Word2Vec 到 BERT:上下文决定一切

讲到 Embedding 训练,绕不开 Word2Vec。它的思路极其巧妙:让模型根据一个词的上下文来预测这个词本身(或者反过来),训练完毕后,把模型中间层的权重拿出来作为词向量。你没有看错,我们用的词向量其实是模型的“副产品”,不是专门设计出来的。

后来 BERT 的思路又进了一步:不再用词窗口这种局部信息,而是让模型遮住句子中 15% 的词,通过整句话的上下文去预测被遮住的词。这样做出来的 Embedding 不单单包含词的语义,还包含它在特定语境下的角色。比如“苹果”出现在水果描述里和出现在科技新闻里,它的向量是不同的,这是上下文相关的强大之处。

但 BERT 类模型也有明显的代价:计算量大、推理速度慢、维度通常很高(768 起步)。所以在工业场景里,很多人不会直接用 BERT 对每个词生成向量,而是会做“句子 Embedding”任务——把一整句或一整段文本压缩成一条高维向量,这就是 Sentence-BERT 这套思路要解决的问题。

2.2 双塔模型:推荐系统和向量检索的主流方案

双塔模型是另一个极其重要的流派。它把“左侧要检索的物料”(如商品、文章、视频)和“右侧用户或查询”(如搜索词、用户画像)分别送入两个独立的编码器,分别产出向量。训练目标就是让匹配的样本在向量空间里靠近、让不匹配的样本远离,用对比学习的方式去拉近正样本、推开负样本。

双塔模型之所以在业界这么流行,原因非常现实:线上服务时只需要把物品向量提前算好存进向量数据库,查询时只对输入侧做一次编码,不需要把每一条候选做一遍全量计算,这在超大规模候选集场景下几乎是唯一可行的方案。像你刷短视频时看到的“相似内容推荐”,背后基本都有双塔的影子。

要注意的是,双塔模型的效果上限被输入信息量限制住了。它要求你把所有有用的信息都拼进两侧的输入里,一旦漏掉某些关键特征,模型就学不到这个信息。这也是很多团队调了很久双塔模型效果上不去,最后发现是输入侧特征拼漏了的原因。做这个方向的朋友,建议先把特征工程做扎实,再谈模型结构优化。

2.3 对比学习与多模态 Embedding 的崛起

近两年对比学习可以说是 Embedding 领域的顶梁柱。最简单的对比学习就是:给你一个锚点样本,再从库里抽一个正样本和一堆负样本,让模型学会把锚点和正样本拉近、和负样本推远。这个范式在文本、图像、音视频甚至用户行为序列上都适用,而且效果出奇地好。

多模态 Embedding 就是在这个范式下发展的,典型做法是让同一个空间里同时容纳文本向量和图片向量。比如你搜“红色连衣裙”,模型会把文本映射到空间的某个位置,再把全网的商品图片也映射到同一个空间里,这样就能直接比对文本和图片的相似度了。这种方案解决了传统搜索里“图片匹配图片、文本匹配文本”的割裂问题。

不过这里有一个我反复强调的坑:多模态模型产出的向量空间和单模态模型的向量空间是不能直接混用的。意思是,你不能拿一个纯文本模型生成的商品向量,去和多模态模型生成的图片向量算余弦相似度,因为它们的空间定义不同。要么全链路统一用同一个模型,要么只做同源向量的比对,这一点必须写进团队的规范文档。

3. 向量检索的核心原理:相似度计算、维度选择与索引

3.1 余弦相似度、欧氏距离和内积,到底应该选哪个

拿到向量之后,怎么判断两个对象“像不像”?最基本的手段就是算距离或者相似度。业界最常用的是余弦相似度,它只关注方向、不关心模长,适合语义相似度任务。欧氏距离则同时考虑方向和模长,适合那些模长本身就有意义的情况,比如用户行为向量。点积适合当你有交互行为数据时用,比如用户向量和物品向量的点击匹配度。

这三种度量不是拍脑袋选的,跟你模型训练时候用的损失函数直接相关。如果你的模型是用余弦相似度训练出来的,那你线上也得用余弦相似度打分,保持一致性比选“最优度量”还要重要。混用度量可能不会报错,但分数分布和你模型学习时看到的期望分布不一致,表现会打折。

我见过一个实际案例:某团队用双塔模型训练推荐向量,训练时用的是内积相似度,线上却习惯性地计算了余弦相似度,结果 TopK 推荐结果明显偏向了高热度商品,因为内积更看重模长大、频次高的物品,换成余弦之后这个信号被削弱了。这个例子说明,度量的选择是贯穿训练和推理的,不是线上单独决定的。

3.2 维度怎么定:为什么要 768,为什么不能用 16 维

新人对 Embedding 维度的理解最容易走极端:要么觉得维度越高越好,要么觉得越低越省资源。实际上这是一个在表达能力和计算成本之间做权衡的问题。过低的维度会丢失语义信息——你想用 20 维去区分 10 万种商品,空间根本装不下这么多语义关系;过高的维度则带来索引膨胀和检索退化的风险。

这里有一个重要的经验:在向量检索里高维并不总是更好。FLANN、HNSW 这类近似最近邻算法在高维空间里的检索效率会明显下降,因为“维度灾难”让距离区分度变差。768 维虽然准确,但如果你有 1 亿条数据,全量精确计算是不现实的,需要靠索引换精度。而 128 维或者 256 维在多数场景下已经够用,还能省下大量的存储和带宽。

什么样的场景适合多少维度,我列一个参考表,这是我自己在多个项目里实践出来的经验值,不保证绝对最优,但作为起点很稳:

场景建议维度理由
粗粒度文本分类/聚类64~128语义区分度要求不高,追求速度
语义搜索/问答匹配256~768需要精细的语义表达、可接受更高延迟
推荐系统用户/物料向量128~256权衡了精度、存储和线上延迟
多模态图文检索512~768模态对齐需要足够容量,但要看具体模型

3.3 从暴力搜索到 HNSW:关系数据库不够用了

一旦你的向量数据量上去,最简单的做法就是全量暴力计算相似度,“拿一个查询向量去和库里每个向量做点积、排序,取 TopK”。在小数据集上没问题,但数据量到百万级、千万级之后,单次查询的代价直线上升。这时候就需要专门的向量索引技术,比如 HNSW、IVF、PQ 等。

HNSW 是目前最流行的图式索引,基本思路是构建一张“分层小世界图”,检索时从高层粗粒度出发、逐层细化,用跳表一样的多层思想压缩搜索路径。它的优点是召回率高、参数好调,缺点是构建内存占用偏大。IVF 则是“先把向量聚类,只检索最近的几个簇”,召回有所下降但非常省内存。PQ 更是用来压缩向量本身,用子空间量化把高维向量变成短码,大幅降低存储成本。

选索引这件事,不能单看检索速度,还得看数据更新频率。如果你的向量库每天有几百万条增量更新,HNSW 的在线插入性能就很重要;如果你的向量几乎不变,而查询量巨大,那用 IVF 配合量化反而更划算。我建议:方案一,直接在开源向量数据库里用 HNSW 起步,跑通业务后再做索引优化,不要一上来就自研。

4. 工程落地实战:一个语义搜索系统从零到一

4.1 项目背景与整体架构设计

为了把前面讲的原理落到实地上,我分享一下我之前做的一个模拟项目X:某电商平台希望实现“以图搜商品”和“语义搜索”两条新链路。原来的搜索只支持关键词精确匹配,用户搜“适合跑步穿的轻便鞋子”时系统识别不了“轻便”和“跑步”这些概念,用户体验很不好。

整个系统的架构一句话概括就是:离线把商品数据向量化进库,在线把用户查询向量化然后检索 TopK,再由上层的精排模型做最终排序。注意,“检索”和“排序”是两件事,向量检索只负责用高效率捞回几百个候选,真正的点击率和转化率排序还需要额外的精排模型,这个认知对做系统设计的同学来说极其重要。

4.2 数据准备与向量化流程

数据准备阶段是整个项目最耗时但最容易被低估的一步。我们当时把商品标题、类目名称、品牌、若干卖点标签拼成了一个正文字段,再用一个多模态文本模型生成 256 维向量。为什么用 256 而不是 768?因为我们的商品量有几百万条,要在可接受的成本范围内跑百亿级相似度计算,256 维在我们实测中的效果已经足够好,性价比最高。

在线查询侧的处理则复杂一些:用户输入的搜索词,我们要先做分词、拼写纠错、同义词扩展,把“轻便的跑步鞋”扩展成“轻便 跑步鞋 运动鞋 透气鞋”等多个候选短语,再分别生成向量,和商品向量做加权融合。这里有个细节:用户查询词短,和商品文本的向量空间本身有分布偏移,所以最好在查询词上多一些人工标注和增强,不然直出的向量检索很容易“召回不准”。

我强烈建议在向量化之前先做个数据质量检查。我们当时清洗了一轮无意义文本、重复商品和纯标点符号的数据,看起来不起眼,但对质量指标的影响比换模型还大。数据决定上限,模型只是尽可能逼近这个上限。

4.3 向量库选型与索引配置

在向量数据库选型上,我测过好几款开源方案,最终选了社区生态比较活跃的一款,因为它既支持 HNSW 索引,又有 SQL 风格的接口,团队上手成本低。存储方面,我们会在一个库里同时存两部分数据:一部分是商品向量,另一部分是商品元信息(价格、库存、类目),这样查询后直接能拿到展示所需的字段,不需要再回源查一遍。

索引参数上我最关注三个:M(每个节点的最大连接数)、efConstruction(构建时的搜索宽度)、efSearch(查询时的搜索宽度)。我们最终卡在 M=24、efConstruction=100、efSearch=80 上,召回率在验证集上超过 98%,单次查询延迟约 5 毫秒。合理的范围一般是 M 在 12-48 之间、efConstruction 在 80-200 之间,具体看数据的聚类难度。

集群部署方面,我们的写入吞吐约每秒 3000 条,单机接近瓶颈,就做了分片。分片的关键是按相似度语义空间切分,而不能随机分片,否则同样的查询会被发到所有分片,性能提升会打折扣。我们用了一个比较工程化的做法:先训练一个聚类模型,把商品向量分成若干个语义簇,每台机器负责几个簇,查询时只发给最近的几个分片。实测这个方案比随机分片快了三倍多。

4.4 评测体系:不能只靠“感觉好用”

在语义搜索项目上线之前,我们搭了一套评测体系,包括三部分:召回率、排序质量、业务指标。召回率靠人工标注的相似商品对来算,看预期的相似物品中有多少出现在 TopK 里;排序质量用 NDCG 这类指标衡量;业务指标则是线上 AB 测试里的点击率和转化率。很多人只会看线上指标,没有离线评测,导致出问题之后根本定位不清是模型的问题还是数据的问题。

我们还对向量检索的结果做了很多“bad case 抽样”,每星期找几个失败的搜索词,看它们的结果哪一步出了问题。我发现了一个很普遍的规律:大多数 bad case 不是因为向量质量差,而是因为输入文本太短或者包含歧义,比如“苹果”到底是水果还是手机。这提醒我们,Embedding 系统不是一个模型的问题,而是从数据清洗、文本增强到检索排序的全链路问题。完善的评测反馈机制才是项目稳定迭代的护城河。

5. 常见问题与排查技巧实录

5.1 本地效果很好,上线效果崩了怎么办

这个现象几乎每个向量检索项目都会遇到。最常见的原因是训练数据分布和线上实际数据分布不一致。比如你用商品历史标题训练的模型,但线上用户开始输入口语化长句,模型就没见过这种输入风格。解决办法是定期收集线上用户的失败查询,扩充训练集或者做在线增强。

还有一个隐蔽的问题:量化压缩带来的精度损失。很多团队为了省内存上了 PQ,结果召回指标全面下降。我之前有个项目,向量维度 768,用 PQ 压缩到 32 字节之后,召回率直接降了 3 个百分点。解决方式是上线前对压缩后的索引做一期完整的召回评测,不要只看压缩率有多漂亮。精确度和压缩比永远是矛盾,需要在两者之间找一个可以接受的中点。

5.2 检索速度慢,如何从参数到架构做分层优化

速度上不去的话,优先检查三件事:第一,索引的 efSearch 参数是不是太大,这个参数越大检索越慢但召回越高,建议从小往大调;第二,向量维度是不是过高,很多场景把 768 降到 256 之后速度明显提升,效果几乎无损;第三,数据有没有做分类过滤,比如线上按类目预筛,只检索用户感兴趣的几个类目,这样就能把有效搜索空间缩小好几倍。

如果这些手段用完之后还是慢,就要考虑架构层面的方案了。一是做缓存,把高频查询的搜索结果做 Redis 缓存,能省掉大量重复计算;二是做层级检索,先用轻量模型做一个粗排筛选,把候选降到几千条,再在这个范围里用大模型精排;三是对长尾查询做降级处理,避免让最强的模型处理所有流量,长尾走轻量链路就够了。

5.3 向量模型怎么选型:开源私有部署还是调用 API

关于“开源模型自己部署”还是“调用现成 API”,我一贯的立场是:看你的数据敏感性和预算。如果你的业务数据涉及用户隐私,强烈建议私有化部署开源模型,哪怕效果稍逊,也要守住数据安全底线。如果只是做 Demo 或者效果验证,先调 API 快速打通链路是完全没有问题的,这也是成本最低的验证路径。

选开源模型的时候,还是要看训练数据和你的业务场景是不是匹配。通用领域的中文语义模型放在电商场景里,效果肯定不如在电商语料上专门微调过的模型。如果你的数据量足够大,建议在预训练模型基础上再做几步领域自适应微调,这一步带来的增益通常比换更大的模版模型还高。

最后说一个很实用的心得:Embedding 项目不要追求一步到位。先把一个环节做顺,比如先跑通“文本到向量到检索”的最小闭环,上线观测几天,再去加多模态、加用户行为序列、加精排模型。我见过不少团队一开始就想搭一个完美的多模态千人千面系统,最后卡在某个模型选型上两三个月出不了成果。从小闭环起步,每次只改一个变量,这样你永远都知道现在的效果是被什么因素影响的。这个原则,我在所有向量项目里都验证过,屡试不爽。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进