Junkor

Embeddings: What they are and why they matter

原文地址:@simonw Embeddings: What they are and why they matter (simonwillison.net)

Embeddings, llm浪潮起来以后,这个概念才进入到我的视野中;但它本身在llm出现之前,就已经在机器学习领域有了非常多的应用场景;这里是 Simon 的一篇博客,通过一些有趣的事例,对Embeddings做了一些深入的介绍,抛开那些令人生畏的专业名词和术语,你会收获很多有趣的知识,读完以后,会对嵌入有一些不同的认识,同时也推荐看一下openAI 官网的一篇有关嵌入的文章,跟下边的一些内容有重合,可以相互加深理解:https://openai.com/blog/introducing-text-and-code-embeddings

我对嵌入的理解是从独热向量,或者基于词袋模型产生的内容向量反推出来的:

  • 独热向量的问题是太耗费输入向量的维度了,每个词都是一个向量,并且是个庞大的稀疏矩阵,存在维度灾难;
  • 词袋模型的形式跟贴近于嵌入模型,但主要问题是词袋的维护问题,词袋变更以后向量的维度都会变更,从而会影响整个输入;并且词袋模型只是简单的表述某个词的出现与否,语料越多,越丰富,词袋就越大,也有可能面临维度灾难问题;此外,虽然已能保证一个词、一个短句、一个长句也都能生成同等长度的向量,但其实只标识了某个词是否出现过、并且所有词的权重也都是一样的,在很多场景并不适用。
  • 而嵌入模型,很好的解决了上述问题,具体怎么解决的,我也还在研究,猜测大概思路应该是通过一些标记物料对模型进行预训练,含义相同的、相近的、甚至是跨语言的物料,通过监督学习让这些相近物料产生出相近的向量,让相近的内容落到一个临近的向量空间内,最后得到模型的权重信息,生成一个可用的模型,然后可以把输入文本映射到一个高纬向量的输出上;等这块儿学习有了新的成功,再输出新的文章吧。

更多高层级的概念和知识,菜品非常有趣,除了常规文本的事例,还有文本图片的CLIP的介绍,这是文生图类模型,使得文本到图片空间达到关联和实现的基础,欢迎品尝下面的内容~


嵌入是一个非常巧妙的技巧,但常常包含在一堆令人生畏的术语中。

如果你能理解这些术语,他们就会解锁强大而令人兴奋的技术,这些技术可以应用于各种有趣的问题。

我在 PyBay 2023 上发表了关于嵌入的演讲。本文代表了该演讲的改进版本,即使不观看视频,它也应该是独立的。

如果您还不熟悉嵌入,我希望为您提供开始将它们应用于现实世界问题所需的一切。

在本文中:

1. 38分钟视频版

这是我在 PyBay 上发表的演讲视频

由于麦克风问题,官方视频的音频质量不太好,但我通过 Adobe 的增强语音工具运行该音频,并将我自己的带有增强音频的视频上传到 YouTube。

2. 什么是嵌入?

嵌入是一项与更广泛的大型语言模型领域相邻的技术——ChatGPT、Bard 和 Claude 背后的技术。

Untitled

嵌入基于一个技巧:获取一段内容——在本例中是博客条目——并将该内容转换为浮点数数组。

该数组的关键之处在于它始终具有相同的长度,无论内容有多长。长度由您使用的嵌入模型定义——一个数组可能包含 300、1,000 或 1,536 个数字。

思考这个数字数组的最好方法是将其想象为一个非常奇怪的多维空间中的坐标。1,536 维空间很难可视化,所以这里是相同想法的 3D 可视化:

Untitled

为什么要在这个空间放置内容?因为我们可以根据内容的位置(特别是附近的其他内容)了解该内容的有趣信息。

根据嵌入模型对世界的奇怪且大多难以理解的理解,空间内的位置代表了内容的语义含义。它可能会捕获已嵌入内容的颜色、形状、概念或各种其他特征。

没有人完全理解这些单独数字的含义,但我们知道它们的位置可用于查找有关内容的有用信息。

3. 使用嵌入的相关内容

我使用嵌入解决的第一个问题是为我的 TIL 博客构建“相关内容”功能。我希望能够在每个页面的底部显示相关文章的列表。

