本文为 Simon Willison 在 PyBay 上发表的演讲视频的文字改进版
原文链接:
https://simonwillison.net/2023/Oct/23/embeddings/
演讲视频链接:
https://youtu.be/ArnMdc-ICCM
Embedding 是一个非常巧妙的技术,但常常和一堆令人生畏的术语一起出现。如果你能理解这些术语,它们就会为你打开强大的技术的大门,这些技术可以应用于各种有趣的问题。
如果你还不熟悉 Embedding,希望这篇文章能够为你提供将它们应用于实际问题所需的一切知识。
本文将围绕以下问题展开:
- 什么是 Embedding?
- 使用 Embedding 的相关内容
- 探索这些工具如何与 Word2Vec 一起工作
- 使用 LLM 工具计算Embedding
- 基于 Vibes 的搜索
- 使用 Symbex Embedding 代码
- 使用 CLIP 将文本和图像 Embedding 在一起
- 水龙头查找器:使用 CLIP 查找水龙头
- 聚类 Embedding
- 通过主成分分析进行二维可视化
- 使用平均位置对句子进行评分
- 使用检索增强生成回答问题
什么是Embedding
Embedding 是一项与更广泛的大型语言模型领域相邻的技术——ChatGPT 和 Bard and Claude 背后的技术。
Embedding 基于一个技巧:获取一段内容并将该内容转换为浮点数数组,如上图。
该数组的关键在于,无论内容有多长,它的长度始终相同。长度由你使用的 Embedding 模型定义,数组的长度可能是 300、1000 或 1536 个数字。
理解这个数字数组的最好方法是将其想象为一个非常奇怪的多维空间中的坐标。
我们很难可视化 1536 维空间,但是我们可以用可视化的3维空间来理解,为什么要在这个空间放置内容?因为我们可以根据内容所处的位置(特别是附近的其他内容)了解该内容相关的信息。
根据 Embedding 模型对世界的抽象且大多难以理解的理解,空间内的位置代表了内容的语义含义。它可能会捕获已 Embedding 内容的颜色、形状、概念或各种其他特征。没有人能够完全理解这些单独数字的含义,但我们知道它们的位置可用于查找有关内容的信息。
使用Embedding的相关内容
作者使用 Embedding 解决的第一个问题是为他的 TIL 博客构建“相关内容”功能。他希望能够在每个页面的底部显示相关文章的列表。
作者使用 Embedding 来完成此操作:
本例中使用了 OpenAItext-embedding-ada-002模型,该模型可通过 OpenAI 官网的 API获得。
目前作者的网站上有 472 篇文章,并计算了每篇文章的 1536 维 Embedding 向量(浮点数数组),并将这些向量存储在他网站的 SQLite 数据库中。
现在,如果想查找给定文章的相关文章,我可以计算该文章的 Embedding 向量与数据库中所有其他文章之间的余弦相似度,然后按距离返回 10 个最接近的匹配项。
这是用来计算这些余弦相似距离的Python 函数:
def cosine_similarity ( a , b ):
dot_product = sum ( x * y for x , y in zip ( a , b ))
模量_a = sum ( x * x for x in a ) ** 0.5模量_b = sum ( x * x for b中的x ) ** 0.5返回点积/ (幅度_a *幅度_b )
作者的 TIL 站点在他的 Datasette Python 框架上运行,该框架支持在 SQLite 数据库之上构建站点。文末会附有关于它的文章。
我们可以浏览将计算的 embedding 存储在 tils/embeddings 的 SQLite 表。
这些是二进制值。我们可以运行此 SQL 查询以十六进制形式查看它们:
从 Embedding 中选择 id、十六进制(Embedding)
但这仍然不太可读。我们可以使用llm_embed_decode()自定义 SQL 函数将它们转换为 JSON 数组:
从 Embedding 限制10中选择 id、llm_embed_decode(embedding)
它显示每篇文章都附有 1536 个浮点数的数组。
我们可以使用另一个自定义 SQL 函数llm_embed_cosine(vector1, vector2)来计算这些余弦距离并找到最相似的内容。该 SQL 函数在 datasette-llm-embed 插件中定义。
以下查询返回与我的 SQLite TG 文章最相似的五篇文章:
执行该查询将返回以下结果:
正如预期的那样,该文章与其自身的相似度为 1.0。其他文章都与 SQLite 中的地理空间 SQL 查询相关。
该查询的执行时间大约为 400 毫秒。为了加快速度,作者预先计算了每篇文章的前 10 个相似之处,并将它们存储在一个名为 tils/similarities 的单独表中。
作者编写了一个 Python 函数来从该表中查找相关文档,并从用于呈现文章页面的模板中调用它。
在这个项目中使用了 OpenAI Embedding API,Embedding 了大约 402500 个代币,按 0.0001 美元/1,000 个代币计算,相当于只需 4 美分!它使用起来真的很方便,将一些文本与 API 密钥一起发布,就会返回浮点数的 JSON 数组。
但是,这是一个专有模型。几个月前,OpenAI 关闭了一些旧的 Embedding 模型,如果你存储了这些模型的大量Embedding,这将是一个问题,因为如果你希望能够 Embedding,则需要根据支持的模型重新计算它们还有什么新的东西。
尽管 OpenAI 承诺会承担用户使用这些新模型重新 Embedding 内容的财务成本,但这仍然是对依赖专有模型持谨慎态度的一个理由。好消息是,有非常强大的开放许可模型,你可以在自己的硬件上运行它们,从而避免它们被关闭的任何风险。我们稍后会详细讨论这个问题。
探索这些工具如何与 Word2Vec 一起工作
Google Research 10 年前发表了一篇有影响力的论文,描述了他们创建的名为 Word2Vec 的早期 Embedding 模型。这篇论文是 Efficient Estimation of Word Representations in Vector Space,发表日期为 2013 年 1 月 16 日。这篇论文引发了人们对 Embedding 的广泛兴趣。
Word2Vec 是一个模型,它将单个单词转换为包含 300 个数字的列表。该数字列表捕获了相关单词的含义。
通过演示可以最好地说明这一点。
搜索单词以根据与其 Word2Vec 表示形式的余弦距离查找相似单词。例如,单词“france”会返回以下相关结果:
这是与法国有关的事物和欧洲地理的混合体。
可以在这里做的一件非常有趣的事情是对这些向量执行算术运算。
获取“德国”的向量,添加“巴黎”并减去“法国”。得到的向量最接近“berlin”!
这个模型的某些部分已经捕捉到了民族和地理的概念,以至于你可以使用算术来探索关于世界的更多事实。
Word2Vec 使用 16 亿字的内容进行训练。我们今天使用的 Embedding 模型是在更大的数据集上进行训练的,并且可以更丰富地理解底层关系。
使用 LLM 工具计算Embedding
作者一直在构建一个名为 LLM 的命令行实用程序和 Python 库。几个月前,作者扩展了 LLM 以支持也可以运行 Embedding 模型的插件。
以下是如何使用 LLM运行名为 all-MiniLM-L6-v2 的模型:
首先,我们安装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
我们可以通过 Embedding 一个句子来测试它,如下所示:
llm embed -m sentence-transformers/all-MiniLM-L6-v2 \
-c 'Hello world'
这会输出一个 JSON 数组,其开头如下:
[-0.03447725251317024,0.031023245304822922, 0.006734962109476328,0.026108916848897934, -0.03936201333999634, …
像这样的 Embedding 本身并不是很有趣,我们需要存储和比较它们才能开始获得有用的结果。
LLM 可以将 Embedding 存储在“集合”(SQLite 表)中。embed -multi 命令可用于一次 Embedding 多条内容并将它们存储在一个集合中。
这就是下一个命令的作用:
在这里,我们正在填充一个名为“readmes”的集合。
该–files选项有两个参数:要搜索的目录和与文件名匹配的全局模式。本例正在递归搜索主目录中任何名为README.md的文件。
该–store选项使 LLM 除了 Embedding 向量之外还将原始文本存储在 SQLite 表中。
该命令在计算机上运行大约需要 30 分钟。现在我们有了一个名叫readmes的 16796 行的集合,它代表README.md在我们的主目录中找到的每个文件。
基于 Vibes 的搜索
现在我们有了 Embedding 集合,我们可以使用 llm 类似命令对其进行搜索:
llm similar readmes -c 'sqlite backup tools' | jq .id
我们要求readmes集合中的项目类似于短语“sqlite backup tools”的 Embedding 向量。
该命令默认输出 JSON,其中包括 README 文件的全文,因为我们–store之前使用它们进行了存储。
通过管道传输结果jq .id会导致命令仅输出匹配行的 ID。
排名靠前的匹配结果是:
这些都是很好的结果!这些自述文件中的每一个都描述了用于处理 SQLite 备份的工具或以某种方式与备份相关的项目。
有趣的是,不能保证术语“备份”直接出现在这些自述文件的文本中。内容在语义上与该短语相似,但可能与文本不完全匹配。
我们可以称之为语义搜索。作者喜欢将其视为基于振动的搜索。
根据单词含义的这种奇怪的多维空间表示,这些自述文件的氛围与我们的搜索词相关。
这非常有用。如果你曾经为网站构建过搜索引擎,你就会知道精确匹配并不总能帮助人们找到他们正在寻找的内容。
我们可以使用这种语义搜索来为一大堆不同类型的内容构建更好的搜索引擎。
使用 Symbex Embedding代码
作者一直在构建的另一个工具称为 Symbex。它是一个用于探索 Python 代码库中符号的工具。
最初构建它是为了帮助快速找到 Python 函数和类,并将它们输送到 LLM 中以帮助解释和重写它们。
然后意识到可以使用它来计算代码库中所有函数的 Embedding,并使用这些 Embedding 来构建代码搜索引擎。
新添加的 llm embed-multi 功能可以使用可用作输入的相同输出格式输出表示找到的符号的 JSON 或 CSV。
以下是如何使用新发布的名为 gte-tiny 的模型构建 Datasette 项目中所有函数的集合。
symbex '’ ':’ --nl 查找当前目录中的所有函数 ( ) 和类方法(模式),并将它们输出为换行符分隔的 JSON。:
该 llm embed-multi … --format nl 命令需要以换行符分隔的 JSON 作为输入,因此我们可以将输出 symbex 直接通过管道传输到其中。
默认情况下将 Embedding 存储在默认的 LLM SQLite 数据库中。你可以添加 --database /tmp/data.db 以指定替代位置。
现在,可以针对代码库运行基于振动的语义搜索!
我们可以使用 llm similar 该命令来执行此操作,但也可以使用 Datasette 本身来运行这些搜索。
这是一个 SQL 查询,使用之前的 datasette-llm-embed 插件:
Datasette 会自动将参数:input转换为表单字段。
当运行它时,我们会得到与列出插件概念相关的函数:
这里的关键思想是使用 SQLite 作为集成点,这是将多个工具组合在一起的基础。
我们可以运行单独的工具,从代码库中提取函数,通过 Embedding 模型运行它们,将这些 Embedding 写入 SQLite,然后对结果运行查询。
现在,任何可以通过管道传输到工具中的内容都可以由该生态系统的其他组件 Embedding 和处理。
使用 CLIP 将文本和图像Embedding在一起
作者目前最喜欢的 Embedding 模型是 CLIP。
CLIP 是 OpenAI 于 2021 年 1 月发布的一个令人着迷的模型,当时他们仍在公开地做大多数事情,可以 Embedding 文本和图像。
至关重要的是,它将文字和图像 Embedding 到同一个向量空间中。
如果 Embedding 字符串“dog”,你将获得 512 维空间中的位置(取决于你的 CLIP 配置)。
如果你 Embedding 狗的照片,你将在同一空间中获得一个位置,并且它与字符串“dog”的位置的距离很接近!
这意味着我们可以使用文本搜索相关图像,也可以使用图像搜索相关文本。
下面构建了一个交互式演示来帮助解释其工作原理。该演示是一个 Observable 笔记本,直接在浏览器中运行 CLIP 模型。
这是一个相当大型的页面,它必须加载 158MB 的资源(CLIP 文本模型为 64.6MB,图像模型为 87.6MB),但一旦加载,你可以使用它来 Embedding 图像,然后 Embedding 文本字符串并计算两者之间的距离。
可以给它这张海滩照片:
然后输入不同的文本字符串来计算相似度得分,此处显示为百分比:
令人惊奇的是,我们可以在浏览器中运行的 JavaScript 中完成所有这些工作!
有一个明显的问题:能够拍摄任意照片并说“这与‘城市’一词有多相似?”实际上并没有那么有用。
诀窍是在此基础上构建额外的接口。我们再一次有能力构建基于振动的搜索引擎。
这是其中一个很好的例子。
水龙头查找器:使用 CLIP 查找水龙头
Drew Breunig 使用 LLM 和作者的 llm-clip 插件构建了一个水龙头搜索引擎。
他正在装修浴室,需要购买新水龙头。因此,他从一家水龙头供应公司收集了 20000 张水龙头照片,并对所有这些照片运行 CLIP。
他利用该结果构建了 Faucet Finder——一个自定义工具(使用 Datasette 部署),用于查找与其他水龙头相似的水龙头。
除此之外,这意味着你可以找到你喜欢的昂贵水龙头,然后寻找视觉上相似的更便宜的选择!
Drew 在 Find Bathroom Faucets with Embeddings 中详细介绍了他的项目。
Drew 的演示使用预先计算的 Embedding 来显示类似的结果,而无需在服务器上运行 CLIP 模型。
受此启发,作者花了一些时间研究如何部署由他自己的 Fly.io 帐户托管的服务器端 CLIP 模型。
Drew 的 Datasette 实例包含的 Embedding 向量表通过 Datasette API 公开。
作者使用此 API 部署了自己的实例来 Embedding 文本字符串,然后构建了一个 Observable 笔记本演示,该演示可以访问这两个 API 并组合结果。
observablehq.com/@simonw/search-for-faucets-with-clip-api
现在可以搜索“金紫色”之类的内容并获取相关的结果:
能够在几个小时内启动这种超特定的搜索引擎正是 Embedding 较为实用的技巧。
聚类Embedding
相关内容和基于语义/振动的搜索是 Embedding 的两个最常见的应用,但是你也可以用它们做很多其他巧妙的事情。
其中之一是聚类。
为此构建了一个名为 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 数组输出,输出看起来像这样(部分内容):
这些看起来确实相关,但我们可以做得更好。该llm cluster命令有一个–summary选项,使其通过 LLM 传递生成的簇文本,并使用它为每个簇生成描述性名称:
llm cluster llm-issues 10 --summary
这会返回“日志管理和交互式提示跟踪”和“持续对话机制和管理”等名称。有关更多详细信息,请参阅文末 GitHub 项目链接。
通过主成分分析进行二维可视化
大规模多维空间的问题在于它很难可视化。
我们可以使用一种称为主成分分析的技术将数据的维度减少到更易于管理的大小,结果表明较低的维度可以继续捕获有关内容的有用语义。
Matt Webb 使用 OpenAI Embedding 模型生成 Embedding,用于描述 BBC 的 In Our Time 播客的每一集。他使用这些来查找相关剧集,同时还针对它们运行 PCA 以创建交互式 2D 可视化。
将 1,536 维减少到只有 2 维仍然可以产生一种有意义的数据探索方式!有关历史战争的剧集彼此相邻,其他地方则有一系列有关现代科学发现的剧集。
Matt 在浏览杜威十进制代码的 BBC In Our Time 档案中对此进行了更多介绍。
使用平均位置对句子进行评分
Embedding 的另一个技巧是使用它们进行分类。
首先计算以某种方式分类的一组 Embedding 的平均位置,然后将新内容的 Embedding 与这些位置进行比较,以将其分配到一个类别。
Amelia Wattenberger 在“利用Embedding发挥创意”中展示了一个很好的例子。
她希望通过鼓励具体和抽象句子的混合来帮助人们提高写作水平。但是如何判断文本句子是具体的还是抽象的呢?
她的技巧是生成两种类型句子的样本,计算它们的平均位置,然后根据它们与新定义的频谱两端的接近程度对新句子进行评分。
该分数甚至可以转换为松散地表示给定句子的抽象或具体程度的颜色!
这是一个非常简洁的演示,展示了你可以在这项技术的基础上开始构建的创意界面。
使用检索增强生成回答问题
每个尝试 ChatGPT 的人最终都会问同样的问题:我如何使用这个版本来回答基于我自己的私人笔记或我公司拥有的内部文档的问题?
人们认为答案是在该内容的基础上训练自定义模型,这可能会花费巨大的代价。
事实证明这实际上没有必要。你可以使用现成的大型语言模型模型(托管模型或本地运行的模型)和一种称为检索增强生成(RAG)的技巧。
关键思想是:用户提出问题。你在私人文档中搜索与问题相关的内容,然后将该内容的摘录与原始问题一起粘贴到 LLM 中(遵守其大小限制,通常在 3,000 到 6,000 个单词之间)。
然后,LLM 可以根据你提供的附加内容回答问题。
这个廉价的技巧却非常有效。获得此功能的基本版本很简单,挑战在于考虑到用户可能会问的无数问题,使其尽可能良好地工作。
RAG 的关键问题是找出最好的内容摘录,以包含在 LLM 的提示中。
由 Embedding 提供支持的“基于振动”的语义搜索正是收集潜在相关内容以帮助回答用户问题所需的东西。
我们可以使用名为 E5-large-v2 的模型。这是一个根据具体用例进行训练的模型。
查找与问题相关的内容的一个挑战是,用户的问题“什么是 shot-scraper?”不能保证被认为在语义上与回答该问题的内容相似。问题和断言具有不同的语法。
E5-large-v2 通过支持两种类型的内容来解决这个问题。你可以在同一空间中 Embedding 短语(事实句子)和查询(问题),类似于 CLIP 支持图像和文本的方式。
作者从博客中 Embedding 了 19,000 个文本段落作为短语,现在可以 Embedding 一个问题作为查询,并使用它来查找最有可能回答该问题的段落。
结果是 RAG 作为单行 Bash 脚本实现:
此示例使用了作者的笔记本电脑上运行的 Llama 2 Chat 7B(带有 llm-mlc 插件),因此他能够使用在他的笔记本电脑上运行的代码完全离线回答问题。
运行这个:
./blog-answer.sh 'What is shot-scraper?'
输出这个:
这是对这个镜头抓取工具的一个很好的描述,没有一个输出与之前在博客上发布的内容完全匹配。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。