多向量嵌入 (ColBERT, ColPali 等)
在本节中,我们将探讨如何在 Weaviate 中使用多向量嵌入。多向量嵌入(通过 ColBERT、ColPali 或 ColQwen 等模型实现)使用多个向量而不是单个向量来表示每个对象或查询。这种方法能够通过“后期交互”实现更精确的搜索——一种匹配文本各个部分而不是将它们作为整体单元进行比较的技术。
先决条件
在开始本教程之前,请确保您拥有以下内容
- Weaviate 的一个实例(例如在 Weaviate Cloud 上,或本地),版本为
v1.29或更高版本。 - 您首选的 Weaviate 客户端库已安装。
- Jina AI 的 API 密钥
- 可以从 Jina AI 获取免费的“玩具”密钥。
简介
如果您之前使用过向量数据库,您可能熟悉用单个向量表示对象的概念。例如,文本 "一只非常可爱的猫" 可以用一个向量表示,例如
[0.0412, 0.1056, 0.5021, ...]
另一方面,多向量嵌入使用一组嵌套的或二维向量来表示相同的对象。例如,文本 "一只非常可爱的猫" 可以用 ColBERT 嵌入表示为
[
[0.0543, 0.1941, 0.0451, ...],
[0.0123, 0.0567, 0.1234, ...],
...,
[0.4299, 0.0491, 0.9811, ...]
]
这种表示的核心思想是文本不同部分的意思可以通过不同的向量来捕捉。例如,第一个向量可能代表词素 "一",第二个向量可能代表词素 "只",依此类推。

多向量表示允许对对象进行更细致的比较,从而提高相似对象的检索效果。
Weaviate 1.29 引入了对多向量嵌入的支持,允许您使用多向量嵌入存储和搜索对象。
本教程将向您展示如何在 Weaviate 中使用多向量嵌入,使用 ColBERT 模型集成(使用 JinaAI 的模型)或 用户提供的嵌入。
跳转到您感兴趣的部分,或同时跟随这两个部分。
后期交互是一种用于计算文本之间相似度的方法,它通过比较文本的各个部分(如单词或短语)来保留细粒度的含义。ColBERT 等模型使用这种技术来实现比传统单向量方法更精确的文本匹配。
下图显示了后期交互在 ColBERT 模型中的工作方式,与单向量模型进行比较。