我使用嵌入来完成此操作 - 在本例中,我使用了 OpenAI text-embedding-ada-002 模型,该模型可通过他们的 API 获得。

目前我的网站上有 472 篇文章。我计算了每篇文章的 1,536 维嵌入向量(浮点数数组),并将这些向量存储在我网站的 SQLite 数据库中。

现在,如果我想查找给定文章的相关文章,我可以计算该文章的嵌入向量与数据库中所有其他文章之间的余弦相似度,然后按距离返回 10 个最接近的匹配项。

本页底部有一个示例。使用 TG、sqlite-tg 和 datasette-sqlite-tg 在 SQLite 中进行地理空间 SQL 查询的前五篇相关文章是:

这是一个非常好的清单!

这是我用来计算这些余弦相似距离的 Python 函数:

def cosine_similarity(a, b):
    dot_product = sum(x * y for x, y in zip(a, b))
    magnitude_a = sum(x * x for x in a) ** 0.5
    magnitude_b = sum(x * x for x in b) ** 0.5
    return dot_product / (magnitude_a * magnitude_b)

我的 TIL 站点在我的 Datasette Python 框架上运行,该框架支持在 SQLite 数据库之上构建站点。我写了更多关于它如何在烘焙数据架构模式中工作的文章。

您可以浏览在 tils/embeddings 中存储计算的嵌入的 SQLite 表。

Untitled

这些是二进制值。我们可以运行此 SQL 查询以十六进制形式查看它们:

select id, hex(embedding) from embeddings

Untitled

但这仍然不太可读。我们可以使用 llm_embed_decode() 自定义 SQL 函数将它们转换为 JSON 数组:

select id, llm_embed_decode(embedding) from embeddings limit 10

在这里尝试一下。它显示每篇文章都附有 1,536 个浮点数的数组。

Untitled

我们可以使用另一个自定义 SQL 函数 llm_embed_cosine(vector1, vector2) 来计算这些余弦距离并找到最相似的内容。

该 SQL 函数在我的 datasette-llm-embed 插件中定义。

以下查询返回与我的 SQLite TG 文章最相似的五篇文章:

select
  id,
  llm_embed_cosine(
    embedding,
    (
      select
        embedding
      from
        embeddings
      where
        id = 'sqlite_sqlite-tg.md'
    )
  ) as score
from
  embeddings
order by
  score desc
limit 5

执行该查询将返回以下结果:

Screenshot 2024-03-04 at 08.20.53.png

正如预期的那样,该文章与其自身的相似度为 1.0。其他文章都与 SQLite 中的地理空间 SQL 查询相关。

该查询的执行时间大约为 400 毫秒。为了加快速度,我预先计算了每篇文章的前 10 个相似之处,并将它们存储在一个名为 tils/similarities 的单独表中。

Untitled

我编写了一个 Python 函数来从该表中查找相关文档,并从用于呈现文章页面的模板中调用它。

我的使用 openai-to-sqlite 和嵌入 TIL 存储和服务相关文档详细解释了这一切是如何工作的,包括如何使用 GitHub Actions 来获取新嵌入作为部署站点的构建脚本的一部分。

我在这个项目中使用了 OpenAI 嵌入 API。它非常便宜——对于我的 TIL 网站,我嵌入了大约 402,500 个代币,按 0.0001 美元/1,000 个代币计算,相当于 0.04 美元——只需 4 美分!

它真的很容易使用:你向它 POST 一些文本以及你的 API 密钥,它会返回浮点数的 JSON 数组。

Untitled

但是……这是一个专有模型。几个月前,OpenAI 关闭了一些旧的嵌入模型,如果您存储了这些模型的大量嵌入,这将是一个问题,因为如果您希望能够嵌入,则需要根据支持的模型重新计算它们还有什么新的东西。

Untitled

值得赞扬的是,OpenAI 确实承诺“承担用户使用这些新模型重新嵌入内容的财务成本。”——但这仍然是对依赖专有模型持谨慎态度的一个理由。

好消息是,有非常强大的开放许可模型,您可以在自己的硬件上运行它们,从而避免它们被关闭的任何风险。我们稍后会详细讨论这一点。

4. 探索这些东西如何与 Word2Vec 一起使用

