Contents
1 从右侧菜单引出的疑问
我在之前实现“右侧菜单”功能的文章中(参见:为博客构建“轻量级知识索引”(七):右侧菜单——语义空间与分类体系的统一组织机制),使用了一种基于语义相似度的分类推荐算法。其核心思路并不复杂:首先为博客中的每篇文章生成对应的语义向量,然后根据同一分类下文章向量的整体分布,计算出能够代表该分类主题的中心向量。
当访客打开某篇文章时,系统会将当前文章的语义向量与所有分类的中心语义向量进行余弦相似度计算,并选出最相似的三个分类作为推荐分类。
但当我真正把这套系统跑起来之后,也派生出了新的疑惑,分类中心向量的计算并不复杂,本质上只是对同一分类下的文章向量进行聚合。真正让我好奇的是:文章的语义向量到底是怎么来的?为什么一篇几千字甚至上万字的文章,最终能够被压缩成一个由几千个数字组成的向量?更进一步说,这些看起来毫无规律的数字,又为什么能够代表文章的主题和含义?如果不能回答这些问题,那么我们实际上只是学会了使用向量,而没有真正理解向量。
在这个系列的上一篇文章《AI 语义空间探索(一):向量——AI 世界里的通用语言》中,我已经介绍过:对于 AI 来说,向量就像是一种通用语言。无论是文字、图片还是声音,最终都可以转换为向量形式进入统一的数学空间。但是上一篇文章主要解决的是:什么是向量,以及为什么向量能够成为 AI 世界里的通用语言。
而这一篇文章要解决的问题则是:AI 如何生成这些向量?一个词语如何变成向量?一句话又如何在此基础上表达完整的语义?一篇文章为什么最终能够拥有自己的向量表示?更进一步,一个分类为什么也能够形成属于自己的中心向量?
当理解了这一过程之后,我们才能真正看懂:Embedding 模型在做什么;语义搜索为什么能够工作;RAG 为什么需要向量数据库;以及我博客中文章的右侧菜单功能背后的原理。
2 从一个词开始:AI 如何理解语言
既然已经知道了一个词最终会拥有自己的语义向量,那么一个新的问题自然就出现了:这个向量是谁赋予它的?
很多人第一次接触语义向量时,都会下意识地认为,是不是模型内部提前保存了一张巨大的”词典”,里面记录着每一个词对应的一组数字。例如,“太阳”对应一组数字,“月亮”对应另一组数字,当模型遇到这些词时,只需要查一下这张词典,就能够得到对应的向量。
这个想法听起来很合理,但实际上并不是现代 AI 理解语言的方式,原因其实并不难理解:自然语言本身就是不断发展的,新词汇几乎每天都在出现。如果真的依靠人工维护这样一张词典,那么无论规模做得多大,都无法覆盖所有词语,更不用说不同语言、不同领域以及不断变化的新概念了。对于 AI 来说,这种方式既不现实,也无法真正理解语言。
现代 AI 更接近人类学习语言的过程:一个刚出生的孩子,并不会因为有人告诉他”太阳”的定义,就真正理解了这个词的含义。他是在不断听别人说话、阅读文字、观察现实世界的过程中,逐渐发现”太阳”经常和”天空”、“白天”、“光”、“温暖”等词语一起出现,也就逐渐理解了这些概念之间的联系。经过长期积累之后,即使没有人专门解释,他也能够建立起对”太阳”这一概念的认知。
AI 学习语言的思路其实非常相似:在训练过程中,模型会阅读海量文本——从新闻报道、百科知识,到技术文档、小说、论坛讨论,几乎所有能够获取的大规模文本,都可能成为训练数据。模型不会去背诵每一句话,而是在不断阅读这些文本的过程中,逐渐学习不同词语之间的关系。
例如,它会发现”程序员”经常和”代码”、“编程”、“软件”一起出现;“医院”经常和”医生”、“病人”、“治疗”一起出现;“Cloudflare”则经常与”CDN”、“DNS”、“WAF”、“Tunnel”等技术词汇共同出现。这些大量重复出现的语言规律,最终构成了模型对于词语含义的理解。
换句话说,AI 学到的并不是一个词典中的固定定义,而是在大量语言使用过程中形成的、与其他概念之间的复杂关系。
正因为如此,词语的含义并不是人工设计出来的,而是在海量语言数据中一点一点学习出来的。不同模型使用的具体训练方法可能有所不同,从早期的 Word2Vec 到今天基于 Transformer 的各种 Embedding 模型,都经历了不断的发展与演进。但无论底层算法如何变化,它们都有一个共同目标:让模型能够通过大量语言数据,学习词语背后的语义关系,而不是依赖人工维护一张静态词典。
不过,理解了这些之后,一个新的问题又出现了:如果 AI 已经学会了”太阳”、“月亮”、“Cloudflare”这些词语的含义,那么这种抽象的理解又是如何变成一组具体的数字,最终形成我们所说的”词向量”的?更进一步说,为什么这些数字又能够参与后续的数学计算,并最终组合成一句话、一篇文章甚至一个分类的语义向量?
3 从理解到数字:AI 如何表达语义
经过前面的讨论,我们已经知道,AI 并不是通过查阅一张预先准备好的词典来理解语言,而是在阅读海量文本的过程中,不断学习词语之间的语义关系。换句话说,模型内部已经逐渐形成了对于”太阳”、“程序员”、“Cloudflare”等概念的语义表示。
不过,新的问题也随之而来。理解本身是一种非常抽象的概念,人类可以说”我理解了这个词”,但对于计算机来说,却不存在”理解”这种存储形式。计算机并不会把”太阳代表一颗恒星”,“程序员负责编写代码”这样的概念直接保存在内部,因为它根本无法直接处理这些抽象含义。
事实上,无论是文字、图片、声音还是视频,计算机最终能够处理的都只有数字。我们平时看到的文字,对于计算机来说,本质上也只是按照一定编码规则存储的一串数字而已。例如,计算机并不知道”太”、“阳”这两个汉字分别代表什么,它只知道这两个字符对应的编码值。当程序读取这些字符时,也只是读取对应的数字,而不会自动理解其中包含的意义。
这也意味着,仅仅让 AI 学会语言还远远不够。如果希望模型能够进一步比较两个词语是否相似、计算一句话的含义,甚至完成语义搜索、文章推荐等各种任务,它最终必须把自己学到的语义,以一种计算机能够处理的形式保存下来。
最直接的办法,就是把语义转换成数字。不过,这里的数字并不是给每个词分配一个编号。例如,我们完全可以规定:
太阳 → 0001
月亮 → 0002
地球 → 0003
这种方式确实也是数字化,但它只能起到”身份标识”的作用。对于计算机来说,0001 和 0002 之间并没有任何语义关系,就像一个人的身份证号码一样,仅仅用于区分不同对象,并不能说明他们彼此之间是否相似。
因此,一个词最终对应的并不是一个数字,而是一组数字。在 AI 中,这组数字通常称为一个向量,而向量中包含的每一个数字,都可以看作是其中的一个维度(Dimension)。
因此,当我们说一个模型生成的是 768 维向量、1024 维向量或者 1536 维向量时,并不是指存在 768、1024 或 1536 种不同类型的向量,而是表示这个向量由 768、1024 或 1536 个数值组成,每一个数值对应其中的一个维度。例如,假设某个 Embedding 模型会为”太阳”这个词生成一个 1536 维语义向量,那么它的结构可以简单表示如下(上方数字表示维度编号,下方数值表示该维度对应的取值):