了解更多关于后期交互的信息
在单向量方法中,两个嵌入具有相同的维度(例如 768)。因此,它们的相似度是直接计算的,例如通过计算它们的点积或余弦距离。在这种情况下,只有在比较两个向量时才会发生交互。
另一种方法是“早期交互”搜索,如一些“交叉编码器”模型所示。在这种方法中,查询和对象贯穿于嵌入生成和比较过程。虽然这可以带来更准确的结果,但挑战在于嵌入不能在已知查询之前预先计算。因此,这种方法通常用于“重新排序器”模型,其中数据集较小。
后期交互是这两种方法之间的折衷方案,使用多向量嵌入。
每个多向量嵌入由多个向量组成,其中一个向量代表对象的一部分,例如一个词素。例如,一个对象的嵌入可能具有 (30, 64) 的形状,这意味着它有 30 个向量,每个向量有 64 个维度。但另一个对象的嵌入可能具有 (20, 64) 的形状,这意味着它有 20 个向量,每个向量有 64 个维度。
后期交互利用这种结构,通过查找最佳匹配操作(使用 MaxSim 操作)在目标文本的所有词素中找到每个查询词素的最佳匹配项。例如,在搜索“数据科学”时,每个词素级别的向量都与文档的相关部分进行比较,而不是试图一次性匹配整个短语的向量。最终的相似度分数将这些单独的最佳匹配项组合起来。这种词素级别的匹配有助于捕捉细微的关系和词序,使其特别适用于较长的文本。
后期交互搜索
- 将每个查询向量与每个对象向量进行比较
- 将这些词素级别的比较组合起来以生成最终的相似度分数
这种方法通常可以带来更好的搜索结果,因为它能够捕捉对象之间更细微的关系。
何时使用多向量嵌入
多向量嵌入特别适用于搜索任务,其中词序和精确短语匹配很重要。这是因为多向量嵌入保留了词素级别的信息并能够进行后期交互。但是,多向量嵌入通常比单向量嵌入需要更多的资源。
虽然多向量嵌入中的每个向量比单向量嵌入小,但多向量嵌入的总大小通常更大,因为每个嵌入包含许多向量。例如,1536 维的单向量嵌入为 (1536 * 4 字节) = 6 kB,而 64 个 96 维向量的多向量嵌入为 (64 * 96 * 4 字节) = 25 kB - 大于 4 倍。
因此,多向量嵌入需要更多的内存来存储和更多的计算来进行搜索。
嵌入生成的时间和/或成本也可能更高,因为多向量嵌入需要更多的计算来生成。
因此,多向量嵌入最适合于后期交互的好处很重要,并且所需的额外资源是可以接受的任务。
选项 1:ColBERT 模型集成
在本节中,我们将使用 Weaviate 与 JinaAI 的 ColBERT 模型集成来为文本数据生成多向量嵌入。
1.1. 连接到 Weaviate
首先,使用您首选的客户端库连接到您的 Weaviate 实例。在此示例中,我们假设您正在连接到本地 Weaviate 实例。对于其他类型的实例,请根据需要替换连接详细信息 (连接示例)。
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
import os
import weaviate
# Recommended: save sensitive data as environment variables
jinaai_key = os.getenv("JINAAI_APIKEY")
client = weaviate.connect_to_local(
headers={"X-JinaAI-Api-Key": jinaai_key}
)
1.2. 集合配置
在这里,我们定义一个名为 "DemoCollection" 的集合。它具有一个命名向量,配置了 jina-colbert-v2 ColBERT 模型集成。
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
from weaviate.classes.config import Configure, Property, DataType
from weaviate.util import generate_uuid5
collection_name = "DemoCollection"
client.collections.create(
collection_name,
vector_config=[
# ColBERT vectorizer
Configure.MultiVectors.text2vec_jinaai(
name="multi_vector",
source_properties=["text"],
model="jina-colbert-v2"
),
],
properties=[
Property(name="text", data_type=DataType.TEXT),
Property(name="docid", data_type=DataType.TEXT),
],
# Additional parameters not shown
)
1.3. 导入数据
现在,我们可以导入数据。在本例中,我们将导入一些任意文本对象。
回想一下,我们上面配置了模型集成(对于 text2colbert-jinaai)。这使得 Weaviate 能够在需要时获取嵌入。
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
# An example dataset
documents = [
{"id": "doc1", "text": "Weaviate is a vector database that is great for AI app builders."},
{"id": "doc2", "text": "PyTorch is a deep learning framework that is great for AI model builders."},
{"id": "doc3", "text": "For people building AI driven products, Weaviate is a good database for their tech stack."},
]
collection = client.collections.use(collection_name)
with collection.batch.fixed_size(batch_size=10) as batch:
for doc in documents:
# Iterate through the dataset & add to batch
batch.add_object(
properties={"text": doc["text"], "docid": doc["id"]},
uuid=generate_uuid5(doc["id"]),
)
# Check for errors in batch imports
if collection.batch.failed_objects:
print(f"Number of failed imports: {len(collection.batch.failed_objects)}")
print(f"First failed object: {collection.batch.failed_objects[0]}")
print(len(collection)) # This should print `3``
1.3.1. 确认嵌入形状
让我们检索一个对象并检查其嵌入的形状。
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
response = collection.query.fetch_objects(limit=3, include_vector=True)
print(f"Embedding data type: {type(response.objects[0].vector['multi_vector'])}")
print(f"Embedding first element type: {type(response.objects[0].vector['multi_vector'][0])}")
for i in range(3):
# Inspect the shape of the fetched embeddings
print(f"This embedding's shape is ({len(response.objects[i].vector['multi_vector'])}, {len(response.objects[i].vector['multi_vector'][0])})")
print()
检查结果,每个嵌入由一个列表的列表(浮点数)组成。
Embedding data type: <class 'list'>
Embedding first element type: <class 'list'>
This embedding's shape is (22, 128)
This embedding's shape is (25, 128)
This embedding's shape is (22, 128)
请注意,这与单个向量相反,后者将是一个浮点数列表。
1.4. 执行查询
现在我们已经导入了数据,我们可以使用多向量嵌入执行搜索。让我们看看如何执行语义、向量和混合搜索。
1.4.1. 附近文本搜索
使用 ColBERT 嵌入模型集成执行附近文本或语义搜索与使用任何其他嵌入模型集成相同。嵌入维度的差异对用户不可见。
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
response = collection.query.near_text(
query="A good database for AI app builders",
target_vector="multi_vector",
)
for result in response.objects:
print(result.properties)
1.4.2. 混合搜索(简单)
与附近文本搜索类似,使用 ColBERT 嵌入模型集成的混合搜索以与使用其他嵌入模型集成相同的方式执行。
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
response = collection.query.hybrid(
query="A good database for AI app builders",
target_vector="multi_vector",
)
for result in response.objects:
print(result.properties)
1.4.3. 向量搜索
在执行手动向量搜索时,用户必须指定查询嵌入。在本例中,要搜索 multi_vector 索引,查询向量必须是相应的多向量。
由于我们在集成中使用 JinaAI 的 jina-colbert-v2 模型,因此我们通过 JinaAI 的 API 手动获取嵌入,以确保查询嵌入与对象嵌入兼容。
手动获取嵌入
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
def get_colbert_embedding(source_text: str):
# As shown in https://jina.ai/api-dashboard/embedding
# For this example, this only retrieves one embedding at a time
import requests
import json
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {jinaai_key}",
}
data = {
"model": "jina-colbert-v2",
"dimensions": 128,
"input_type": "document",
"embedding_type": "float",
"input": [source_text],
}
response = requests.post(
"https://api.jina.ai/v1/multi-vector", headers=headers, data=json.dumps(data)
)
response_data = json.loads(response.text)
embedding = response_data["data"][0]["embeddings"]
return embedding
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
response = collection.query.near_vector(
near_vector=get_colbert_embedding("A good database for AI app builders"), # Raw ColBERT embedding, in [[e11, e12, e13, ...], [e21, e22, e23, ...], ...] shape
target_vector="multi_vector",
)
for result in response.objects:
print(result.properties)
1.4.4. 混合搜索(手动向量)
在所有需要专门提供向量嵌入的搜索中,必须提供多向量嵌入,就像上面手动向量搜索中展示的那样。
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
response = collection.query.hybrid(
query="A good database for AI app builders",
target_vector="multi_vector",
)
for result in response.objects:
print(result.properties)
选项 2:用户提供的嵌入
在本节中,我们将使用用户提供的嵌入来填充 Weaviate。当您希望使用 Weaviate 集成之外的模型时,这将非常有用。
请注意,如果您正在使用模型集成,您仍然可以提供用户提供的嵌入。如果对象提供了嵌入,它将代替模型集成使用。
这使您能够使用任何预先存在的嵌入,同时受益于模型集成对其他对象带来的便利性。
2.1. 连接到 Weaviate
首先,使用您首选的客户端库连接到您的 Weaviate 实例。在此示例中,我们假设您正在连接到本地 Weaviate 实例。对于其他类型的实例,请根据需要替换连接详细信息 (连接示例)。
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
import os
import weaviate
# Recommended: save sensitive data as environment variables
jinaai_key = os.getenv("JINAAI_APIKEY")
client = weaviate.connect_to_local(
headers={"X-JinaAI-Api-Key": jinaai_key}
)
2.2. 集合配置
在这里,我们定义一个名为 "DemoCollection" 的集合。请注意,我们不使用模型集成,因为我们将手动提供嵌入。
集合配置显式启用了 multi-vector 索引选项。这是处理多向量嵌入所必需的。
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
from weaviate.classes.config import Configure, Property, DataType
from weaviate.util import generate_uuid5
collection_name = "DemoCollection"
client.collections.create(
collection_name,
vector_config=[
# User-provided embeddings
Configure.MultiVectors.self_provided(
name="multi_vector",
),
],
properties=[
Property(name="text", data_type=DataType.TEXT),
Property(name="docid", data_type=DataType.TEXT),
],
# Additional parameters not shown
)
2.3. 导入数据
现在,我们可以导入数据。在本例中,我们将导入一些任意文本对象。
请注意,在此示例中,每个对象都连同相应的多向量嵌入一起发送到 Weaviate。在本例中,我们获得了 Jina AI 的 ColBERT 嵌入,但它可以是任何多向量嵌入。
手动获取嵌入
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
def get_colbert_embedding(source_text: str):
# As shown in https://jina.ai/api-dashboard/embedding
# For this example, this only retrieves one embedding at a time
import requests
import json
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {jinaai_key}",
}
data = {
"model": "jina-colbert-v2",
"dimensions": 128,
"input_type": "document",
"embedding_type": "float",
"input": [source_text],
}
response = requests.post(
"https://api.jina.ai/v1/multi-vector", headers=headers, data=json.dumps(data)
)
response_data = json.loads(response.text)
embedding = response_data["data"][0]["embeddings"]
return embedding
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
# An example dataset
documents = [
{"id": "doc1", "text": "Weaviate is a vector database that is great for AI app builders."},
{"id": "doc2", "text": "PyTorch is a deep learning framework that is great for AI model builders."},
{"id": "doc3", "text": "For people building AI driven products, Weaviate is a good database for their tech stack."},
]
collection = client.collections.use(collection_name)
with collection.batch.fixed_size(batch_size=10) as batch:
for doc in documents:
# Iterate through the dataset & add to batch
batch.add_object(
properties={"text": doc["text"], "docid": doc["id"]},
uuid=generate_uuid5(doc["id"]),
vector={"multi_vector": get_colbert_embedding(doc["text"])}, # Provide the embedding manually
)
# Check for errors in batch imports
if collection.batch.failed_objects:
print(f"Number of failed imports: {len(collection.batch.failed_objects)}")
print(f"First failed object: {collection.batch.failed_objects[0]}")
print(len(collection)) # This should print `3``
2.4. 执行查询
现在我们已经导入了数据,我们可以使用多向量嵌入执行搜索。让我们看看如何执行向量和混合搜索。
请注意,使用用户提供的嵌入时,无法进行 near text 搜索。在这种配置下,Weaviate 无法在不知道用于生成嵌入的模型的情况下,将文本查询转换为兼容的嵌入。
2.4.1. 向量搜索
您可以通过指定查询嵌入来执行手动向量搜索。在本例中,我们使用与用于生成对象嵌入的相同模型(JinaAI 的 jina-colbert-v2)将查询转换为向量。
这确保了查询嵌入与对象嵌入兼容。
手动获取嵌入
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
def get_colbert_embedding(source_text: str):
# As shown in https://jina.ai/api-dashboard/embedding
# For this example, this only retrieves one embedding at a time
import requests
import json
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {jinaai_key}",
}
data = {
"model": "jina-colbert-v2",
"dimensions": 128,
"input_type": "document",
"embedding_type": "float",
"input": [source_text],
}
response = requests.post(
"https://api.jina.ai/v1/multi-vector", headers=headers, data=json.dumps(data)
)
response_data = json.loads(response.text)
embedding = response_data["data"][0]["embeddings"]
return embedding
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
response = collection.query.near_vector(
near_vector=get_colbert_embedding("A good database for AI app builders"), # Raw ColBERT embedding, in [[e11, e12, e13, ...], [e21, e22, e23, ...], ...] shape
target_vector="multi_vector",
)
for result in response.objects:
print(result.properties)
2.4.2. 混合搜索(手动向量)
要使用用户提供的嵌入执行混合搜索,请提供查询向量以及混合查询。
如果某个片段无法工作或您有任何反馈,请打开一个 GitHub issue。
response = collection.query.hybrid(
query="A good database for AI app builders",
target_vector="multi_vector",
)
for result in response.objects:
print(result.properties)
总结
本教程展示了如何在 Weaviate 中使用多向量嵌入。
Weaviate 允许您从 v1.29 版本开始使用多向量嵌入,可以使用 ColBERT 模型集成,也可以通过提供您自己的嵌入来实现。
请注意,在使用多向量嵌入时,向量索引可能需要手动配置才能处理多向量嵌入。这是因为嵌入的形状与单向量嵌入不同。在使用 ColBERT 等模型集成时,此操作会自动完成,但在手动提供嵌入时必须手动完成。
数据导入完成后,您可以像往常一样执行语义、混合和向量搜索。主要区别在于嵌入的形状,在手动提供嵌入时必须考虑到这一点。
更多资源
问题和反馈
如果您有任何问题或反馈,请在 用户论坛 中告诉我们。