Google Research 10 年前发表了一篇有影响力的论文,描述了他们创建的名为 Word2Vec 的早期嵌入模型。

这篇论文是 Efficient Estimation of Word Representations in Vector Space,发表日期为 2013 年 1 月 16 日。这篇论文引发了人们对嵌入的广泛兴趣。

Word2Vec 是一个模型,它将单个单词转换为包含 300 个数字的列表。该数字列表捕获了相关单词的含义。

通过演示可以最好地说明这一点。

Turbomaze.github.io/word2vecjson 是 Anthony Liu 与 Word2Vec 语料库的 10,000 个单词子集组合而成的交互式工具。您可以查看此 JavaScript 文件以查看这 10,000 个单词的 JSON 及其关联的 300 长数字数组。

Untitled

搜索单词以根据与其 Word2Vec 表示形式的余弦距离查找相似单词。例如,单词“france”会返回以下相关结果:

Screenshot 2024-03-04 at 08.26.54.png

这是法国事物和欧洲地理的混合体。

您可以在这里做的一件非常有趣的事情是对这些向量执行算术运算。

获取“德国”的向量,添加“巴黎”并减去“法国”。得到的向量最接近“berlin”!

这个模型的某些部分已经捕捉到了民族和地理的概念,以至于你可以使用算术来探索关于世界的更多事实。

Word2Vec 使用 16 亿字的内容进行训练。我们今天使用的嵌入模型是在更大的数据集上进行训练的,并且可以更丰富地理解底层关系。

5. 使用我的 LLM 工具计算嵌入

我一直在构建一个名为 LLM 的命令行实用程序和 Python 库。

您可以在此处阅读有关 LLM 的更多信息:

LLM 是一个用于处理大型语言模型的工具。你可以像这样安装它: pip install llm

Or via Homebrew: 或者通过自制程序: brew install llm

您可以将其用作与 LLMs 交互的命令行工具,或用作 Python 库。

它开箱即用,可以与 OpenAI API 配合使用。设置 API 密钥,您可以运行如下命令: llm 'ten fun names for a pet pelican'

真正有趣的是当你开始安装插件时。有些插件可以向其中添加全新的语言模型,包括直接在您自己的计算机上运行的模型。

几个月前,我扩展了 LLM 以支持也可以运行嵌入模型的插件。

以下是如何使用 LLM 运行标题醒目的 all-MiniLM-L6-v2 模型:

Untitled

首先,我们安装 llm ,然后使用它来安装 llm-sentence-transformers 插件 - SentenceTransformers 库的包装器。

pip install llm
llm install llm-sentence-transformers

接下来我们需要注册 all-MiniLM-L6-v2 模型。这会将模型从 Hugging Face 下载到您的计算机:

llm sentence-transformers register all-MiniLM-L6-v2

我们可以通过嵌入一个句子来测试它,如下所示:

llm embed -m sentence-transformers/all-MiniLM-L6-v2 \
  -c 'Hello world'

这会输出一个 JSON 数组,其开头如下:

