什么是嵌入?向量和余弦相似度简单解释

  1. 首页
  2. 博客
  3. 人工智能

人工智能

什么是嵌入? 向量和余弦相似度简单解释

嵌入让计算机可以比较含义,而不是匹配确切的单词。它们为语义搜索、推荐和 RAG 提供支持。了解它们是什么、如何衡量相似性以及它们在哪些方面让您失望。

阅读约 8 分钟
什么是嵌入?向量和余弦相似度简单解释

要点速览

  • 嵌入是表示文本、图像或其他数据含义的数字列表。
  • 相似的项目最终会得到相似的数字,因此可以将意义与数学进行比较。
  • 余弦相似度比较方向,这是对两个嵌入的相似程度进行评分的常用方法。
  • 仅比较同一模型的嵌入,并在您自己的数据上测试结果。

搜索“如何取回我的钱”,一个好的系统应该找到一个标题为“退款政策”的页面,即使两者没有共同的重要单词。这种能力来自嵌入,它是现代人工智能中最有用的想法之一。

本指南无需大量数学知识即可解释嵌入。我们在 Python 3.13 中运行了一个小示例,其输出如下所示。其中的向量是我们手工制作的,每个向量只有三个数字,以使这个想法易于理解。真正的嵌入来自经过训练的模型,通常有数百或数千个数字。

什么是嵌入?

嵌入是将某物表示为数字列表,称为向量。训练一个模型,以便具有相关含义的项目获得靠近的向量。

"kitten"  -> [0.9, 0.8, 0.1]
"cat"     -> [0.9, 0.7, 0.2]
"invoice" -> [0.0, 0.1, 0.9]

一旦文本采用这种形式,计算机就可以计算出两段文本的相似程度,而这是它无法直接用单词来计算的。嵌入也不限于单词。句子、整个文档、图像、音频和产品都可以嵌入。

为什么不直接匹配关键词呢?

查询和文档 关键词搜索 嵌入搜索
“退款”和“退款政策” 错过了,没有共同语言 可以找到它
“膝上型电脑”和“笔记本电脑” 想念它 可以找到它
“错误 E1042”和带有“E1042”的页面 准确地找到它 可能会错过
“银行”(河流)和“银行”(金钱) 对待他们一视同仁 根据上下文可以将它们分开

我们的 RAG 指南在实践中展示了这一点。一个简单的字数搜索“我什么时候可以拿回我的钱?”的得分为零。反对退款文件,因为他们没有透露任何信息。还要注意第三行:精确标识符是嵌入的弱点,因此关键字搜索仍然有一席之地。

想出一张地图

想象一下将每个单词放在一张巨大的地图上,其中位置反映了含义。 “猫”和“小猫”几乎坐在一起,“狗”在同一个街区,“发票”在不同的城市。要找到相关的单词,您只需查看附近即可。

真正的嵌入以相同的方式工作,只是地图有数百个维度而不是两个。人们无法想象这一点,但数学原理是一样的。单个数字通常没有明确的含义,例如“毛茸茸的”。重要的是整体定位。

一个小的工作示例

以下是如何比较我们手工制作的矢量:

import math

def cosine(a, b):
    dot = sum(x * y for x, y in zip(a, b))
    return dot / (math.sqrt(sum(x * x for x in a)) *
                  math.sqrt(sum(y * y for y in b)))

vectors = {
    "kitten":  [0.9, 0.8, 0.1],
    "cat":     [0.9, 0.7, 0.2],
    "dog":     [0.8, 0.3, 0.2],
    "invoice": [0.0, 0.1, 0.9],
}

for word in ("kitten", "dog", "invoice"):
    print(word, round(cosine(vectors["cat"], vectors[word]), 3))

# kitten 0.994
# dog 0.955
# invoice 0.238

对于“猫”,“小猫”得分最高,“狗”次之,“发票”则低得多。我们选择这些向量来产生该模式,因此排名并不能证明真实语言的情况。它仅显示计算的行为方式。语义搜索通过三个步骤执行相同的操作:嵌入查询、针对每个存储的项目对其进行评分,然后进行排序。

query = [0.85, 0.75, 0.15]
sorted(vectors, key=lambda w: cosine(query, vectors[w]), reverse=True)

# ['kitten', 'cat', 'dog', 'invoice']

余弦相似度解释

