Chroma DB指南 | 为您的生成式AI LLMs提供的向量存储
Chroma DB指南 | 向量存储生成式AI LLMs
介绍
生成式大型语言模型(如GPT、PaLM等)是通过大量的数据进行训练的。这些模型不会直接使用数据集中的文本,因为计算机无法理解文本,它们只能理解数字。嵌入是文本的表示,但以数字格式呈现。与大型语言模型之间的所有信息通过这些嵌入进行传递。直接访问这些嵌入是耗时的。因此,所谓的向量数据库专门设计用于有效存储和检索向量嵌入。在本指南中,我们将重点介绍一种这样的向量存储/数据库,即广泛使用且开源的Chroma DB。
学习目标
- 使用ChromaDB和嵌入模型生成嵌入
- 在Chroma向量存储中创建集合
- 在集合中存储文档、图像和嵌入
- 执行集合操作,如删除和更新数据,重命名集合
- 最后,查询集合以提取相关信息
本文是数据科学博文马拉松的一部分。
嵌入简介
嵌入或向量嵌入是一种将数据(无论是文本、图像、音频、视频等)以数字格式表示的方式,准确地说是一种在n维空间(数值向量)中表示数据的方式。通过嵌入,我们可以将相似的数据聚集在一起。有一些模型会接受这些输入并将它们转换为向量。一个例子是Word2Vec,它是由Google开发的一种流行的嵌入模型,可以将单词转换为向量(向量是具有n维的点)。所有大型语言模型都有各自的嵌入模型,用于为其生成嵌入。
这些嵌入用于什么目的?
将单词转换为向量的好处是我们可以进行比较。计算机无法直接比较两个单词,但如果我们以数值输入的形式提供它们,即向量嵌入,它就可以进行比较。我们可以创建一个包含具有相似嵌入的单词的聚类。单词“国王”、“皇后”、“王子”和“公主”将出现在一个聚类中,因为它们与其他词相关联。
这样,嵌入可以帮助我们找到与给定单词相似的单词。我们可以将其应用于句子,输入一个句子并从提供的数据中获取相关句子。这是语义搜索、句子相似性、异常检测、聊天机器人等许多用例的基础。我们构建的用于从给定的PDF、文档中进行问答的聊天机器人正是利用了这个嵌入的概念。所有生成式大型语言模型都使用这种方法来获取与提供给它们的查询类似相关的内容。
向量存储和它们的需求
正如前面所讨论的,嵌入是将任何类型的数据(通常是非结构化的数据)以数字格式在n维空间中表示的方式。那么我们应该将它们存储在哪里呢?传统的关系数据库管理系统(RDMS)无法用于存储这些向量嵌入。这就是向量存储/向量数据库发挥作用的地方。向量数据库旨在以高效的方式存储和检索向量嵌入。存在许多不同的向量存储,它们之间的区别在于它们支持的嵌入模型以及它们用于获取相似向量的搜索算法的类型。
为什么我们需要它们?我们需要它们是因为它们能够快速访问我们所需的数据。以基于PDF的聊天机器人为例。当用户输入一个查询时,首先要做的是从PDF中提取与该查询相关的内容,并将这些信息提供给聊天机器人。这样,聊天机器人就可以获取与查询相关的信息,并向用户提供相关的答案。那么我们如何从PDF中获取与用户查询相关的内容呢?答案很简单,就是进行相似性搜索
当数据以向量嵌入的形式表示时,我们可以找到数据的不同部分之间的相似性,并提取与特定嵌入类似的数据。查询首先通过嵌入模型转换为嵌入,然后向量存储接受这个向量嵌入,通过搜索算法在其数据库中与其他嵌入进行相似性搜索,并获取所有相关数据。然后将这些相关的向量嵌入传递给大型语言模型,即聊天机器人,聊天机器人使用这些信息生成最终的答案。
什么是Chroma DB?
Chroma是由Chroma公司提供的向量存储/向量数据库。像其他许多向量存储一样,Chroma DB用于存储和检索向量嵌入。好处是Chroma是一个自由开源项目,这使得世界上其他熟练的开发人员可以提出建议并对数据库进行巨大改进,甚至可以期望在处理开源软件时获得问题的快速回复,因为整个开源社区都在那里查看和解决这个问题。
目前,Chroma不提供任何托管服务。在围绕Chroma创建应用程序时,将数据存储在本地文件系统中。虽然Chroma计划在不久的将来建立一个托管服务。Chroma DB提供了不同的存储向量嵌入的方式。您可以将它们存储在内存中,可以保存和加载它们在内存中,也可以运行Chroma客户端与后端服务器进行通信。总体而言,Chroma DB在API中只有4个功能,因此入门简短、简单且易于使用。
让我们开始使用Chroma DB
在本节中,我们将安装Chroma并查看它提供的所有功能。首先,我们将通过pip命令安装库
$ pip install chromadb
Chroma向量存储API
这将下载用于Python的Chroma向量存储API。通过此包,我们可以执行存储向量嵌入、检索它们以及对给定向量嵌入执行语义搜索等所有任务。
import chromadb
from chromadb.config import Settings
client = chromadb.Client(Settings(chroma_db_impl="duckdb+parquet",
persist_directory="/content/"
))
内存数据库
我们将首先创建一个持久的内存数据库。上述代码将为我们创建一个内存数据库。要创建一个客户端,我们从Chroma DB中获取Client()对象。现在,要创建一个内存数据库,我们使用以下参数配置我们的客户端
- chroma_db_impl = “duckdb+parquet”
- persist_directory = “/content/”
这将创建一个带有parquet文件格式的内存DuckDB数据库。我们提供存储此数据的目录。在这里,我们将数据库保存在/content/文件夹中。因此,每当我们使用此配置连接到Chroma DB客户端时,Chroma DB将在提供的目录中查找现有的数据库并将其加载。如果不存在,则会创建一个新的数据库。当我们关闭连接时,数据将保存到此目录中。
现在,我们将创建一个集合。在向量存储中,集合是我们保存一组向量嵌入、文档和任何元数据(如果有)的地方。在向量数据库中,集合可以被视为关系数据库中的一个表。
创建集合并添加文档
我们现在将创建一个集合并向其添加文档。
collection = client.create_collection("my_information")
collection.add(
documents=["这是一个包含汽车信息的文档",
"这是一个包含有关狗的信息的文档",
"这个文档包含四轮车目录"],
metadatas=[{"source": "汽车书"},{"source": "狗书"},{'source':'车辆信息'}],
ids=["id1", "id2", "id3"]
)
- 我们首先创建一个集合。在这里,我们将集合命名为“my_information”。
- 在这个集合中,我们将添加文档。在这里,我们添加了3个文档,我们的情况下,我们只是添加了三个句子作为三个文档。第一个文档是关于汽车的,第二个是关于狗的,最后一个是关于四轮车的。
- 我们还添加了元数据。提供了所有三个文档的元数据。
- 每个文档都需要有一个唯一的ID,因此我们为它们分别提供了id1、id2和id3
- 所有这些都是add()函数从集合中的变量
- 在运行代码后,将这些文档添加到我们的集合“my_information”
向量数据库
我们了解到,向量数据库中存储的信息是以向量嵌入的形式。但在这里,我们提供的是文本/文本文件,即文档。那么它是如何存储它们的呢?默认情况下,Chroma DB 使用一个all-MiniLM-L6-v2向量嵌入模型为我们创建嵌入。该模型将接受我们的文档并将其转换为向量嵌入。如果我们想使用特定的嵌入函数,如来自HuggingFace或OpenAI的其他句子转换器模型,我们可以在embeddings_function=embedding_function_name变量名下指定它,放在create_collection()方法中。
我们还可以直接将嵌入传递给向量存储,而不是将文档传递给它。就像create_collection中的文档参数一样,我们有一个embedding参数,我们将希望存储在向量数据库中的嵌入传递给它。
所以现在,模型已经成功地将我们的三个文档以向量嵌入的形式存储在向量存储中。现在,我们将看一下如何从中检索相关文档。我们将传递一个查询,并提取与之相关的文档。相应的代码如下:
results = collection.query(
query_texts=["汽车"],
n_results=2
)
print(results)
查询向量存储
- 要查询向量存储,我们有一个由集合提供的query()函数,它允许我们查询向量数据库以获取相关文档。在此函数中,我们提供了两个参数
- query_texts – 对于这个参数,我们提供一个查询列表,以便提取相关文档。
- n_results – 此参数指定数据库应返回多少个顶级结果。在我们的例子中,我们希望我们的集合返回与查询相关的前2个最相关的文档。
- 当我们运行并打印结果时,我们得到以下输出
我们可以看到向量存储返回与id1和id3相关联的两个文档。其中id1是关于汽车的文档,而id3是关于四轮车的文档,它再次与汽车有关。所以当我们给出一个查询时,Chroma DB将查询转换为我们在开始时提供的嵌入模型的向量嵌入。然后,这个向量嵌入在所有可用的文档上执行语义搜索(类似的最近邻搜索)。这里的查询“汽车”与id1和id3文档最相关,因此我们得到了以下查询结果。
当我们尝试构建一个包含多个文档的聊天应用程序时,这非常有帮助。通过向量存储,我们可以通过执行语义搜索并仅将这些文档提供给最终的生成性AI模型,从而获取与提供的查询相关的文档,并生成对应的回答。
更新和删除数据
并不总是一次将所有信息添加到向量存储中。在大多数情况下,我们最初只有有限的数据/文档,我们将其按原样添加到向量存储中。后来,当我们获得更多数据时,有必要更新向量存储中存在的现有数据/向量嵌入。要更新Chroma DB中的数据,我们执行以下操作:
collection.update(
ids=["id2"],
documents=["这是一个包含有关猫的信息的文档"],
metadatas=[{"source": "猫书"}],
)
先前,与id2关联的文档中的信息是关于狗的。现在我们将其更改为关于猫。为了使这些信息在向量存储中得到更新,我们将文档的id、更新后的文档以及文档的更新后的元数据传递给集合的update()函数。这将将id2更新为以前关于狗的文档。
数据库中的查询
results = collection.query(
query_texts=["猫科动物"],
n_results=1
)
print(results)
我们将”猫科动物”作为查询传递给向量存储库。猫属于被称为”猫科动物”的哺乳动物家族。因此,该集合必须将猫文档作为与我们相关的文档返回。在输出中,我们可以看到完全相同的结果。向量存储库能够在查询和文档内容之间执行语义搜索,并且能够返回与提供的查询相符的完美文档。
The Upset Function
有一个与更新函数类似的函数,称为upsert()函数。 update()函数和upsert()函数之间唯一的区别是,如果在update()函数中指定的文档ID不存在,则update()函数会引发错误。但是,在upsert()函数的情况下,如果集合中不存在文档ID,则它将被添加到集合中,类似于add()函数。
有时,为了减少空间或删除不必要/不需要的信息,我们可能希望从向量存储库中删除一些文档。
collection.delete(ids = ['id1'])
results = collection.query(
query_texts=["汽车"],
n_results=2
)
print(results)
The Delete Function
要从集合中删除项目,我们有delete()函数。在上面的代码中,我们正在删除与id1关联的第一个文档,该文档与汽车有关。现在,为了检查,我们使用”汽车”作为查询对集合进行查询,然后查看结果。我们可以看到只有2个文档id2和id3出现,其中id2是关于离汽车最近的四轮车的文档,而id3是关于猫的文档,与汽车最不相关,但是由于我们指定了n_results = 2,我们也获取了id3。如果我们在delete()函数中不指定任何变量,则将删除该集合中的所有项目。
集合函数
我们已经看到如何创建一个新的集合,然后向其中添加文档和嵌入。我们甚至看到了如何从向量存储库中提取与查询相关的信息,即来自存储在其中的文档。Chroma DB的collections对象还与许多其他有用的函数相关联。
让我们看一下Chroma DB提供的其他功能
new_collections = client.create_collection("new_collection")
new_collections.add(
documents=["这是Python文档",
"这是一个Javascript文档",
"这个文档包含了Flask API的速查表"],
metadatas=[{"source": "Python For Everyone"},
{"source": "JS Docs"},
{'source':'Everything Flask'}],
ids=["id1", "id2", "id3"]
)
print(new_collections.count())
print(new_collections.get())
The Count Function
来自collections的count()函数返回集合中的项目数量。在我们的案例中,我们的集合中有3个文档,因此输出将为3。至于get()函数,它将返回我们集合中存在的所有项目以及元数据、ids和embeddings(如果有的话)。在输出中,我们可以看到我们的集合中的所有项目都通过了get()命令。现在让我们来看看如何修改集合名称
collection.modify(name="new_collection_name")
修改函数
使用collections中的modify()函数来更改在创建集合时指定的集合名称。当运行时,将集合名称从开始定义的旧名称更改为在modify()函数中提供的新名称。现在假设我们的Vector Store中有多个集合。如何操作特定的集合,即如何从Vector Store中获取特定的集合以及如何删除特定的集合?让我们来看一下
my_collection = client.get_collection(name="my_information_2")
client.delete_collection(name="my_information_2")
获取集合函数
get_collection()函数将根据提供的name从Vector Store中获取现有的集合。如果提供的集合不存在,则该函数将引发相应的错误。这里get_collection()将尝试获取my_information_2集合并将其赋值给变量my_collection。要删除现有集合,我们有delete_collection()函数,它以集合名称作为参数(在本例中为my_information),然后删除它(如果存在)。
结论
在本指南中,我们看到了如何开始使用Chroma,一个开源的向量数据库。我们最初学习了什么是向量嵌入,为什么它们对生成式AI模型很重要,以及向量存储如何帮助这些生成式大型语言模型。然后我们深入研究了Chroma,看到了如何在Chroma中创建集合。然后我们研究了如何向Chroma添加文档等数据,以及Chroma数据库如何将它们转换为向量嵌入。最后,我们看到了如何从向量存储中的特定集合中检索与给定查询相关的信息。
本指南的一些关键要点包括:
- 向量嵌入是非数值数据(如文本、图像、音频等)的数值表示(数值向量)
- 向量存储是用于以集合形式存储向量嵌入的数据库
- 它们提供了对嵌入数据的高效存储和检索
- Chroma数据库可以作为内存数据库和后端工作
- Chroma数据库具有在退出时存储数据并在启动连接时加载数据到内存的功能,从而保持数据的持久性
- 使用向量存储,从文档中提取信息、生成推荐和构建聊天机器人应用程序将变得更加简单
常见问题
本文中显示的媒体不归Analytics Vidhya所有,仅根据作者的决定使用。