Embedding 是什么,把语义变成坐标,AI 才算看懂了世界
你想给自己的团队搭一个知识库问答机器人,或者想做一个按内容来找东西的搜索框,再或者只是每次听人聊 RAG、聊向量数据库的时候,不想再一头雾水。
这些事的背后,都站着同一个东西。
Embedding。
这个词经常被翻译成嵌入,也有人称它向量表示。名字听着挺玄,但它干的事很具体,把一段文字变成一串数字,而且让意思相近的文字,数字也相近。
在本篇里,我会带你把它从地基到应用完整拆一遍,主要包括五个部分。
先讲向量,把中学的坐标捡回来。然后讲 Embedding 模型,看一句话是怎么变成一串数字的。接着讲相似度,看两句话像不像是怎么算出来的。再讲向量数据库,看这些向量要跟什么一起存。讲完这四个地基,我们再去看它的三个真实用途,RAG 召回、推荐系统、文本去重。结尾我们还会把整条链路重走一遍。
不过话说回来,这篇讲的是一个过度简化的流程,我隐藏了很多实现细节,比如模型的训练方法,比如索引的底层结构。这些先不挖,先把主干走通,相信你就会清楚很多。
向量
那向量到底是什么呢。
别急,可能这个词你也记不太清了,我们从头捡。
先看一个你每天都在用的东西,地图。地图上任何一个位置,都可以用两个数字表示,经度和纬度。给你一组经纬度,你就能在地图上钉一个点。
中学数学课上讲的坐标系,干的是同一件事。一条横轴,一条纵轴,平面上的任何一个点,都能用两个数字锁定。横着数三格,竖着数两格,那个位置就记作 (3, 2)。
向量是什么呢,你可以先粗暴地理解,就是这一组数字本身。(3, 2) 是一个向量。它比「一个孤立的点」多了一层意思,你可以把它画成一个从原点出发、指向那个位置的箭头,它有方向。
数字也可以更多。(0.5, -1.2, 7) 也是一个向量,三个数字,可以画在立体空间里。再往上,四个数字、七百个数字、三千个数字,都行,只是画不出来了,但数学上完全成立。
到底多少个数字,看用途,没有死规定。
二维三维我们还能在脑子里比划,一千维就画不出来了。但「远近」这个概念照样成立,算距离的公式不挑维度。这正是向量这套东西值钱的地方,只要能把一个东西变成向量,不管它是位置、是人还是一句话,都能放进同一套比较远近的体系里。
这种思路你其实早就在用了。想用数字描述一个人,年龄、身高、体重,三个数字,这也是一个向量。描述一杯咖啡,温度、浓度、奶量,也是一个向量。
一组数字,描述一个东西。
那问题来了。
描述位置容易,经纬度就行。描述人也不难,挑几个指标就行。可怎么用数字描述一句话的意思呢。
给「火锅」的辣度打个分?给「天气」的晴朗程度打个分?这条路明显走不通,没人知道该打几分,也没人规定过意思的度量衡。
Embedding 解决的就是这件事。下面我们看它是怎么把「意思」变成坐标的。
Embedding
Embedding 的英文原意是嵌入。放在 AI 里,它指的是这么一件事,把一段文字交给一个模型,模型吐出一串数字,这串数字就是这段文字的向量。
先看一个场景,顺便见识一下这件事的必要性。
你手里有一堆句子,想让程序把意思相近的找出来。最直接的办法是关键词匹配,句子 A 里有「天气」,句子 B 里也有「天气」,就算它们相近。
没错,简单场景下这个办法能用。
但下面这三句话,它就失灵了。
今天天气真好。
今日阳光灿烂。
我晚饭吃了火锅。
第一句和第二句,意思很近,一个说天气好,一个说阳光足。可它们连一个共同的词都没有。第三句跟前两句八竿子打不着,同样一个共同的词都没有。关键词匹配在这三句话上,什么都判断不出来。
要是碰上「今天天气真好」和「今天天气真差」,只差一个字,关键词匹配还会把它们判成近亲,那就错得更离谱了。
看来光看字面是行不通的。
我们需要一个办法,让程序绕过字面,直接看到语义。把「意思」变成数字,而且意思越近,数字越近。
这个东西就是 Embedding。
Embedding 模型接收一段文字,吐出一个向量。「今天天气真好」进去,出来一个向量。「我晚饭吃了火锅」进去,出来另一个向量。
为了方便演示,我给这三句话各编一个只有两个数字的向量。注意,这两个数字是我编的,真实的向量动辄几百上千个数字,这里砍到两个,纯粹是为了能在平面上画出来。
「今天天气真好」,向量是 (1, 2)。
「今日阳光灿烂」,向量是 (2, 1)。
「我晚饭吃了火锅」,向量是 (-2, 1)。
把这三个箭头画在坐标系上,前两个都指向右上方,尖端离得很近。第三个指向左边,跟它们隔了差不多一个直角。
这不是巧合,是设计出来的。
Embedding 模型的目标就一个,语义相近的文本,向量也相近。你可以把它想成一个翻译官,只不过它翻译出来的不是某种语言,是一串数字。输入的是人话,输出的是坐标。
那模型是怎么知道「阳光灿烂」和「天气真好」该放在相近位置的呢。
这里先降一下预期。模型的训练过程涉及大量数学,这一篇不展开,想深挖可以去看模型训练相关的资料。我们只看它的直觉。
模型在训练时读过海量的文本。它背后有一个很朴素的观察,一个词的含义,由它周围经常出现的词决定。「火锅」周围出现的是吃、辣、涮,「阳光」周围出现的是晒、明媚、好天气。上下文不一样,落点就拉开了。
模型看了几十亿句话之后,慢慢学会了给每句话安排一个合适的位置。意思经常可以互换的句子,位置就挨得近。意思风马牛不相及的句子,位置就拉得远。这个安排不是人去规定的,是模型从数据里自己长出来的。
真实模型吐出来的向量,常见的是几百到几千个数字。为什么需要这么多维。因为意思这个东西太复杂,两个数字只能表达平面上的一个点,装不下「天气不错」和「刚吃完火锅」之间那么多微妙差别。维度越高,能表达的关系就越多。具体多少维,各家模型不一样,不是铁律。
顺带澄清一下名词。embedding 这个词有两种用法,一种指这门技术,一种直接指那串数字本身。工程里常听到「把这句话的 embedding 拿出来」,说的就是那串数字,不是让你去调用什么功能。
这里再补一个分层。模型既可以把一个词变成向量,也可以把一整句话变成向量,前者工程里叫词向量,后者叫句向量。判断两句话像不像,用的是句向量,原理相通,只是粒度不同。我们这篇的三句话,用的都是句向量。
另外它不限于文字。图片、音频也能被转成向量,图库里按图搜图,用的就是同一套思路。这篇我们只聊文本。
讲完了模型,我们回来看那三句话。(1, 2) 和 (2, 1) 离得近,因为前两句都在说天气不错。(-2, 1) 独自待在左边,因为晚饭吃了火锅跟天气没关系。
对,Embedding 做的事就这么一件。
把语义变成坐标。
是不是很简单。
相似度
现在三句话都变成了向量,新问题跟着就来了。
「离得近」这件事,怎么算出来呢。总不能每次都靠肉眼在坐标系上看吧,何况真实的向量有几百上千个数字,根本画不出来。
这就需要一个公式,把「两个向量有多近」变成一个具体的数。
这个公式是怎么算的呢。答案是有很多种,目前常用的包括余弦相似度、欧氏距离和点积。我这里大致说一下最常用的余弦相似度,另外两个顺带提一句。
先说直觉。
两个向量都是箭头,都从原点出发。把它们摆在一起,两个箭头之间有一个夹角。
夹角越小,方向越一致,就越相似。夹角是零,指向同一个方向,完全相似。夹角是直角,方向毫无关系。夹角撑到最大,方向完全相反。
余弦相似度就是把这个夹角换算成一个数,夹角越小,这个数越大。完全同向算出来是一,垂直算出来是零,完全反向算出来是负一。它只看方向,不看箭头的长短。
这个设计是有讲究的。一句话长一点短一点,主要影响的是箭头的长短。像不像,看的是方向。把长度这个干扰因素扔掉,剩下的就纯粹是语义了。
那具体怎么算呢。公式是两个向量的点积,除以两个向量长度的乘积。听着有点抽象,我们直接上手算一遍,还是用那三句话。
第一句 (1, 2),第二句 (2, 1)。
点积,就是把对应位置的数字相乘再相加。1 乘 2 等于 2,2 乘 1 等于 2,加起来是 4。
两个向量的长度都是根号五,长度的乘积就是五。
余弦相似度,4 除以 5,等于 0.8。
再算第一句和第三句,(1, 2) 对 (-2, 1)。
点积是 1 乘 -2 加上 2 乘 1,等于 0。
余弦相似度,0。
0.8 和 0,差距一下就出来了。天气和阳光,方向大体一致,相似。天气和火锅,互相垂直,不相关。
当然,真实的计算里向量是几百上千维的,数字也远没有这么整。我这里为了能口算,全用了编好的数,真实场景交给程序算就行,原理一模一样。
另外提一句,这个分数没有绝对刻度。不是说 0.8 就一定算相似、0.3 就一定算不相似,阈值怎么定,要看你库里数据的分布,实践中都是拿自己的数据试出来的。
欧氏距离也顺带提一句。它算的是两个箭头尖端之间的直线距离,(1, 2) 和 (2, 1) 之间是根号二,(1, 2) 和 (-2, 1) 之间是根号十。一个近一个远,结论跟余弦相似度一致。具体用哪个看场景,不展开。
好,相似度就讲到这里,我们手里已经有了一个完整的闭环。
文字可以变成向量,向量之间可以算相似度。
剩下的问题是,这些向量存在哪,查询的时候发生了什么。
向量数据库
这就轮到向量数据库登场了。
先想一个问题,光存向量行不行。
不行。今天库里只有一个孤零零的 (1, 2),你什么也干不了,因为你根本不知道它对应哪句话。向量必须和它的原文绑在一起存。
所以向量数据库存一条数据,至少要存两样东西。
原始文本,和它的向量。一对一,锁死。
我们还是用那三句话,把一个向量数据库的存储结构模拟出来。为了方便演示,我这里只放三条数据。
| id | 原始文本 | 向量 |
|---|---|---|
| 1 | 今天天气真好 | (1, 2) |
| 2 | 今日阳光灿烂 | (2, 1) |
| 3 | 我晚饭吃了火锅 | (-2, 1) |
看中间这一列,原始文本。看右边这一列,向量。一行一条记录,文本和向量绑在一起。
真实场景里,原始文本这一列可能换成一段文档片段、一张图的描述、一个商品的介绍,但结构不变。而且一般还会多存一些元数据,比如这段话来自哪篇文档的第几段,方便捞出来之后溯源。
那查询是怎么做的呢。
注意,向量数据库的查询,跟普通数据库不是一个路数。普通数据库查精确匹配,你说查 id 等于一,它就把 id 等于一的那行拿出来,一字不差。向量数据库查的是相似,你说给我找出跟这个向量最像的几条,它要去逐个比对方向。
你可以把它想成一个只有一种检索方式的图书馆。你不报书名,不报作者,你报一段意思,它把意思最接近的几本书递给你。
我们模拟一次完整的查询。
假设用户提了一个问题,今天外面阳光怎么样。
这句话先经过同一个 Embedding 模型,变成一个向量,假设是 (1.5, 1.5)。这个数字也是我编的,别当真。
然后把这个向量交给向量数据库。数据库拿着它,跟表里每一条向量都算一遍余弦相似度。
跟「今天天气真好」算,0.95。跟「今日阳光灿烂」算,0.95。跟「我晚饭吃了火锅」算,负 0.32。
按分数从高到低排,取前几条,把这几条的原始文本返回来。前两句被捞出去了,火锅那句留在库里。
返回的是文本,不是向量。因为最终要用的,是那几句话本身。
写入的流程跟查询正好是反过来的。新来一段文本,先过一遍 Embedding 模型拿到向量,再把文本、向量和元数据组成一行,插进库里。所以向量数据库的日常就是两件事,写入时转一次向量,查询时转一次向量,中间的存储和比对交给它自己。
取几条也是你自己定的,想取三条取三条,想取十条取十条,工程里管这个叫 top k。k 具体是多少不是很重要,按场景调。
你可能会想,这跟把文本和数字写在一个 Excel 表里有什么区别。
区别在于,向量数据库把「按相似度查」做成了原生能力。数据量一大,逐条硬算是扛不住的,库里几百万条向量,每次查询都全量比对一遍,谁都受不了。所以向量数据库内部有专门的索引结构,比如 HNSW 这一类,能把搜索范围缩小到一小片区域,快速找到最相似的。这一层先不挖,记住它能快就行。
市面上的产品也不少,开源的 Milvus、Qdrant,云服务 Pinecone,还有一些传统数据库加装的向量插件。用法大同小异,存文本加向量,查询就是查相似。
讲完了存储和查询,我们再回到前面那张表。三行数据,文本和向量一一绑定。这个结构虽然小,但真实的向量数据库核心也就是这么个结构,只是规模大了成千上万倍。
四个地基到这里就齐了。
文字变向量,向量存进库,查询就是查相似。
下面我们看这条链路能干什么。
应用
RAG 召回
RAG 的全称是 Retrieval Augmented Generation,中文叫检索增强生成。听起来挺高大上,但也就这么回事,先从资料库里检索相关的内容,再基于这些内容生成答案。
它解决的是大模型的记性问题。你想让大模型回答一个它没见过的内部问题,比如你们公司产品手册里的某条规则。模型不知道,硬答还会编。RAG 的做法是,把问题变成向量,去向量数据库里把相关的片段捞出来,再把片段连同问题一起发给大模型,让它照着材料答。
实际使用时,文档会先被切成一段一段的小片段再入库,这一步叫分片。为什么不整篇直接转成一个向量呢,因为一整篇文档的意思太杂,天气、火锅、退货政策全搅在一个坐标里,查不准。切成小段,每段意思单纯,相似度才算得准。切多大有讲究,这里先不展开。
我们还是用那三句话演示。
假设知识库里就存了这三句话。用户问,今天外面天气怎么样。
问题先变成向量,(1.5, 1.5),这个我们刚才在查询那一节已经算过了。
然后去库里查相似。第一句 0.95,第二句 0.95,第三句负 0.32。前两句被捞出来,第三句留在库里。
大模型拿到的,是问题加两段材料。它照着材料就能回答,今天天气真好,阳光灿烂。
火锅那句话从头到尾没被捞出来,因为它跟问题不相关。这一步在工程里叫召回,捞什么不捞什么,由向量相似度说了算。
没有 Embedding,召回就退回到关键词匹配。而我们已经看过了,关键词匹配连「今天天气真好」和「今日阳光灿烂」这层近亲关系都认不出来。
顺带提一句,捞出来的片段后面通常还有一道精排,把不太相关的再筛掉一些,那是另一个话题,这里先不展开。
推荐系统
第二个场景,你可能每天都在被它服务。
视频软件为什么总能推到你爱看的,电商首页为什么总能摆上你刚想买的东西。背后的逻辑大同小异。
把你看过、点过赞、停留久的内容变成向量,再把候选内容也变成向量,然后算相似度,把最像的一批排到前面。
你连着刷了很多讲做菜的视频,你的兴趣向量就慢慢挪到了美食那一带。下一个候选是火锅探店,向量离你很近,排前面。下一个候选是量子力学入门,离得远,先放一放。
还是那句话,我晚饭吃了火锅。在推荐系统的眼里,它跟「今晚吃了一顿麻辣烫」是近邻,跟「今天天气真好」是远房。判断依据不是字面有没有重合,是向量指向哪边。
当然,真实的推荐系统比这个复杂得多,用户画像、实时行为、各种策略混在一起。但语义相似度是里面最基础的一块砖,砖就是我们今天讲的这套东西。
文本去重
第三个场景朴素一点,但非常实用。
内容平台每天涌进海量投稿,其中不少是搬运、洗稿、换个说法重复发。靠什么识别。
靠人眼看不过来。靠精确匹配也行不通,洗稿的人早把词全换了一遍,字面上已经是两段话了。
这时候就轮到 Embedding 出场。把每篇内容都变成向量,新投稿进来了,跟库里的存量内容算一遍相似度。相似度超过一个阈值,判为疑似重复,进人工复核。没超过,放行。
「今天天气真好」和「今日阳光灿烂」,一个共同的词都没有,向量相似度 0.8,在这个极简例子里就会被判成高度相似。「我晚饭吃了火锅」,相似度 0,放行。
也就是说,去重抓的不是字面重复,是语义重复。字面上的伪装骗得过精确匹配,骗不过坐标。阈值定多高,要拿自己平台的数据试,宁可放过去一些,也别把原创错杀进复核队列。
小结
三个场景讲完了,我们收一下。
RAG 召回,靠向量从知识库里捞出相关片段。推荐系统,靠向量从候选池里挑出你可能喜欢的。文本去重,靠向量把语义重复的内容挑出来。
同一个地基,三种用法。干的事都是那三句话演示过的,把语义变成坐标,再按坐标的远近来取用。
回到那三句话。「今天天气真好」和「今日阳光灿烂」从头到尾都挨在一起,「我晚饭吃了火锅」从头到尾都在远处。三个场景换的是用法,不换的是这套坐标。
重走一遍
到这里,所有的零件都见过了。下面我们把整条链路从头到尾重走一遍,分入库和使用两段。
先看入库,它发生在使用之前。
我们先把语料准备出来,可能是一份产品手册,可能是一批视频的描述,把它们整理成一段一段的文本。然后我们把每段文本都喂给 Embedding 模型,模型给每段文本生成一个对应的向量,语义相近的文本向量也相近,「今天天气真好」和「今日阳光灿烂」会落在坐标里相邻的位置,「我晚饭吃了火锅」独自待在远处。最后我们把文本和向量成对存进向量数据库,一行一条,谁也不跟谁分开,有需要的话再给每条配上元数据,记下它出自哪篇文档。
到这里,入库的流程就结束了。
再来看使用,问题进来之后发生了什么。
首先用户的问题会经过同一个 Embedding 模型,变成一个向量。然后这个向量被交给向量数据库,数据库把它和库里每条向量的余弦相似度都算一遍,方向越一致分数越高。接着按分数排序,取出最相似的前几条,把它们的原始文本返回来,相关的「天气」和「阳光」被捞出来,不相关的「火锅」留在库里。最后看你要干什么,捞出来的文本可以拼给大模型生成答案,可以做推荐排序,也可以拿去判断是不是重复内容。
好,提问之后的流程也结束了。
到这里,Embedding 的来龙去脉就算是全部讲完了。如果你想系统地学习这些内容,从大模型原理一路讲到 Agent、RAG 和模型部署,可以看看这门0基础Agent开发课,220 个课时,从零基础一路讲到生产部署。
评论区
暂无评论,快来抢沙发吧