每个向量都可以被认为是从原点指向的箭头。余弦相似度测量两个箭头之间的角度,忽略它们的长度。我们确认了关键值:

cosine([1, 2, 3], [2, 4, 6])   # 1.0   same direction
cosine([1, 0], [0, 1])         # 0.0   unrelated (at right angles)
cosine([1, 0], [-1, 0])        # -1.0  opposite directions
分数 含义
接近1 指向几乎相同的方向,非常相似
接近0 无关的
接近-1 指向相反的方向

看第一行。 [2, 4, 6] 的长度是 [1, 2, 3] 的两倍,但分数却是完美的 1.0,因为方向才是最重要的。这对于文本很有用,因为文本的长度可以反映文档大小等内容,而不是含义。也要小心阈值。没有表示“相似”的通用分数,因为有用范围因模型而异,因此请使用您自己的数据进行测试。

其他距离测量

测量 它看什么 注释
余弦相似度 仅角度 文本的常见默认值
点积 角度和长度 快速,并且等于归一化向量上的余弦
欧氏距离 直线距离 越小意味着越相似

差异很容易看出。余弦分数为 1.0 的这两个相同向量的欧几里得距离为 3.742,因为其中一个比另一个长。许多系统对向量进行归一化,将每个向量的长度缩放为 1。之后,点积和余弦给出相同的答案,我们证实了这一点:“猫”和“狗”的结果都是 0.955。检查您的模型的文档以了解它推荐的措施。

嵌入是如何学习的

在实际系统中没有人手动分配这些数字。神经网络在训练过程中学习它们。一个早期且有影响力的想法是,出现在相似上下文中的单词往往具有相似的含义,例如“我喂了我的猫”和“我喂了我的狗”。 Word2Vec 和 GloVe 等模型从这些模式中学习了词向量。

后来,基于 Transformer 的模型(例如 BERT)使嵌入具有上下文感知能力,因此“银行”可以在“河岸”和“银行贷款”中获得不同的向量。许多现代模型一次嵌入整个句子或段落。我们的大型语言模型指南解释了转换器如何处理上下文。

使用嵌入的地方

使用 它有什么帮助
语义搜索 根据含义而不是确切的单词查找结果
RAG 检索语言模型的相关段落
建议 推荐与您喜欢的商品类似的商品
聚类 群组支持按主题分类的票证或评论
重复检测 发现类似的问题或列表
分类 提供简单模型的功能
异常检测 标记远离其他一切的项目

我们的示例将查询与每个项目进行比较,这称为精确搜索。这对于数千个项目来说效果很好,但对于数百万个项目来说就变得很慢。 近似最近邻 (ANN) 索引(例如 HNSW)解决了这个问题。他们通过放弃错过绝对最佳比赛的微小机会,很快找到非常接近的比赛。

矢量数据库和扩展(例如 PostgreSQL 的 pgvector)提供这些索引以及过滤和存储。我们的矢量数据库指南涵盖了这些选项。对于一个小型项目,您可能根本不需要它,内存中搜索是一个非常好的开始方式。

提示:尽早考虑混合搜索。将嵌入相似性与关键字匹配相结合通常会比单独使用任何一种方法提供更好的结果,特别是当用户搜索产品代码、名称或错误消息时。

选择嵌入模型

  • 语言。 例如,如果您的内容是印地语或泰米尔语,请检查模型是否可以很好地处理它。
  • 领域。 一般模型可能会与专业的法律或医学术语发生冲突。
  • 维度。 更多维度可以捕获更多细节,但会消耗更多存储和内存。
  • 成本和托管。 将托管 API 与您可以自己运行的开放模型进行比较,尤其是对于私有数据。
  • 输入长度。模型有限制,因此必须先将长文档分割成块。

最好的测试是你自己的数据。收集大约 20 个真实查询以及它们应该找到的文档,并比较每个模型如何对它们进行排名。发布的基准可能无法反映您的内容。

局限性

  • 模型锁定。来自不同模型的向量无法进行比较。如果更改模型,则必须重新嵌入所有内容。
  • 精确术语。 ID、代码和罕见名称可能无法可靠匹配。
  • 否定。“允许退款”和“不允许退款”可以同时出现,尽管含义相反。
  • 相似并不意味着正确。 接近的匹配可能已经过时或根本就是错误的。
  • 偏差。 嵌入可以吸收训练数据中的刻板印象。
  • 余弦不是魔法。 研究人员质疑它对于某些学习嵌入的意义有多大,因此将分数视为有用的信号,而不是作为真理的衡量标准。

常见错误

  • 混合模型。嵌入具有相同模型和版本的查询和文档。
  • 到处使用一个相似性阈值。针对每个模型和任务对其进行调整。
  • 整个嵌入巨大的文档。意义被稀释,所以首先将它们分块。
  • 跳过评估。 如果没有测试查询,您无法判断更改是否有帮助。
  • 对所有内容使用嵌入。 如果用户搜索确切的 ID,则普通的数据库索引 更好。

最后的话

嵌入将意义转化为数字,以便软件可以比较想法而不是字符串。这个概念很简单,而实际工作在于选择模型、对数据进行分块以及对实际查询进行测试。要亲自尝试,请使用任何嵌入模型嵌入几十个您自己的文档,用真实的问题搜索它们,然后看看结果在哪里让您感到惊讶。

常见问题

简单来说什么是嵌入?

嵌入是表示含义的数字列表。具有相似含义的项目会得到相似的数字,这让计算机可以对它们进行数学比较。

什么是余弦相似度?

它测量两个向量之间的角度,从 -1 到 1。接近 1 的分数意味着它们指向几乎相同的方向,这表明内容相似。

嵌入和令牌有什么区别?

令牌是模型处​​理的文本块。嵌入是表示标记、句子或文档含义的数字向量。

嵌入应该有多少个维度?

这取决于型号,常见的是几百到几千。更多可以捕获更多细节,但会消耗更多存储空间,因此请测试您的用例需要什么。

我需要矢量数据库才能使用嵌入吗?

不会。小集合可以在内存中搜索。矢量数据库对于更大的集合、频繁的更新或过滤需求变得有用。

继续学习

与Stack Quora合作

需要测试、WordPress 或 SEO 方面的帮助?

我们帮助团队交付更好的软件并发展他们的网站。告诉我们您正在做什么,我们将建议明确的下一步。

继续阅读

更多指南 测试、人工智能和现代开发

喜欢这篇文章吗?选择一个主题并直接跳至我们最新的教程。

人工智能

人工智能幻觉解释:语言模型为何会产生幻觉以及如何减少它

人工智能幻觉解释:它们是什么,为什么语言模型产生它们,常见类型,如何通过基础和提示来减少它们,以及我们进行的简单检查。

阅读文章 →
DevOps

环境变量解释:.env 文件、秘密和配置正确完成

使用我们运行的 Python 示例解释环境变量:读取和设置它们、为什么每个值都是字符串、.env 文件、进程继承、秘密和验证。

阅读文章 →
软件开发

API 速率限制解释:算法、标头和处理 429 错误

API 速率限制通过我们运行的模拟进行了解释:令牌桶、固定窗口、滑动窗口、429 响应、速率限制标头、带回退的重试和测试限制。

阅读文章 →
测试自动化

Python 中的模拟:unittest.mock、补丁以及导致测试谎言的错误

使用unittest.mock 在Python 中进行模拟,经过测试:模拟和return_value、side_effect、修补使用名称的位置、spec 和autospec、模拟日期以及常见错误。

阅读文章 →
软件测试

代码覆盖率解释:行覆盖率与分支覆盖率以及 100% 的真正含义

用真实的coverage.py运行来解释代码覆盖率:行、分支和条件覆盖率,阅读报告,为什么100%覆盖率仍然会错过错误,以及合理的目标。

阅读文章 →
数据库与 SQL

SQL 中的 DELETE、TRUNCATE 和 DROP:差异、回滚以及何时使用它们

DELETE、TRUNCATE 和 DROP 通过经过测试的 SQL 进行了解释:各自删除什么、回滚、触发器、身份重置、速度,以及 PostgreSQL、MySQL、SQL Server 和 SQLite 有何不同。

阅读文章 →
数据库与 SQL

SQL 视图解释:创建、使用和避免陷阱(附示例)

SQL 视图通过我们运行的查询进行解释:创建视图、实时结果、隐藏列、性能、可更新和物化视图、破坏的依赖关系和最佳实践。

阅读文章 →
软件开发

TypeScript 接口与类型:真正的差异,使用编译器进行测试

TypeScript 接口与类型,使用真实编译器进行检查:两者都可以做什么,只有一个可以做什么,声明合并,扩展与交集以及要遵循的简单规则。

