构建生产级向量搜索

构建生产级向量搜索' can be condensed to '构建生产级向量搜索'.

向量储存库在机器学习的发展中起着重要作用,它们是数值编码数据的基本存储库。向量是数学实体,用于表示多维空间中的分类数据点。在机器学习的上下文中,向量储存库提供了一种存储、检索、过滤和操作这些向量的方法。

由于向量模型擅长找到数据点之间的相似性,它们对于大型语言模型是不可或缺的,也是许多预测、相似性搜索和上下文检索案例的基础。

通过快速和结构化的数据访问,向量储存库使机器学习模型能够以非凡的效率处理和学习复杂数据,从而促进了不断重塑行业和技术的先进人工智能应用的发展。

Qwak向量存储库

Qwak很高兴宣布我们的托管向量存储库服务的发布。Qwak向量存储库提供了一个可扩展的解决方案,用于转换和摄入向量数据,并提供一个低延迟的查询引擎,可以对元数据和属性进行高级过滤。

Qwak向量存储库与Qwak平台无缝集成,允许您轻松地将模型连接到向量存储库进行训练和预测,并在一个地方管理您的所有机器学习基础设施。

在本教程中,我们将介绍Qwak向量存储库的实现-构建一个将维基百科文章文本转换为嵌入式的Qwak模型,并将该嵌入数据存储在Qwak向量存储库中,同时与文章相关的元数据,最后,查询向量存储库以检索与我们的搜索条件相似的对象。

创建将单词转换为向量的模型

首先,我们需要创建一个模型,将输入文本转换和嵌入,以便可以在Qwak向量存储库中持久化。幸运的是,我们可以在Qwak平台上完成所有这些。

对于这个模型,我们将使用Hugging Face的sentence-transformers/all-MiniLM-L12-v2模型。我们首先从qwak-sdk中导入QwakModel类,并定义我们的基本模型。

我们需要定义两个函数,build()函数和predict()函数。由于模型是预训练的,我们可以简单地通过传递我们的build函数并将self.model设置为SentenceTransformer的实例来完成。

在预测函数中,我们将定义模型如何处理输入数据。

import qwak
from pandas import DataFrame
from qwak.model.base import QwakModel
from qwak.model.schema import ModelSchema, ExplicitFeature
from sentence_transformers import SentenceTransformer

from helpers import get_device


class SentenceEmbeddingsModel(QwakModel):
    def __init__(self):
        self.model_id = "sentence-transformers/all-MiniLM-L12-v2"
        self.model = None
        self.device = None

    def build(self):
        qwak.log_metric({"val_accuracy": 1})

    def schema(self):
        return ModelSchema(
            inputs=[
                ExplicitFeature(name="input", type=str),
            ]
        )

    def initialize_model(self):
        self.device = get_device()
        print(f"Inference using device: {self.device}")
        self.model = SentenceTransformer(
            model_name_or_path=self.model_id,
            device=self.device,
        )

    @qwak.api()
    def predict(self, df):
        text_embeds = self.model.encode(
            df["input"].values.tolist(),
            convert_to_tensor=True,
            normalize_embeddings=True,
            device=self.device,
            batch_size=128,
        ).tolist()

        return DataFrame({"embeddings": text_embeds})

<p我们将输入文本转换为列表,以便可以由SentenceTransformer编码逻辑处理,定义批量大小,并添加一些配置设置。

我们返回一个带有字段output和值为向量列表的DataFrame。

我们需要将其构建和部署为实时端点,这样我们在查询向量存储时就可以调用我们的嵌入函数。

你可以在我们的Qwak示例存储库中找到这个模型的示例。

  1. 克隆本地存储库
  2. 确保你已经安装并配置了Qwak CLI。
  3. 进入sentence_transformers_poetry目录。
  4. 运行make build来启动该模型的训练作业。您可以在Qwak UI的Models -> Builds选项卡中导航并监视构建的进度。
  5. 现在,该模型已经成功训练并存储在Qwak模型存储库中,您可以运行make deploy将此构建版本部署为实时端点。您还可以通过转到Qwak UI的Models -> Deployments选项卡来监视部署步骤。
  6. 当部署完成后,点击平台右上角的Test Model选项卡,Qwak将生成示例推断调用,您可以使用这些调用来调用实时端点并实时测试您的预测!

在我们的文档中阅读更多关于Qwak构建和部署过程的内容。

创建集合

集合是Qwak的组织功能,允许您在向量存储中结构化和管理各种向量分组。集合允许您指定度量配置(余弦、L2),以及向量维数的数量,从而对数据的分组和索引提供细粒度控制。

您可以在UI中创建集合,也可以使用qwak-sdk将集合定义为代码。对于本教程,您可以看到我们创建的示例集合。我们选择余弦度量来进行分组,并选择384个维度用于向量平面。我们还需要选择一个向量化模型。

向量化器是一个已部署的Qwak模型,它接受数据作为输入,并在其predict函数中返回一个向量-就像我们在上一步中创建的模型一样!

在这里,我们选择在Qwak平台上运行的sentence-transformer模型,Qwak向量存储库将在准备插入或搜索向量存储时自动使用该模型的嵌入函数,从而允许我们向Qwak集合API发送自由文本。

‍现在我们已经部署了我们的模型并且我们的集合已经就位,我们可以开始使用我们的向量存储。

将数据插入到向量存储中

我们首先需要准备和格式化我们的数据,以便可以正确地插入到向量存储中。