[-0.03447725251317024, 0.031023245304822922, 0.006734962109476328, 0.026108916848897934, -0.03936201333999634, ...

像这样的嵌入本身并不是很有趣——我们需要存储和比较它们才能开始获得有用的结果。

LLM 可以将嵌入存储在“集合”(SQLite 表)中。 embed-multi 命令可用于一次嵌入多条内容并将它们存储在一个集合中。

这就是下一个命令的作用:

llm embed-multi readmes \
  --model sentence-transformers/all-MiniLM-L6-v2 \
  --files ~/ '**/README.md' --store

在这里,我们正在填充一个名为“自述文件”的集合。

--files 选项有两个参数:要搜索的目录和与文件名匹配的 glob 模式。在本例中,我正在我的主目录中递归搜索任何名为 README.md 的文件。

--store 选项导致 LLM 除了嵌入向量之外还将原始文本存储在 SQLite 表中。

该命令在我的计算机上运行大约需要 30 分钟,但它有效!我现在有一个名为 readmes 的集合,其中包含 16,796 行 - 它对应在我的主目录中找到的每个 README.md 文件。

6. 基于 Vibes 的搜索

现在我们有了嵌入集合,我们可以使用 llm 类似命令对其进行搜索:

llm similar readmes -c 'sqlite backup tools' | jq .id

llm similar readmes -c 'sqlite backup tools' | jq .id

我们要求 readmes 集合中的项目类似于短语“sqlite backup tools”的嵌入向量。

该命令默认输出 JSON,其中包括 README 文件的全文,因为我们之前使用 --store 存储了它们。

通过 jq .id 管道传输结果会导致命令仅输出匹配行的 ID。

排名靠前的匹配结果是:

"sqlite-diffable/README.md"
"sqlite-dump/README.md"
"ftstri/salite/ext/repair/README.md"
"simonw/README.md"
"sqlite-generate/README.md"
"sqlite-history/README.md"
"dbf-to-sqlite/README.md"
"ftstri/sqlite/ext/README.md"
"sqlite-utils/README.md"
"ftstri/sqlite/README.md"

这些都是很好的结果!这些自述文件中的每一个都描述了用于处理 SQLite 备份的工具或以某种方式与备份相关的项目。

有趣的是,不能保证术语“备份”直接出现在这些自述文件的文本中。内容在语义上与该短语相似,但可能与文本不完全匹配。

我们可以称之为语义搜索。我喜欢将其视为基于振动的搜索。

根据单词含义的这种奇怪的多维空间表示,这些自述文件的氛围与我们的搜索词相关。

这非常有用。如果您曾经为网站构建过搜索引擎,您就会知道精确匹配并不总能帮助人们找到他们正在寻找的内容。

我们可以使用这种语义搜索来为一大堆不同类型的内容构建更好的搜索引擎。

7. 使用 Symbex 嵌入代码

我一直在构建的另一个工具称为 Symbex。它是一个用于探索 Python 代码库中符号的工具。

我最初构建它是为了帮助快速查找 Python 函数和类,并将它们通过管道传输到 LLMs 中以帮助解释和重写它们。

然后我意识到我可以使用它来计算代码库中所有函数的嵌入,并使用这些嵌入来构建代码搜索引擎。

我添加了一项功能,可以输出表示找到的符号的 JSON 或 CSV,使用与 llm embed-multi 可以用作输入的相同输出格式。

以下是我如何使用新发布的名为 gte-tiny 的模型构建 Datasette 项目中所有函数的集合 - 只是一个 60MB 的文件!

llm sentence-transformers register TaylorAI/gte-tiny

cd datasette/datasette

symbex '*' '*:*' --nl | \
  llm embed-multi functions - \
  --model sentence-transformers/TaylorAI/gte-tiny \
  --format nl \
  --store
symbex '*' '*:*' --nl finds all functions (*) and class methods (the *:* pattern) in the current directory and outputs them as newline-delimited JSON.

symbex '*' '*:*' --nl 查找当前目录中的所有函数 ( * ) 和类方法( *:* 模式),并将它们输出为换行符分隔的 JSON。

llm embed-multi ... --format nl 命令需要以换行符分隔的 JSON 作为输入,因此我们可以将 symbex 的输出直接通过管道传输到其中。

默认情况下将嵌入存储在默认的 LLM SQLite 数据库中。您可以添加 --database /tmp/data.db 来指定替代位置。

现在…我可以针对我的代码库运行基于振动的语义搜索!

我可以使用 llm similar 命令来执行此操作,但我也可以使用 Datasette 本身运行这些搜索。

这是一个 SQL 查询,使用之前的 datasette-llm-embed 插件:

with input as (
  select
    llm_embed(
      'sentence-transformers/TaylorAI/gte-tiny',
      :input
    ) as e
)
select
  id,
  content
from
  embeddings,
  input
where
  collection_id = (
    select id from collections where name = 'functions'
  )
order by
  llm_embed_cosine(embedding, input.e) desc
limit 5

:input 参数会被 Datasette 自动转换为表单字段。

当我运行它时,我得到与列出插件概念相关的函数:

Untitled

这里的关键思想是使用 SQLite 作为集成点——将多个工具组合在一起的基础。

我可以运行单独的工具,从代码库中提取函数,通过嵌入模型运行它们,将这些嵌入写入 SQLite,然后对结果运行查询。

现在,任何可以通过管道传输到工具中的内容都可以由该生态系统的其他组件嵌入和处理。

8. 使用 CLIP 将文本和图像嵌入在一起

我目前最喜欢的嵌入模型是 CLIP

CLIP 是 OpenAI 于 2021 年 1 月发布的一个令人着迷的模型,当时他们仍在公开地做大多数事情,可以嵌入文本和图像。

至关重要的是,它将它们嵌入到同一个向量空间中。

如果嵌入字符串“dog”,您将获得 512 维空间中的位置(取决于您的 CLIP 配置)。

如果您嵌入狗的照片,您将在同一空间中获得一个位置……并且它与字符串“dog”的位置的距离很接近!

这意味着我们可以使用文本搜索相关图像,也可以使用图像搜索相关文本。

我构建了一个交互式演示来帮助解释其工作原理。该演示是一个 Observable 笔记本,直接在浏览器中运行 CLIP 模型。

这是一个相当重的页面 - 它必须加载 158MB 的资源(CLIP 文本模型为 64.6MB,图像模型为 87.6MB) - 但一旦加载,您可以使用它来嵌入图像,然后嵌入文本字符串并计算两者之间的距离。

我可以给它这张我拍的海滩照片:

Untitled

然后输入不同的文本字符串来计算相似度得分,此处显示为百分比:

Untitled

Screenshot 2024-03-04 at 08.38.57.png

令人惊奇的是,我们可以在浏览器中运行的 JavaScript 中完成所有这些工作!

有一个明显的问题:能够拍摄任意照片并说“这与‘城市’一词有多相似?”实际上并没有那么有用。

诀窍是在此基础上构建额外的接口。我们再一次有能力构建基于振动的搜索引擎。

这是其中一个很好的例子。

9. 水龙头查找器:使用 CLIP 查找水龙头

Drew Breunig 使用 LLM 和我的 llm-clip 插件构建了一个水龙头搜索引擎。

他正在装修浴室,需要购买新水龙头。因此,他从一家水龙头供应公司收集了 20,000 张水龙头照片,并对所有这些照片运行 CLIP。

他使用该结果构建了 Faucet Finder——一个自定义工具(使用 Datasette 部署),用于查找与其他水龙头相似的水龙头。

Untitled

除此之外,这意味着您可以找到您喜欢的昂贵水龙头,然后寻找视觉上相似的更便宜的选择!

Drew在《寻找带有嵌入物的浴室水龙头》中详细介绍了他的项目。

Drew 的演示使用预先计算的嵌入来显示类似的结果,而无需在服务器上运行 CLIP 模型。

受此启发,我花了一些时间研究如何部署由我自己的 Fly.io 帐户托管的服务器端 CLIP 模型

Drew 的 Datasette 实例包含通过 Datasette API 公开的嵌入向量表。

我使用此 API 部署了自己的实例来嵌入文本字符串,然后构建了一个 Observable 笔记本演示,该演示可以访问这两个 API 并组合结果。

observablehq.com/@simonw/search-for-faucets-with-clip-apiobservablehq.com/@simonw/search-for-faucets-with-clip-api

现在我可以搜索“金紫色”之类的内容并获取基于振动的水龙头结果:

Untitled

能够在几个小时内启动这种超特定的搜索引擎正是让我对将嵌入作为工具箱中的工具感到兴奋的技巧。

10. 聚类嵌入

相关内容和基于语义/振动的搜索是嵌入的两个最常见的应用,但是您也可以用它们做很多其他巧妙的事情。

其中之一是聚类。

我为此构建了一个名为 llm-cluster 的插件,它使用 scikit-learn 中的 sklearn.cluster 来实现此功能。

为了证明这一点,我使用 paginate-json 工具和 GitHub issues API 将 simonw/llm 存储库中所有问题的标题收集到名为 llm-issues 的集合中:

paginate-json 'https://api.github.com/repos/simonw/llm/issues?state=all&filter=all' \
  | jq '[.[] | {id: .id, title: .title}]' \
  | llm embed-multi llm-issues - \
  --store

现在我可以创建 10 个问题集群,如下所示:

llm install llm-cluster

llm cluster llm-issues 10

簇以 JSON 数组的形式输出,输出如下所示(已截断):

[
  {
    "id": "2",
    "items": [
      {
        "id": "1650662628",
        "content": "Initial design"
      },
      {
        "id": "1650682379",
        "content": "Log prompts and responses to SQLite"
      }
    ]
  },
  {
    "id": "4",
    "items": [
      {
        "id": "1650760699",
        "content": "llm web command - launches a web server"
      },
      {
        "id": "1759659476",
        "content": "`llm models` command"
      },
      {
        "id": "1784156919",
        "content": "`llm.get_model(alias)` helper"
      }
    ]
  },
  {
    "id": "7",
    "items": [
      {
        "id": "1650765575",
        "content": "--code mode for outputting code"
      },
      {
        "id": "1659086298",
        "content": "Accept PROMPT from --stdin"
      },
      {
        "id": "1714651657",
        "content": "Accept input from standard in"
      }
    ]
  }
]

这些看起来确实相关,但我们可以做得更好。 llm cluster 命令有一个 --summary 选项,该选项使其通过 LLM 传递生成的簇文本,并使用它为每个簇生成描述性名称:

llm cluster llm-issues 10 --summary

这会返回“日志管理和交互式提示跟踪”和“持续对话机制和管理”等名称。有关更多详细信息,请参阅自述文件。

11. 通过主成分分析进行二维可视化

大规模多维空间的问题在于它很难可视化。

我们可以使用一种称为主成分分析的技术将数据的维度减少到更易于管理的大小,结果表明较低的维度可以继续捕获有关内容的有用语义。

Matt Webb 使用 OpenAI 嵌入模型生成嵌入,用于描述 BBC 的 In Our Time 播客的每一集。他使用这些来查找相关剧集,同时还对它们运行 PCA 以创建交互式 2D 可视化

Untitled

将 1,536 维减少到只有 2 维仍然可以产生一种有意义的数据探索方式!有关历史战争的剧集彼此相邻,其他地方则有一系列有关现代科学发现的剧集。

Matt 在通过 Dewey 十进制代码浏览 BBC In Our Time 档案中写了更多相关内容。

12. 使用平均位置对句子进行评分

嵌入的另一个技巧是使用它们进行分类。

首先计算以某种方式分类的一组嵌入的平均位置,然后将新内容的嵌入与这些位置进行比较,以将其分配到一个类别。

Amelia Wattenberger 在“利用嵌入发挥创意”中展示了一个很好的例子。

她希望通过鼓励具体和抽象句子的混合来帮助人们提高写作水平。但是如何判断文本句子是具体的还是抽象的呢?

她的技巧是生成两种类型句子的样本,计算它们的平均位置,然后根据它们与新定义的频谱两端的接近程度对新句子进行评分。

Untitled

该分数甚至可以转换为松散地表示给定句子的抽象或具体程度的颜色!

这是一个非常简洁的演示,展示了您可以在这项技术的基础上开始构建的创意界面。

13. 用检索增强一代回答问题

我将以最初让我对嵌入感到兴奋的想法作为结束语。每个尝试 ChatGPT 的人最终都会问同样的问题:我如何使用这个版本来回答基于我自己的私人笔记或我公司拥有的内部文档的问题?

人们认为答案是在该内容的基础上训练自定义模型,这可能会花费巨大的代价。事实证明这实际上没有必要。您可以使用现成的大型语言模型模型(托管模型或本地运行的模型)和一种称为检索增强生成(RAG)的技巧。

关键思想是:用户提出问题。您在私人文档中搜索与问题相关的内容,然后将该内容的摘录与原始问题一起粘贴到 LLM(遵守其大小限制,通常在 3,000 到 6,000 字之间)。然后,LLM 可以根据您提供的附加内容回答问题。

这个廉价的伎俩却非常有效。获得这个工作的基本版本很简单——挑战在于考虑到用户可能会问的无数问题,让它尽可能好地工作。

RAG 中的关键问题是找出包含在 LLM 提示中的最佳内容摘录。由嵌入提供支持的“基于 Vibes”的语义搜索正是您收集潜在相关内容以帮助回答用户问题所需的东西。

我根据我的博客内容构建了一个版本,详细描述见使用 E5-large-v2 嵌入我的博客段落。为此,我使用了名为 E5-large-v2 的模型。这是一个根据具体用例进行训练的模型。查找与问题相关的内容的一个挑战是,用户的问题“什么是 shot-scraper?”不能保证被认为在语义上与回答该问题的内容相似。问题和断言具有不同的语法。

E5-large-v2 通过支持两种类型的内容来解决这个问题。您可以在同一空间中嵌入短语(事实句子)和查询(问题),类似于 CLIP 支持图像和文本的方式。我从博客中嵌入了 19,000 个文本段落作为短语,现在我可以嵌入一个问题作为查询,并使用它来查找最有可能回答该问题的段落。

结果是 RAG 作为单行 Bash 脚本实现:

llm similar blog-paragraphs -c "query: $1" \
  | jq '.content | sub("passage: "; "")' -r \
  | llm -m mlc-chat-Llama-2-7b-chat-hf-q4f16_1 \
  "$1" -s 'You answer questions as a single paragraph'

此示例使用在我的笔记本电脑上运行的 Llama 2 Chat 7B(带有 llm-mlc 插件),因此我能够使用在我的笔记本电脑上运行的代码完全离线回答问题。

运行这个:

./blog-answer.sh 'What is shot-scraper?'

输出这个:

Shot-scraper 是一个包装 Playwright 的 Python 实用程序,提供命令行界面和 YAML 驱动的配置流程,用于自动执行网页屏幕截图并使用 JavaScript 从中抓取数据的过程。它可用于拍摄一次性屏幕截图,或通过在 YAML 文件中定义它们以可重复的方式拍摄多个屏幕截图。此外,它还可用于在页面上执行 JavaScript 并返回结果值。

这是对我的镜头抓取工具的一个很好的描述。我检查过,没有一个输出与我之前在博客上发布的内容完全匹配。

14. Q&A

我的演讲以问答环节结束。以下是总结的问题和答案。

  • LangChain 如何融入其中?

    LangChain 是一个流行的框架,用于在 LLMs 之上实现功能。它涵盖了很多领域——我对浪链的唯一问题是你必须投入大量的工作来理解它是如何工作的以及它能为你做什么。检索增强生成是LangChain的关键功能之一,所以如果您投入精力,我今天向您展示的很多东西都可以构建在LangChain之上。

    我的理念与 LangChain 不同,我专注于构建一套可以协同工作的小工具,而不是一次性解决所有问题的单一框架。

  • 您是否尝试过余弦相似度以外的距离函数?

    我没有。余弦相似度是其他人似乎都在使用的默认函数,我还没有花任何时间探索其他选项。

    实际上,我让 ChatGPT 跨 Python 和 JavaScript 编写了所有不同版本的余弦相似度!

    RAG 的一个令人着迷的地方是它有很多不同的旋钮可供您调整。你可以尝试不同的距离函数、不同的嵌入模型、不同的提示策略和不同的LLMs。这里有很大的实验空间。

  • 如果你有 10 亿个对象,你需要调整什么?

    我今天展示的演示都很小——最多大约 20,000 个嵌入。它足够小,您可以对所有内容运行强力余弦相似函数,并在合理的时间内返回结果。

    如果您要处理更多数据,有越来越多的选项可以提供帮助。

    许多初创公司正在推出新的“向量数据库”——这些数据库实际上是定制的,可以尽快回答针对向量的最近邻查询。

    我不相信您需要一个全新的数据库:我更兴奋的是向现有数据库添加自定义索引。例如,SQLite 有 sqlite-vss,PostgreSQL 有 pgvector。

    我还使用 Facebook 的 FAISS 库做了一些成功的实验,包括构建一个使用它的 Datasette 插件,称为 datasette-faiss

  • 您很高兴看到嵌入模型的哪些改进?

    我对多模式模型感到非常兴奋。 CLIP 是一个很好的例子,但我也一直在尝试 Facebook 的 ImageBind,它“学习跨六种不同模式(图像、文本、音频、深度、热和 IMU 数据)的联合嵌入。”看起来我们可以走得更远,而不仅仅是图像和文本!

    我也喜欢这些型号越来越小的趋势。我之前演示了一个新模型,gtr-tiny,只有 60MB。能够在受限设备或浏览器中运行这些东西对我来说真的很令人兴奋。

15. 进一步阅读

如果您想更深入地了解嵌入如何工作的底层细节,我建议如下: