一架梯子,一头程序猿,仰望星空!
Chroma向量数据库教程 > 内容正文

Python Chromadb 向量数据库开发指南


Python Chromadb详细开发指南,快速入门请参考前一个章节。

安装

pip install chromadb

持久化Chromadb数据

import chromadb

你可以指定 Chroma 的数据库文件存储路径,如果数据存在,程序启动的时候会自动加载数据库文件。

client = chromadb.PersistentClient(path="/data/tizi365.db")

path参数是Chroma数据库文件路径。

提示:一个chroma数据库,创建一个client客户端对象即可,在同一路径上加载和保存多个客户端可能会导致奇怪的行为,包括数据删除。一般来说,在应用程序中只创建一次 Chroma 客户端。

客户端对象一些常用的函数。

client.reset() # 清空并完全重置数据库

集合操作

Chromadb使用collection原语来管理向量数据的集合,可以简单的把集合类比成MYSQL的表。

创建、查看和删除Collections

Chroma在URL中使用集合名称,因此对其进行了一些命名限制:

  • 名称长度必须介于3到63个字符之间。
  • 名称必须以小写字母或数字开头和结尾,中间可以包含点、破折号和下划线。
  • 名称不能包含两个连续的点。
  • 名称不能是有效的IP地址。

创建集合,需要指定集合名称和一个可选的向量计算函数(也叫embedding嵌入函数)。如果提供了嵌入函数,则必须在每次获取集合时提供它。

说明:向量计算函数(embedding嵌入函数)的作用是用于计算文本向量。

# 通过embedding_function参数指定向量计算函数,不指定则使用内置的函数
collection = client.create_collection(name="my_collection", embedding_function=emb_fn)
# 如果创建集合的使用指定了向量计算函数,引用集合的时候也要指定向量计算函数
collection = client.get_collection(name="my_collection", embedding_function=emb_fn)

embedding函数以文本作为输入然后返回一个计算的向量数据。

说明:新手可以了解文本嵌入模型教程

可以通过.get_collection函数引用现有集合,并使用.delete_collection删除集合。你也可以使用.get_or_create_collection引用一个集合(如果存在),或者如果不存在则创建它。

# 按名称从现有集合中获取集合对象。如果找不到,则抛出异常。
collection = client.get_collection(name="tizi365") 
# 引用指定集合,如果集合不存在则自动创建一个新的
collection = client.get_or_create_collection(name="tizi365") 
# 删除指定集合
client.delete_collection(name="tizi365")

其他集合常用操作

collection.peek() # 返回集合中前 10 个数据的列表
collection.count() # 返回集合中的数据总数
collection.modify(name="new_name") # 重命名集合

指定向量距离计算方法

create_collection 函数还包含一个可选的元数据参数,通过设置 hnsw:space 的值,可以自定义向量空间距离计算方法。

说明:向量数据是通过计算向量之间的空间距离表示向量之间的相似度,距离越近相似度越高,反而相似度越低。

 collection = client.create_collection(
        name="collection_name",
        metadata={"hnsw:space": "cosine"} # l2 是默认的计算方法
    )

hnsw:space 的有效选项为 “l2”、”ip “或 “cosine”。默认为 “l2”。

向集合添加数据

使用 .add 方法将数据添加到 Chroma。

不指定文档向量,直接添加数据:

collection.add(
    documents=["lorem ipsum...", "doc2", "doc3", ...],
    metadatas=[{"chapter": "3", "verse": "16"}, {"chapter": "3", "verse": "5"}, {"chapter": "29", "verse": "11"}, ...],
    ids=["id1", "id2", "id3", ...]
)

如果 Chroma 收到一个文档列表(documents),它会自动使用集合的嵌入函数对文档进行向量计算(如果创建集合时未提供嵌入函数,则使用默认值)。Chroma 还会存储文档本身。如果文档太大,无法使用所选的嵌入函数计算,则会出现异常。

每个文件都必须有一个唯一的ID(ids)。两次添加相同的 ID 会导致只存储初始值。可以为每个文档提供一个可选的元数据字典(metadatas)列表,以存储更多信息,用于支持查询的时候筛选数据。

或者,你也可以直接提供文档相关向量数据的列表,Chroma直接使用你填写的向量数据,而不会自动计算向量。

collection.add(
    documents=["doc1", "doc2", "doc3", ...],
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"chapter": "3", "verse": "16"}, {"chapter": "3", "verse": "5"}, {"chapter": "29", "verse": "11"}, ...],
    ids=["id1", "id2", "id3", ...]
)

如果提供的向量数据维度(长度)与集合的维度不一致,则会出现异常。

你也可以将文档存储在其他地方,只需向 Chroma 提供向量数据和元数据列表即可。你可以使用 ids 将向量与存储在其他地方的文档关联起来。

collection.add(
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"chapter": "3", "verse": "16"}, {"chapter": "3", "verse": "5"}, {"chapter": "29", "verse": "11"}, ...],
    ids=["id1", "id2", "id3", ...]
)

说明:向量数据库的核心功能是基于向量数据的语义相似搜索,为减小向量数据库的大小,提高效率,我们可以选择在向量数据库存储向量数据和一些需要筛选的属性条件就行,其他数据,例如文章内容等数据,存储到MYSQL之类的数据库,只要通过id关联起来就行。

查询集合数据

使用 .query 方法可以以多种方式查询 Chroma 数据集。

你可以通过一组 query_embeddings(向量数据) 进行查询。

提示:query_embeddings向量数据怎么来,实际开发场景,通常是先把用户的查询问题,通过文本嵌入模型计算出问题向量,然后拿着这个向量去查询相似的内容。

collection.query(
    query_embeddings=[[11.1, 12.1, 13.1],[1.1, 2.3, 3.2], ...],
    n_results=10,
    where={"metadata_field": "is_equal_to_this"},
    where_document={"$contains":"search_string"}
)

查询将按顺序返回与每个查询向量(query_embedding)最匹配的 n_results 个结果。可以提供一个可选的 where 过滤字典,根据与每个文档相关的元数据过滤结果。此外,还可提供一个可选的 where_document 过滤字典,用于根据文档内容过滤结果。

如果提供的 query_embeddings 与集合的维度不一致,则会出现异常,为了确保向量维度一致,统一使用同一个文本嵌入模型计算向量就行。

你也可以通过一组查询文本进行查询。Chroma 会首先使用集合的嵌入函数计算每一个查询文本的向量,然后使用生成的文本向量执行查询。

# 直接通过文本查询,相似的内容,这里chroma会使用默认嵌入模型计算向量
collection.query(
    query_texts=["doc10", "thus spake zarathustra", ...],
    n_results=10,
    where={"metadata_field": "is_equal_to_this"},
    where_document={"$contains":"search_string"}
)

您还可以使用 .get 从集合中按 id 查询数据。

collection.get(
    ids=["id1", "id2", "id3", ...],
    where={"style": "style1"}
)

.get 还支持 where 和 where_document 筛选器。如果没有提供 id,它将返回集合中符合 where 和 where_document 筛选器的所有项目。

指定返回字段

使用 get 或 query 时,可以使用 include 参数指定需要返回的数据—embeddings, documents, metadatas中的任意数据,而对于查询,则需要返回距离数据。默认情况下,Chroma 会返回文档、元数据,查询时则返回结果的距离,而“ids”始终会返回。您可以通过向查询或获取方法的 includes 参数传递一个包含字段名称的数组来指定要返回的字段。

# 仅返回 documents 和 ids
collection.get(
    include=["documents"]
)

collection.query(
    query_embeddings=[[11.1, 12.1, 13.1],[1.1, 2.3, 3.2], ...],
    include=["documents"]
)

使用 Where 过滤条件

Chroma 支持通过元数据和文档内容过滤查询。where 过滤器用于过滤元数据,where_document 过滤器用于过滤文档内容,下面讲解过滤条件表达式怎么写。

通过元数据过滤

要对元数据进行过滤,必须为查询提供一个 where 过滤器字典。字典必须具有以下结构:

{
    "metadata_field": {
        <Operator>: <Value>
    }
}

过滤元数据支持以下操作符:

  • $eq - 等于(字符串、整数、浮点数)
  • $ne - 不等于(字符串、整数、浮点数)
  • $gt - 大于(int、浮点数)
  • $gte - 大于或等于(int、浮点数)
  • $lt - 小于(整数、浮点数)
  • $lte - 小于或等于(int、浮点数)

使用 $eq 操作符等同于使用 where 过滤器。

{
    "metadata_field": "search_string"
}

# 等价表达式

{
    "metadata_field": {
        "$eq": "search_string"
    }
}

过滤文档内容

要对文档内容进行过滤,必须为查询提供一个 where_document 过滤字典。字典必须具有以下结构:

# 查询包含search_string字符串的内容
{
    "$contains": "search_string"
}

使用逻辑操作符

您还可以使用逻辑运算符 $and$or 来组合多个筛选器。

$and 运算符将返回与列表中所有筛选器匹配的结果。

{
    "$and": [
        {
            "metadata_field": {
                <Operator>: <Value>
            }
        },
        {
            "metadata_field": {
                <Operator>: <Value>
            }
        }
    ]
}

$or 运算符将返回与列表中任意筛选条件相匹配的结果。

{
    "$or": [
        {
            "metadata_field": {
                <Operator>: <Value>
            }
        },
        {
            "metadata_field": {
                <Operator>: <Value>
            }
        }
    ]
}

更新集合数据

使用 .update 可以更新集合中数据的任何属性。

# 根据id批量更新数据
collection.update(
    ids=["id1", "id2", "id3", ...],
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"chapter": "3", "verse": "16"}, {"chapter": "3", "verse": "5"}, {"chapter": "29", "verse": "11"}, ...],
    documents=["doc1", "doc2", "doc3", ...],
)

如果在集合中找不到 id,将记录错误并忽略更新。如果提供的文档没有相应的向量,将使用集合的嵌入函数计算向量。

如果提供的向量数据与集合的维度不同,则会出现异常。

Chroma 还支持向上插入(upsert)操作,它可以更新现有数据,如果数据丕存在则插入新数据。

# 数据不存在则插入,存在则更新
collection.upsert(
    ids=["id1", "id2", "id3", ...],
    embeddings=[[1.1, 2.3, 3.2], [4.5, 6.9, 4.4], [1.1, 2.3, 3.2], ...],
    metadatas=[{"chapter": "3", "verse": "16"}, {"chapter": "3", "verse": "5"}, {"chapter": "29", "verse": "11"}, ...],
    documents=["doc1", "doc2", "doc3", ...],
)

删除集合数据

Chroma 支持使用 .deleteid 从集合中删除数据。与每个数据相关的向量、文档和元数据都将被删除。

collection.delete(
    ids=["id1", "id2", "id3",...],
    where={"chapter": "20"}
)

.delete 还支持 where 过滤器。如果没有提供 id,它将删除集合中与 where 过滤器匹配的所有项目。


关联主题