这些数值单独拿出来看几乎没有任何意义,因为其中任何一个维度都不能单独代表某种具体含义。真正有意义的是它们组合在一起所形成的整体,它共同决定了这个词在语义空间中的位置,并表达了这个词的整体语义。
换句话说,我们真正关心的,并不是某一个数字本身,而是这一整组数字共同构成的整体表示。正因为如此,我们通常不会去解释向量中的某一个维度代表什么,而是把整个向量看作一个不可分割的整体。当两个词语的向量彼此接近时,说明它们表达的语义更加相似;距离越远,则表示它们在语义上差异越大。
这时候,我们终于得到了上一篇文章中反复提到的那个概念——语义向量。它并不是人为规定的一串数字,也不是简单的编号,而是 AI 在学习语言之后,为了能够进行数学计算而建立起来的一种语义表示。
不过,到这里新的疑问又出现了。如果每一个词都拥有自己的语义向量,那么一句话、一段文字,甚至一篇几千字的文章,又是如何得到自己的语义向量的?难道只需要把所有词语的向量简单相加,就能够得到整篇文章的向量吗?
4 从文字到向量:Embedding 如何生成语义表示
上一章中,我们已经知道,AI 最终会把语义表示为一个向量。那么,一个新的问题来了:这些向量究竟是预先存放好的,还是模型根据输入内容实时计算出来的?
如果按照最简单的思路,我们或许会想到一种方法:为每个词都准备一个固定的向量。当模型看到”太阳”时,就直接取出”太阳”对应的向量;看到”月亮”时,再取出”月亮”对应的向量。
在早期词向量模型中,确实采用过这种方式——每个词对应一个固定向量。但很快,人们便发现了一个根本问题。还是之前的例子:苹果很好吃;苹果发布了新一代 iPhone。这两句话中的”苹果”显然不是同一个含义。如果仍然使用同一个固定向量来表示,它们在模型眼里就没有任何区别。
也正是为了解决这一问题,现代 AI 模型开始采用一种新的思路:不再为每个词准备一个固定的向量,而是结合整个上下文,动态生成当前语境下的语义表示。 这一过程,正是现代 Embedding 模型所完成的核心工作。
不过,要生成这种结合上下文的语义表示,仅靠传统意义上的 Embedding 层是不够的,它还需要 Transformer 这样的上下文建模结构。Transformer 可以理解为模型的”阅读器”。当一段文字输入模型之后,它并不会像传统程序那样一个词一个词地独立处理,而是会同时观察整个句子,分析每一个词与其他词之间的关系。
以前面的两个例子为例:苹果很好吃;苹果发布了新一代 iPhone。虽然两个句子都包含”苹果”这个词,但 Transformer 在分析上下文之后,会发现第一个句子中,“苹果”与”好吃”、“水果”等概念联系更加紧密;而第二个句子中,它则与”发布”、“iPhone”、“科技公司”等概念形成了不同的语义关联。
因此,模型最终生成的,实际上是两个不同的语义表示。也就是说,在现代 AI 中,我们不再认为”苹果”只有一个固定的向量,而是认为每一次出现在不同上下文中的”苹果”,都可能拥有不同的语义向量。 换句话说,现代 AI 真正表示的已经不是”词”本身,而是词在当前上下文中的语义。
因此,我们虽然习惯说”词向量”,但在现代 Transformer 模型中,更准确地说,它表示的是上下文中的语义表示(Contextual Representation)。这也是现代 Embedding 与早期词向量模型最大的区别之一:向量不再是静态保存的,而是在模型分析上下文关系之后动态生成的。
理解了这一点,我们也就能够解释一个长期以来困扰很多初学者的问题:为什么现代 Embedding 模型能够识别一词多义,甚至能够区分同一个词在不同场景下表达的细微语义差异。答案并不是模型内部保存了很多套不同的词向量,而是因为它每次都会重新阅读当前上下文,并根据整段文字重新计算当前词语的语义表示。
到这里,我们已经知道,Transformer 能够结合上下文,为每一个词生成对应的语义表示。但真正用于语义搜索、知识库检索或 RAG 的 Embedding,并不是几百个词分别对应的几百个向量,而是整段文字最终只对应一个向量。
那么,这么多词语的语义表示,又是如何进一步融合,最终压缩成一个能够代表整篇文章的语义向量的呢?
5 从词语到文章:AI 如何组织语义
经过前面的讨论,我们已经知道,Transformer 会结合上下文,为文章中的每一个词动态生成对应的语义表示。不过,这里的”词”其实只是为了便于理解的一种简化说法。严格来说,现代 AI 模型真正处理的并不是词,而是 Token。一个 Token 可以是一个完整的词,也可以是一个汉字、一个词语的一部分,甚至是一个标点符号,因此它并不完全等同于我们日常理解的”词”。
也就是说,当一篇文章输入模型之后,模型最终得到的并不是一个语义表示,而是一组与 Token 数量相对应的语义表示——每一个 Token 都拥有自己的语义表示。
注:在后面的讨论中,当涉及模型内部的处理过程时,我们统一使用 Token 这一术语。
不过,这又带来了一个新的问题:如果一篇文章包含两千个 Token,那么模型最终就会生成两千个语义表示,而我们在进行语义搜索、相似文章推荐或者 RAG 检索时,并不会拿着两千个语义表示去逐个比较,而是希望能够用一个向量来代表整篇文章。
那么,这一个文章向量又是怎么来的?最容易想到的方法,就是把所有 Token 对应的语义表示全部保存下来。不过,这样做不仅需要占用大量存储空间,而且不同文章长度各不相同,有的只有几十个 Token,有的则可能包含几千个 Token,这意味着每篇文章都会拥有数量不同的语义表示,彼此之间也难以直接进行比较。
因此,模型需要进一步完成一项新的工作:将数量不固定的 Token 语义表示,压缩成一个长度固定的文章向量。 这个过程通常称为 Pooling(池化)。
这里的”压缩”,并不是简单地丢弃信息,而是尽可能保留整篇文章最重要的语义特征,并将这些信息浓缩到一个固定长度的向量中。这样一来,无论原文只有几句话,还是包含几万字,最终都可以得到一个维度完全相同的文章向量。
例如,假设某个 Embedding 模型最终输出的是 1536 维向量,那么无论是一篇五百字的博客文章,还是一本几十万字的电子书,经过 Pooling 之后,都能够得到一个长度相同的 1536 维文章向量:

