速成课 · No. 32
计算机靠逐字比对文本,文字背后的意义对它而言是不可见的。embedding 修正了这一点——它把一段文本变成一串数字,变成空间中的一个点,并且放置得让意义相近的东西彼此靠近。于是「帮我找出与此类似的东西」一下子变成了「找出邻近的点」,而这是计算机瞬间就能解决的问题。它是搜索、RAG、推荐等许多东西背后那台安静的引擎。
只讲精髓 · 每个想法一个画面 · 让含义可计算
整个领域都建立在一个优雅的想法上:如果你能把意义放进空间,你就能对它进行计算。领会了这一点,embedding 便不再神秘。
计算机看到的是字符,而非意义
一台机器能逐像素地分辨出两张照片不同,却完全不知道它们都画着一只狗——表面有差异,意义却对它不可见。
对计算机来说,文本只是字符。它能检查两个字符串是否完全相同,但它天生就没有这种意识:「car」和「automobile」是同一个意思,或者「the bank approved my loan」和「my mortgage was accepted」说的是同一件事。逐字匹配会完全错过意义。要让软件处理文本所意味的东西,而不是它如何拼写,你需要一种把意义变成计算机能度量之物的方法。
embedding 把文本变成空间中的一个点
一张地图,每个想法都有一个位置,意义相近的想法彼此靠近——「dog」紧挨着「puppy」,两者都远离「tax return」。
embedding 是一串数字——一个 vector——它代表一段文本的意义,被安放在一个高维空间里,使得相近的意义落在彼此附近。一个模型读入文本,产出它的坐标。于是「dog」和「puppy」靠得很近,两者又都远离「spreadsheet」。原本对计算机不可见的意义,变成了一个位置——而位置正是软件能够比较、排序和检索的东西。
一旦意义有了坐标,你就能对它计算
一旦每座城镇都成了地图上的一个点,「找出最近的城镇」就变成了简单的几何——地图把一个含糊的问题变成了可度量的问题。
正是这一步让其余一切成为可能:当意义被表达为坐标,关于意义的模糊问题就变成了关于几何的精确问题。「哪些文档是关于这个主题的?」变成了「哪些点靠近这个点?」「这个和那个相似吗?」变成了「它们相隔多远?」计算机无法理解意义,但它能完美地度量距离——而 embedding 让它用度量距离来回答关于意义的问题。这一转换就是全部的诀窍。
计算机看到的是字符,而非意义。embedding 把一段文本变成空间中的一个点,放置得让相近的意义彼此靠近——让意义成为软件能度量之物。
当意义被放进空间,按意义比较两样东西就变成了度量它们的点相隔多远。这个简单的想法是 embedding 所做一切之下的引擎。
靠近的点意味着相近的东西
在地图上,相邻的两座城镇之间易于往来;分处两岸的两座则相距遥远。地图上的近,映照着现实中的近。
因为 embedding 把相近的意义放在彼此附近,点与点之间的距离度量的是意义上的差异:靠得近意味着相似,离得远意味着无关。要问两段文本有多相像,你把两者都嵌入,再度量它们 vector 之间的间隔——间隔很小,就表明它们意思几乎相同。几何直接编码了语义,于是一个你能计算的距离,替代了一个你本无法计算的相似度。
按相似度查找:最近邻
一位图书管理员,递给他一本书,他能立刻为你指出书架上与它最像的那一批书——不是按书名,而是按它们讲的是什么。
核心操作是 nearest-neighbour search:给定一个点,找出离它最近的那些点。「给我看看和这个类似的东西」变成了「在空间里找出这一项的最近邻」。把一个问题、一件商品、一份文档交给系统,它就返回语义上最相似的那些,按接近程度排序。这一个操作——找出最近的——正是 semantic search、recommendation,以及后面大多数东西的动力。一切都是「找出邻近的点」的某种版本。
方向比原始距离更重要
两支指向同一方向的箭是相像的,哪怕其中一支更长——重要的是它们指的方向,而非它们的长度。
实践中,相似度通常用 vector 之间的夹角来度量,而非直线距离——这种度量叫 cosine similarity。两个指向同一方向的 embedding 会被视为相似,哪怕其中一个「更长」,因为方向捕捉意义,而长度往往只是反映文本长度之类的东西。你不需要懂数学也能用 embedding,但值得知道:「有多相似」的标准度量关乎空间中的方向,而非原始距离。
相近的意义靠得近,于是相似度变成了距离。核心操作是 nearest-neighbour search——找出最近的点——通常借助 cosine similarity,按方向来度量。
只有少数几个点时,找最近邻很容易;面对数百万个点时则很难。vector database 就是为在大规模下快速做到这件事而造的引擎——是 embedding 实际安家之处。
为 nearest-neighbour search 而造的存储
一座仓库被组织得:给定任意一件物品,它能立刻为你取来最相似的一百件——不是靠扫遍每个货架,而是靠它的摆放方式。
vector database 存储你的 embedding,并能快速回答「找出离这个最近的点」之类的查询,哪怕面对数百万个 vector。它专为 embedding 所需的那一个操作而造:按相似度做 nearest-neighbour search。你把数据嵌入一次,存下 vector,数据库就成了一个可检索的意义索引。当人们在 embedding 之上搭建搜索、RAG 或 recommendation 时,vector database 正是 vector 所居、检索所发生之处。
近似检索让大规模变快
要找最近的咖啡馆,你不会去量到地球上每一家咖啡馆的距离——你先在自己的街区里找。聪明的捷径胜过逐一查遍。
在大规模下,把一个查询和每一个存下的 vector 都比一遍会慢得无法接受。于是 vector database 采用 approximate nearest-neighbour(ANN)检索:用巧妙的索引在不检查全部的情况下找出最近的点,以一丁点精度换取巨大的速度提升。这正是为什么对数百万项做一次向量检索能在毫秒内返回。你很少需要精确的最近邻——「非常近」和「最近」一样好用——而 ANN 正是让 embedding 检索在真实规模下变得可行的东西。
嵌入一次,检索多次
书到馆时你只编目一次,此后每一次查阅都是即时的——缓慢的索引发生在前头,飞快的检索此后永远享用。
一个 embedding 系统的形态是:先把你的数据嵌入一次并存下 vector,然后在每次查询时低成本地检索它们。计算 embedding 是有代价的,但你在数据加入时付出,而非每次检索都付。这正是为什么 embedding 在检索方面扩展得好——昂贵的步骤是一次性的索引,而每次查询的成本不过是一次快速的 nearest-neighbour 查找。明白这一划分,能帮你同时推断一个 embedding 系统的成本与新鲜度。
vector database 存储 embedding 并快速找出最近邻,靠近似(ANN)检索扩展到数百万。先把你的数据嵌入一次;每次查询时低成本地检索它。
embedding 最直接的用途,是理解意义的搜索。它值得看清楚,因为它既是最常见的应用,又是 RAG 的基础。
按意义搜索,而非匹配字词
一位助手,哪怕你用自己的话描述,也能找到你想要的东西——「那个让饮料保持冰凉的东西」就把冰箱给你找出来,无需精确匹配。
semantic search 靠意义而非靠匹配关键词来找出结果。你把用户的查询嵌入,再去检索 vector 离它最近的那些文档片段。于是搜索「how do I get my money back」会找到一个标题为「refund policy」的页面,因为它们的意义靠得很近——尽管它们几乎不共用任何字词。关键词搜索会完全错过这一点;semantic search 正是为它而生的。它找的是你想表达的,而不只是你字面上敲下的。
用与数据相同的方式嵌入查询
要比较两个测量值,你必须用同一把尺子——一个量成英寸、另一个量成厘米,数字就对不上。
要让搜索奏效,查询和存下的文档必须由同一个模型嵌入,进入同一个空间——否则它们的坐标无法比较,「最近」也就毫无意义。你用一个模型嵌入你的文档,在查询时再用同一个模型嵌入问题,然后找出最近的文档 vector。这听起来理所当然,却是个常见的 bug:混用 embedding 模型,或换了模型却不把一切重新嵌入,会悄悄破坏搜索,因为那些点不再共享一个空间。
这就是 RAG 之下的检索
又是那场开卷考试:semantic search 正是你回答之前用来找到该看哪几页的方法——是让有据可依的回答成为可能的那次查找。
semantic search 是 retrieval-augmented generation(即 RAG 那门课)中的 retrieval 步骤。当一个 RAG 系统从你的文档作答时,embedding 正是它找出相关片段、放进模型上下文的方式。所以一个 RAG 系统的质量,在很大程度上取决于它的 embedding 检索的质量——把对的片段放到查询附近,回答便有据可依;放错了,模型就会从垃圾里作答。embedding 正是让 RAG 找到正确事实的那台引擎。
semantic search 靠意义而非关键词来查找——用与数据相同的方式嵌入查询,再找出最近的 vector。它是 RAG 之下的那台检索引擎。
搜索是显而易见的用途,但同样「意义即坐标」的想法,悄悄为出人意料的一大批其他任务提供着动力。看到它们,就明白为何 embedding 是如此基础的工具。
clustering 与去重:按意义分组
把一堆混杂的文档按主题分成几摞——把意义相近的放在一起,无需读完每一个字。
因为相近的意义在空间里聚成一簇,你可以按意义自动分组数据:把客户反馈归拢成几个主题、把文章按主题整理、在一堆文本中找出天然的类别。同一个想法也能逮住重复与近似重复——两张用不同字句描述同一问题的工单靠得很近,于是你能检测并合并它们。任何本质上是「这些里哪些意思相同?」的任务,都是一个 embedding 任务。
recommendation:更多你喜欢过的那种
一位店员,看到你买了什么,就为你指出其他口味相近的人喜欢的东西——相似度进去,建议出来。
recommendation 是乔装的 nearest-neighbour search:把物品(商品、文章、歌曲)嵌入得让相似的彼此靠近,于是「推荐更多这样的」就变成了「找出离用户喜欢过的东西最近的那些物品」。那套找出与查询相似之文档的同样机制,也找出与一次购买相似的商品。每当你看到「你可能还喜欢」,背后很有可能就是 embedding 和一次 nearest-neighbour search 在悄悄运作。
classification 与 anomaly detection
一名保安,知道正常是什么样子,并注意到那个举止与众人都不同的人——离群者在空间里独自站开。
embedding 为 classification 提供动力——一个新物品很可能与空间里离它最近的那些已标注物品属于同一类别——也为 anomaly detection 提供动力——某个 embedding 远离一切正常之物的东西,就是值得标记的离群点。揪出欺诈、跑题内容或异常输入,全都变成了「找出离簇很远的东西」。所有这些用途中反复出现的模式都是同一个:把东西变成点,然后就近与距离来推理。掌握了它,embedding 便成为远不止用于搜索的工具。
embedding 的动力远不止于搜索:clustering 与去重按意义分组,recommendation 找出相似物品,classification 与 anomaly detection 则就近与距离来推理。
embedding 很强大,却也容易被以悄悄毁掉结果的方式误用。少数几个陷阱,能解释大多数令人失望的 embedding 系统。
embedding 模型决定质量
一位只半懂一门语言的译者,会画出一张有缺陷的意义地图——下游的每一次比较都继承了那份失真。
一切都取决于创建 embedding 的那个模型。一个好的 embedding 模型把意义安放得准确,让相似的东西真的靠得近;一个孱弱或不匹配的模型,则产出一个失真的空间,那里的距离会误导你。embedding 模型的选择是一个质量决策,而非一个细节——一个在通用网络文本上训练的模型,可能把你的专门领域(法律、医疗、代码)嵌得很糟,因为它没有捕捉到那里要紧的那些区分。要刻意地挑选模型,并在你自己的数据上检验它。
相似不等于相关
两段话可以是同一主题、却回答不同的问题——「how to cancel」和「why people cancel」靠得很近,但只有一个是被问的那个。
embedding 相似度捕捉的是主题上的接近,这通常是相关性的不错代理——但并非总是。两段文本可以意义相近,却只有一个真正回答了需求。这正是为什么原始的 embedding 检索是一记强有力的初筛,却不是定论:它找出的是关于正确事物的东西,而这与有用的东西并不完全相同。明白「最近」意味着「最相似」,而非「最相关」,能让你不至于过度信任排在最前的结果,并把你引向 re-ranking 之类的细化手段。
垃圾进,垃圾空间
如果你把文档归在马虎、前后不一的标签下,哪怕对柜子做一次完美的检索,返回的也是一团乱——索引的好坏,只取决于放进去的是什么。
一个 embedding 系统的质量,受限于你所嵌入之物的质量。糟糕的分块(即 RAG 那门课里的 chunking)、嘈杂的文本,或嵌错了字段,会产出一个空间,那里哪怕完美的检索也返回糟糕的结果——那些距离是在垃圾之上算出来的。所以 embedding 系统里有大量工作并不在搜索本身;而在于首先准备好干净、结构良好、分块得当的文本去嵌入。在你怪罪搜索之前,先修好输入,因为空间会继承你放进它的一切。
embedding 会以已知的方式令人失望:孱弱或不匹配的模型扭曲空间,「相似」未必是「相关」,嘈杂的输入会让哪怕完美的检索也返回垃圾。挑好模型,并清理输入。
用好 embedding,归结为选对模型、喂它干净的数据,以及度量这个空间是否真的反映了你在意的意义。
为你的数据挑选 embedding 模型
你雇了一位真正懂你所在行话的译者——一个通才可能恰恰错过对你最要紧的那些区分。
最重大的选择是 embedding 模型,而对的那个取决于你的数据和任务。通用模型对通用文本足够好;一个专门领域则可能需要一个理解它的模型,或一个在它上面微调过的模型,才能把它的意义安放得准确。要考虑语言、领域、vector 尺寸和成本。别只是抓一个默认值——挑选那个为你的意义构建出准确空间的模型,因为你今后做的每一次比较都依托于它。
度量这个空间是否反映真实意义
你检验一张新地图,靠的是查看你已知彼此相邻的地方是否真的落得彼此相邻——若不是,这张地图就是错的。
别假定一套 embedding 配置奏效——检验它。拿一组你知道应该相似的案例,验证检索是否真的把那些作为最近的返回。这就是 embedding 版本的 evals:在你真实的数据上,这个空间是否把对的东西放得很近?度量这一点,能在一个糟糕的模型、一处领域不匹配或一个分块问题悄悄拖垮其上一切之前就把它逮住。一个你没检验过的 embedding 空间是一次猜测;一个你度量过的,则是一件你能信任的工具。
- 任务是否关乎意义——相似、分组、检索——embedding 是否真的契合? - embedding 模型是否合适于你的数据、领域和语言? - 查询与文档是否由 同一个模型嵌入,进入同一个空间? - 是否有一个 vector database,带着 适配你规模的近似检索? - 你嵌入的文本是否干净、分块得当,而非噪声? - 你是否度量过这个空间在真实案例上把对的东西放得很近?
- embedding / vector——一段文本被变成代表其意义的坐标。 - similarity / distance / cosine similarity——意义上的接近被度量为就近,通常按 方向。 - nearest-neighbour search——找出离给定点最近的那些点;核心 操作。 - vector database / approximate(ANN)search——存储,以及那台快速、可扩展的 检索引擎。 - semantic search——靠意义而非关键词来查找;RAG 之下的检索。 - clustering / recommendation / classification / anomaly detection——检索之外的用途。 - embedding model——创建坐标的东西,决定着整个空间的质量。
- 当任务关乎意义而非精确匹配时,你伸手去拿 embedding。 - 你为你的数据挑选了 embedding 模型,并用相同的方式嵌入查询与文档。 - 你在大规模下使用带近似检索的 vector database。 - 你嵌入干净、分块得当的 文本,并把「相似」当作相关性的一个强有力却不完美的代理。
- 你度量过 这个空间反映真实意义,而非想当然。
embedding 把意义变成坐标,于是相似度变成了距离。用好它的方式是:选对模型、一致地嵌入、喂入干净的文本,并度量这个空间真的把相似的东西放得很近。