阅读文章 →
软件开发

Python *args 和 **kwargs 解释:灵活的函数和清晰的示例

Python *args 和 **kwargs 用我们运行的代码进行了解释:打包和解包、参数顺序、仅关键字和仅位置参数、装饰器和常见错误。

阅读文章 →
软件开发

Python Lambda 函数:语法、实际用途以及何时不使用它们

Python lambda 函数用我们运行的代码进行了解释:语法、使用键排序、映射和过滤器、闭包、后期绑定陷阱,以及何时普通 def 是更好的选择。

阅读文章 →
人工智能

什么是人工智能代理?它们是如何工作的,只需运行一个简单的代理循环

什么是人工智能代理?关于代理循环、工具、内存和护栏的简单指南,以及我们运行的一个小型 Python 循环、真正的风险以及何时简单的聊天机器人是更好的选择。

阅读文章 →
DevOps

监控与可观察性:真正的区别,包括日志、指标和跟踪

监控与可观察性解释:已知和未知的故障、日志、指标和跟踪、检查事件演练、黄金信号、OpenTelemetry 和有用的警报。

阅读文章 →
DevOps

Docker Volumes 与 Bind Mounts:以正确的方式保留容器数据

Docker 卷与绑定挂载解释:为什么容器数据消失、命名卷、绑定挂载、tmpfs、Compose 示例、备份、权限以及何时使用它们。

阅读文章 →
测试自动化

TDD 与 BDD:主要区别、以两种方式构建的一个功能以及何时使用每种方式

TDD 与 BDD 的解释是通过一种双向构建的折扣功能:使用我们运行的代码进行红绿重构、Given-When-Then 场景、工具、权衡以及何时使用每种方案。

阅读文章 →
软件测试

用户验收测试 (UAT):流程、清单和真实示例

用户验收测试解释:谁运行它、它与 QA 有何不同、七步流程、UAT 测试用例模板、清单、签核标准和常见错误。

阅读文章 →
数据库与 SQL

SQL NULL 值解释:IS NULL、COALESCE、NULLIF 和要避免的陷阱

通过我们运行的查询解释了 SQL NULL 值:三值逻辑、IS NULL、COALESCE、NULLIF,聚合、连接和排序如何处理 NULL,以及它悄悄引起的错误。

阅读文章 →
数据库与 SQL

SQL CASE WHEN 语句:语法、实例和常见错误

SQL CASE WHEN 解释了我们运行的查询:简单和搜索表单、条件顺序、SUM 透视、COUNT 技巧、自定义排序、更新和要避免的 NULL 陷阱。

阅读文章 →
软件开发

什么是智威汤逊?通过工作示例解释 JSON Web 令牌

什么是智威汤逊? JSON Web Tokens 解释:这三个部分,在我们运行的代码中构建和篡改的令牌,JWT 与会话,存储,刷新令牌和安全错误。

阅读文章 →
软件开发

JavaScript 事件循环解释:调用堆栈、微任务和计时器以及示例

JavaScript 事件循环用我们运行的代码进行了解释:调用堆栈、任务和微任务队列、为什么 setTimeout 0 不是立即执行、阻塞、饥饿和面试测验。

阅读文章 →
软件开发

Python 生成器和 Yield 解释:通过真实示例节省内存

Python 生成器和yield 用我们运行的代码进行了解释:它们如何暂停和恢复、测量内存节省、管道、无限序列、yield from、发送和常见陷阱。

阅读文章 →
人工智能

什么是嵌入?向量和余弦相似度简单解释

什么是嵌入?关于向量、余弦相似度和语义搜索的简单语言指南,其中包含我们运行的一个小示例、常见用途、选择模型和已知限制。

阅读文章 →
DevOps

蓝绿部署与金丝雀部署:差异、权衡以及如何选择

蓝绿部署与金丝雀部署解释:每种部署的工作原理、成本、风险和回滚、我们测试的稳定用户分桶、数据库迁移、滚动更新以及如何选择。

阅读文章 →
测试自动化

不稳定的测试:造成这些问题的原因以及如何彻底解决它们

我们运行的演示解释了不稳定的测试:主要原因、如何确认和诊断、正确的时间修复、共享状态和日期以及隔离过程。

阅读文章 →
软件测试

负载测试与压力测试:差异、指标和 k6 示例

负载测试与压力测试的解释:各自证明什么、峰值和浸泡测试、重要的指标、为什么平均值会误导、k6 个示例以及如何计划测试。