这样,不同长度的文本便拥有了统一的数学表示,也就能够直接计算它们之间的语义相似度。
实际场景中,不同的 Embedding 模型可能采用不同的 Pooling 策略,例如取所有 Token 语义表示 的平均值、使用特定位置的语义表示,或者采用更加复杂的聚合方式。不过,对于大多数现代文本 Embedding 模型来说,其目标都是一致的:让一个固定长度的向量,尽可能完整地表达整篇文章的语义。
对于使用者来说,我们通常并不需要关心模型内部具体采用了哪一种 Pooling 方法,因为这些工作都已经由模型自动完成了。我们真正需要理解的是:文章向量并不是某一个 Token 的语义表示,而是整篇文章所有 Token 的语义表示经过组织、压缩和抽象之后得到的整体表示。
这也解释了为什么一篇几千字的文章,最终能够被压缩成一个只有一千多个数值组成的向量。它并不是简单地把文章中的每一个 Token 原封不动地保存到向量中,而是在保留整体语义特征的基础上,将整篇文章压缩成一个固定长度的表示。因此,语义相近的文章,最终在向量空间中的位置也会更加接近。
对于我博客中的右侧菜单来说,这一步正是整个推荐系统的基础。每篇文章都会生成一个对应的文章向量,后续无论是计算文章之间的相似度,还是生成分类中心向量,都是建立在这些文章向量之上的。不过,到这里,我们得到的仍然只是单篇文章的语义表示。
如果一个分类下面包含几十篇甚至上百篇文章,那么这些文章向量又是如何进一步抽象为一个能够代表整个分类主题的中心向量?
6 从文章到主题:AI 如何抽象语义
经过前面的讨论,我们已经知道,一篇文章最终可以被表示为一个固定长度的文章向量。无论原文长短、内容多少,经过 Embedding 和 Pooling 之后,都能够得到一个能够代表整篇文章语义的数学表示。
不过,新的问题又出现了:如果一篇文章能够拥有自己的语义向量,那么一个更大的主题,例如 ”Cloudflare”、“Linux”、“AI”,是否也能够拥有自己的语义向量呢?
乍一看,这似乎有些不可思议,因为一篇文章是实际存在的文本,可以直接输入模型生成向量;而”Cloudflare”这样的主题,本身并不是一篇具体的文章,它只是许多相关文章共同形成的一个更高层次的概念。那么,AI 又是如何表示这样一种抽象概念的?
答案其实比想象中简单:如果把每一篇文章都看作语义空间中的一个点,那么讨论同一主题的文章,通常都会分布在相近的位置。虽然它们关注的内容各不相同,例如有的介绍 Cloudflare Tunnel,有的介绍 Cloudflare Workers,还有的介绍 Cloudflare DNS,但由于都围绕着同一个主题展开,因此它们在语义空间中的距离往往不会太远。
也就是说,一个主题并不是一个单独的点,而更像是语义空间中的一片区域。
既然如此,一个很自然的想法便出现了:如果能够找到这片区域的”中心”,那么这个中心位置,是不是就能够代表整个主题?
在实际应用中,这个中心通常可以通过聚合该主题下所有文章向量来计算。其中最常见的方法,就是直接计算所有文章向量的平均值。虽然不同系统也可以采用更加复杂的聚合算法,但它们的目标都是一致的:找到一个最能够代表整个主题整体语义的中心向量。
在我的这个应用场景中,一个主题的语义向量并不是模型直接生成的,而是在文章向量基础上进一步聚合得到的——文章描述的是具体内容,而主题则提炼出了这些内容背后的共同语义。
对于我博客文章的右侧菜单来说,正是利用了这一思想。系统会首先为每一篇文章生成对应的文章向量,然后根据同一主题下所有文章的向量计算出对应的主题中心向量。当访客打开某篇文章时,系统并不是直接去比较所有文章,而是先将当前文章的向量与所有主题的中心向量计算余弦相似度,找出最接近的TOP 3主题,然后用同样的思路,在主题中进一步找出最接近的TOP 4文章。
整个过程可以简化表示为:
当前文章
│
▼
文章语义向量
│
▼
与所有主题中心向量计算余弦相似度
│
▼
选择最相似的3个主题
│
▼
生成右侧菜单第一层导航
│
▼
与3个相似主题中所有文章向量计算余弦相似度
│
▼
选择最相似的4篇文章
│
▼
生成右侧菜单第二层导航
事实上,这种”由具体到抽象”的思想,并不仅仅适用于我的博客。在很多 AI 应用中,我们都会看到类似的过程:大量具体的数据经过不断聚合,最终形成能够代表某一类事物的整体语义表示。例如知识库中的主题组织、文档聚类、内容推荐,甚至很多 RAG 系统中的知识组织方式,本质上都建立在这一思想之上。
回过头来看,我们这篇文章其实已经走完了一条完整的路径:
词语
│
▼
Token
│
▼
Token 语义表示
│
▼
文章向量
│
▼
主题向量
从最初的一个词,到最终能够表示整个主题的向量,AI 并不是一次完成所有工作,而是在不同层次不断组织、压缩和抽象语义。也正因为如此,无论是一个词、一篇文章,还是一个主题,在 AI 看来,本质上都可以表示为语义空间中的一个向量,只不过它们所对应的语义层次不同而已。
7 总结:语义是如何一步步形成的
在上一篇文章中,我们认识了向量这种表示方式,知道了 AI 为什么需要使用向量来处理语义。而这一篇文章,则进一步回答了另一个问题:这些语义究竟是如何一步步变成语义向量的。
从一个词开始,到模型结合上下文理解它在当前语境中的含义;从 Token 的语义表示,到整篇文章的语义向量;再到多个文章向量进一步抽象为能够代表整个主题的语义表示,我们看到的并不是几个彼此独立的技术,而是一条不断组织、压缩和抽象语义的完整过程。
理解这一过程最大的意义,并不在于学会几个新的专业术语,而是在于建立一种新的观察视角。
很多 AI 技术在刚接触时,看起来彼此毫无关系:Embedding、向量数据库、语义搜索、RAG、相关文章推荐、知识组织……似乎每学习一种技术,就需要重新理解一套新的概念。
但当理解了语义是如何一步步形成之后,就会发现,这些技术其实都建立在同一个基础之上:让不同层次的语义,都能够被表示、组织,并最终映射到同一个语义空间中进行计算。
正因为如此,当真正理解了语义向量的形成过程,再去学习这些具体技术时,关注点往往就不再是”应该调用哪个接口”、“应该使用哪个框架”,而会开始思考另一个更本质的问题:系统中的语义是如何表示的?又是如何随着信息不断汇聚,逐步完成组织和抽象的?
对于我来说,这也是写这一系列文章的初衷。相比于介绍某个具体框架或者实现某个具体功能,我更希望理解这些技术背后的共同原理。因为具体的模型会不断更新,框架会不断迭代,今天流行的技术,也许几年之后就会被新的方案取代;但语义如何表示、如何组织、如何抽象这一整套思想,却往往具有更长的生命力。
当真正建立起这种视角之后,再回头去看语义搜索、RAG、知识图谱,甚至我博客中的右侧菜单、知识地图等功能时,就会发现,它们虽然表现形式各不相同,但本质上都只是同一种思想在不同场景下的具体应用。
从这一刻开始,Embedding、RAG、向量数据库、语义搜索等看似独立的技术,也不再是一块块零散的知识,而开始成为同一个认知体系中的不同组成部分。