让我们首先安装项目依赖项。

pip install pandas pyarrow numpy qwak-sdk

我们有一个源parquet文件,其中包含一系列维基百科文章及其内容。我们还有用作向量属性的url、文章标题和文本长度字段。我们首先使用pandas将parquet文件读入Dataframe,并过滤掉不包含文本的文章。

import pandas as pd
import numpy as np
import os

df = pd.read_parquet("short_articles.parquet")
df = df[df["text"].str.len() > 0].sample(frac=0.25)
df = df.reset_index()

接下来,我们需要为我们存储的每个向量选择一个唯一标识符,因此我们将选择文章id字段。我们还将选择要与我们的向量一起包含的属性。幸运的是,这些字段相当简单,所以我们不需要进行太多的转换。

## 收集每篇文章的id
ids = df["article_id"].tolist()
## 收集我们将附加到每个向量的属性
properties = df.apply(
   lambda r:{
       "url": r.url,
       "title": r.title,
       "title_len": r.title_len,
       "text": r.text,
       "text_len": r.text_len}
   , axis=1
).tolist()

我们需要检索在UI中创建的集合。使用Qwak VectorStoreClient,我们可以找到我们在先前步骤中创建的集合,或者可以使用create_collection()方法创建一个新的集合。

from qwak.exceptions import QwakException
from qwak.vector_store import VectorStoreClient

## 创建向量客户端并获取集合
client = VectorStoreClient()

# 检索集合或创建新的集合
collection_name = "wikipedia-vectorizer-demo"
try:
   collection = client.get_collection_by_name(collection_name)
except QwakException:
   collection = client.create_collection(
       name=collection_name,
       description="索引维基百科文章",
       dimension=384,
       metric="cosine",
       vectorizer="sentence_transformer"  # 在Qwak上部署的实时模型的名称
   )
 

通过正确格式化我们的id和元数据,我们将从数据帧中选择文章文本字段作为我们要嵌入到向量存储中的列。我们使用在上一步中获取的集合客户端的upsert()方法。对于我们的文章文本,使用“natural_inputs”参数,upsert命令将调用实时模型,使用predict函数返回我们的文章文本的向量,并将向量持久化到数据库中。

collection.upsert(
   ## 文章id列表
   ids=ids,
   # 自然输入
   natural_inputs=df['text'].tolist(),
   ## 文章属性字典列表
   properties=properties
)

查询向量存储并查看结果

现在我们的向量存储已经被填充,让我们使用它吧!

让我们查询我们的向量存储,看看它是否有与鸭子相关的内容。我们可以使用之前创建的VectorStoreClient。‍

我们将查询“ducks”并将其传递给客户端的search()方法。在后端,向量客户端将调用我们的sentence-transformer模型对查询进行向量化,以便向量存储正确使用。我们可以指定要返回的结果数量和向量属性。如果我们想评估查询性能或向量索引的质量,还可以返回输入与返回结果之间的距离。

from qwak.vector_store import VectorStoreClient

## 使用模型提供的向量搜索向量存储
search_results = collection.search(
   natural_input="鸭子",
   top_results=2,
   output_properties=["title", "title_len", "url"],
   include_distance=True,
   include_vector=False
)

我们指定客户端返回最接近的三篇文章的标题、标题长度和URL,这里是结果!

[print(x.properties, x.distance) for x in search_results]

# 搜索结果对象
{'title': '安纳海姆鸭子', 'title_len': 13.0, 'url': 'https://simple.wikipedia.org/wiki/Anaheim%20Ducks'} 0.38589573
{'title': '鸭子', 'title_len': 4.0, 'url': 'https://simple.wikipedia.org/wiki/Duck'} 0.4547128

我们的查询返回了与安纳海姆鸭子有关的文章,他们是NHL中的一支曲棍球队,以及鸭子发出的“呱呱”声。不是最好的搜索结果,但对于只有几千个文章向量来说,我们接受它!您应该使用集合的度量特性来尝试不同的距离计算方法,并衡量它们对性能的影响。

支持预过滤向量查询

Qwak提供向量预过滤以优化搜索查询,从而实现更快速和更轻量级的查询。

预过滤是一种方法,即在启动向量搜索之前首先识别符合条件的候选项。随后,向量搜索仅考虑“allow”列表上存在的候选项。‍

预过滤查询的两个主要优点包括:

  1. 结果计数的预测:将过滤器应用于已经减少的候选列表,可以简单地估计搜索结果中的元素数量。
  2. 即时匹配检测:如果过滤器非常严格,即仅与数据集大小相比仅匹配少部分数据点,您将立即知道原始向量搜索中是否没有匹配项。
from qwak.vector_store import VectorStoreClient
from qwak.vector_store.filters import Or, GreaterThan, Equal

## 使用模型提供的向量搜索向量存储
search_results = collection.search(
   natural_input="鸭子",
   top_results=2,
   output_properties=["title", "title_len", "url"],
   filter=Or(
    GreaterThan(property="text_len", value=110.0),
    Equal(property="title_len", value=16.0)
  )
)

在我们的文档中了解有关预过滤向量查询的更多信息。

摘要

Qwak向量存储现已推出,您可以立即开始使用,只需转到Qwak UI中的“集合”选项卡并创建一个新的集合。要了解有关所有Qwak向量存储功能的更多信息,您可以访问我们的文档或与Qwak团队的成员联系。

文章最初发布在此处。已获得许可重新发布。