阅读文章 →
软件测试

测试用例与测试场景:主要区别、示例以及何时使用它们

测试用例与测试场景的解释:清晰的定义、并排的表格、从场景到案例的密码重置功能、模板以及何时使用每个功能。

阅读文章 →
数据库与 SQL

SQL 查询优化:读取执行计划并修复慢速查询(带计时)

对一百万行进行 SQL 查询优化测试:读取执行计划、添加正确的索引、修复不可控制的过滤器、使用复合索引和键集分页。

阅读文章 →
数据库与 SQL

SQL UNION 与 UNION ALL:差异、示例和性能测试

SQL UNION 与 UNION ALL 通过我们运行的查询进行了解释:重复、都遵循的规则、真实的数据错误、测量的性能、INTERSECT 和 EXCEPT 以及何时使用它们。

阅读文章 →
软件开发

JavaScript Fetch API 教程:GET、POST、错误处理和超时

针对本地服务器测试的 JavaScript Fetch API 教程:GET 和 POST、为什么不抛出 404、可重用的帮助程序、查询字符串、超时、CORS 和常见错误。

阅读文章 →
软件开发

Python 文件处理:读取、写入、CSV、JSON 和 pathlib 以及示例

使用我们运行的代码解释了 Python 文件处理:打开模式、使用、读取大文件、CSV、JSON、pathlib、二进制文件、安全写入、错误处理和常见错误。

阅读文章 →
软件开发

Python 类和对象:带有实际示例的 OOP 教程

用我们运行的代码解释 Python 类和对象:__init__、方法、类与实例属性、继承、属性、dunder 方法、数据类和常见错误。

阅读文章 →
人工智能

什么是大语言模型?法学硕士如何运作,在没有炒作的情况下进行解释

什么是大语言模型?法学硕士用简单的语言解释:标记、嵌入、注意力、训练、温度和上下文窗口、一个微小的下一个单词演示和真正的限制。

阅读文章 →
DevOps

适合初学者的 Linux 命令:带有示例的基本终端指南

适合初学者的 Linux 命令,按任务分组:导航、文件、查看日志、grep、管道、权限、进程和档案,并附有实用的日志分析示例。

阅读文章 →
测试自动化

Selenium 与 Playwright:主要区别、代码比较以及选择

Selenium 与 Playwright 比较:架构、等待、浏览器和语言支持、调试、两种工具中的相同测试、迁移建议以及如何选择。

阅读文章 →
软件测试

等价划分:通过实例解释测试设计技术

等效分区解释:如何找到有效和无效分区,年龄、密码和优惠券的工作示例,我们运行的测试,以及为什么将其与边界配对。

阅读文章 →
软件测试

软件测试中的严重性与优先级:差异、矩阵和实例

软件测试中的严重性与优先级解释:清晰的定义、带有示例的四向矩阵、级别尺度、谁设置每个级别以及如何运行错误分类。

阅读文章 →
数据库与 SQL

SQL 子查询教程:类型、相关子查询和 EXISTS 以及示例

SQL 子查询教程,其中包含我们运行的查询:标量、IN、FROM 和 SELECT 子查询、相关子查询、EXISTS、NOT IN NULL 陷阱以及子查询与 JOIN 与 CTE。

阅读文章 →
数据库与 SQL

SQL 事务和 ACID 解释:COMMIT、ROLLBACK 和隔离级别

SQL 事务和 ACID 通过我们运行的银行转账进行了解释:COMMIT、ROLLBACK、保存点、锁、隔离级别、脏读、死锁和最佳实践。

阅读文章 →
软件开发

Git merge 与 rebase 的区别:示例及如何选择

用真实的 log 输出、对比表、rebase 黄金法则和安全的团队工作流,讲清 Git merge 与 rebase 的区别。

阅读文章 →
软件开发

Python 列表推导式:语法、示例与何时避免使用

Python 列表推导式详解:语法、过滤、if-else、嵌套循环、实测耗时,以及何时普通 for 循环更合适。

阅读文章 →
数据库与 SQL

SQL JOIN 详解:附示例(INNER、LEFT、RIGHT、FULL)

通过示例表和查询,一步步讲解 SQL JOIN:INNER、LEFT、RIGHT、FULL、CROSS 和 SELF,并介绍常见错误与性能技巧。

阅读文章 →