将 Weaviate 与 OpenAI 向量化模块结合用于混合搜索

2023 年 2 月 13 日
在 Github 中打开

本笔记本专为以下场景准备:

  • 您的数据未被向量化
  • 您希望在您的数据上运行混合搜索(了解更多
  • 您希望将 Weaviate 与 OpenAI 模块 (text2vec-openai) 结合使用,为您生成向量嵌入。

本笔记本将引导您完成一个简单的流程,以设置 Weaviate 实例,连接到它(使用 OpenAI API 密钥),配置数据模式,导入数据(这将自动为您的数据生成向量嵌入),并运行混合搜索(向量和 BM25 搜索的混合)。

对于希望在安全环境中存储和搜索我们的嵌入以及他们自己的数据,以支持诸如聊天机器人、主题建模等生产用例的客户来说,这是一个常见的需求。

什么是 Weaviate

Weaviate 是一个开源向量搜索引擎,用于存储数据对象及其向量。这允许将向量搜索与结构化过滤相结合。

Weaviate 使用 KNN 算法来创建向量优化的索引,这使得您的查询能够非常快速地运行。 在此处了解更多信息。

Weaviate 允许您使用您最喜欢的 ML 模型,并无缝扩展到数十亿个数据对象。

部署选项

无论您的场景或生产设置如何,Weaviate 都有适合您的选项。您可以在以下设置中部署 Weaviate:

  • 自托管 – 您可以使用 Docker 在本地或您想要的任何服务器上部署 Weaviate。
  • SaaS – 您可以使用 Weaviate 云服务 (WCS) 来托管您的 Weaviate 实例。
  • 混合 SaaS – 您可以在您自己的私有云服务中部署 Weaviate

编程语言

Weaviate 提供四个客户端库,允许您从您的应用程序进行通信

此外,Weaviate 还有一个 REST 层。基本上,您可以使用任何支持 REST 请求的语言调用 Weaviate。

演示流程

演示流程是:

  • 先决条件设置:创建 Weaviate 实例并安装所需的库
  • 连接:连接到您的 Weaviate 实例
  • 模式配置:配置您的数据模式
    • 注意:在这里我们可以定义要使用的 OpenAI 嵌入模型
    • 注意:在这里我们可以配置要索引的属性
  • 导入数据:加载演示数据集并将其导入 Weaviate
    • 注意:导入过程将自动索引您的数据 - 基于模式中的配置
    • 注意:您不需要显式地向量化您的数据,Weaviate 将与 OpenAI 通信为您完成
  • 运行查询:查询
    • 注意:您不需要显式地向量化您的查询,Weaviate 将与 OpenAI 通信为您完成

一旦您运行完本笔记本,您应该对如何设置和使用向量数据库有一个基本的了解,并且可以继续进行更复杂的使用案例,利用我们的嵌入。

Weaviate 中的 OpenAI 模块

所有 Weaviate 实例都配备了 text2vec-openai 模块。

此模块负责处理导入期间(或任何 CRUD 操作时)以及您运行查询时的向量化。

无需手动向量化数据

这对您来说是个好消息。使用 text2vec-openai,您无需手动向量化您的数据,因为 Weaviate 将在必要时为您调用 OpenAI。

您只需要做的是:

  1. 提供您的 OpenAI API 密钥 – 当您连接到 Weaviate 客户端时
  2. 在您的模式中定义要使用的 OpenAI 向量化器

先决条件

在开始本项目之前,我们需要设置以下内容:

  • 创建一个 Weaviate 实例
  • 安装库
    • weaviate-client
    • datasets
    • apache-beam
  • 获取您的 OpenAI API 密钥

===========================================================

创建 Weaviate 实例

要创建 Weaviate 实例,我们有两种选择:

  1. (推荐路径)Weaviate 云服务 – 在云中托管您的 Weaviate 实例。免费沙箱应该足以满足本 Cookbook 的需求。
  2. 使用 Docker 在本地安装并运行 Weaviate。

选项 1 – WCS 安装步骤

使用 Weaviate 云服务 (WCS) 创建一个免费的 Weaviate 集群。

  1. 创建免费帐户和/或登录 WCS
  2. 使用以下设置创建一个 Weaviate 集群
    • 沙箱:Sandbox Free
    • Weaviate 版本:使用默认(最新)
    • OIDC 身份验证:禁用
  3. 您的实例应在一两分钟内准备就绪
  4. 记下 集群 ID。该链接将带您到集群的完整路径(您稍后需要它来连接到它)。它应该是这样的:https://your-project-name.weaviate.network

选项 2 – 使用 Docker 的本地 Weaviate 实例

使用 Docker 在本地安装并运行 Weaviate。

  1. 下载 ./docker-compose.yml 文件
  2. 然后打开您的终端,导航到您的 docker-compose.yml 文件所在的位置,并使用以下命令启动 Docker:docker-compose up -d
  3. 准备就绪后,您的实例应在 https://127.0.0.1:8080 上可用

注意:要关闭您的 Docker 实例,您可以调用:docker-compose down

了解更多

要了解更多关于将 Weaviate 与 Docker 结合使用的信息,请参阅安装文档

# Install the Weaviate client for Python
!pip install weaviate-client>3.11.0

# Install datasets and apache-beam to load the sample datasets
!pip install datasets apache-beam

===========================================================

准备您的 OpenAI API 密钥

OpenAI API 密钥用于在导入时以及运行查询时向量化您的数据。

如果您没有 OpenAI API 密钥,您可以从 https://beta.openai.com/account/api-keys 获取一个。

获取密钥后,请将其添加到您的环境变量中,命名为 OPENAI_API_KEY

# Export OpenAI API Key
!export OPENAI_API_KEY="your key"
# Test that your OpenAI API key is correctly set as an environment variable
# Note. if you run this notebook locally, you will need to reload your terminal and the notebook for the env variables to be live.
import os

# Note. alternatively you can set a temporary env variable like this:
# os.environ['OPENAI_API_KEY'] = 'your-key-goes-here'

if os.getenv("OPENAI_API_KEY") is not None:
    print ("OPENAI_API_KEY is ready")
else:
    print ("OPENAI_API_KEY environment variable not found")
import weaviate
from datasets import load_dataset
import os

# Connect to your Weaviate instance
client = weaviate.Client(
    url="https://your-wcs-instance-name.weaviate.network/",
#   url="https://127.0.0.1:8080/",
    auth_client_secret=weaviate.auth.AuthApiKey(api_key="<YOUR-WEAVIATE-API-KEY>"), # comment out this line if you are not using authentication for your Weaviate instance (i.e. for locally deployed instances)
    additional_headers={
        "X-OpenAI-Api-Key": os.getenv("OPENAI_API_KEY")
    }
)

# Check if your instance is live and ready
# This should return `True`
client.is_ready()

模式

在本节中,我们将:

  1. 为您的数据配置数据模式
  2. 选择 OpenAI 模块

这是第二个也是最后一个步骤,它需要 OpenAI 特定的配置。完成此步骤后,其余的说明将仅涉及 Weaviate,因为 OpenAI 任务将自动处理。

什么是模式

在 Weaviate 中,您创建模式来捕获您将要搜索的每个实体。

模式是您告诉 Weaviate 的方式:

  • 应该使用什么嵌入模型来向量化数据
  • 您的数据由什么组成(属性名称和类型)
  • 哪些属性应该被向量化和索引

在本 Cookbook 中,我们将使用 文章 的数据集,其中包含:

  • 标题
  • 内容
  • URL

我们想要向量化 titlecontent,但不向量化 url

为了向量化和查询数据,我们将使用 text-embedding-3-small

# Clear up the schema, so that we can recreate it
client.schema.delete_all()
client.schema.get()

# Define the Schema object to use `text-embedding-3-small` on `title` and `content`, but skip it for `url`
article_schema = {
    "class": "Article",
    "description": "A collection of articles",
    "vectorizer": "text2vec-openai",
    "moduleConfig": {
        "text2vec-openai": {
          "model": "ada",
          "modelVersion": "002",
          "type": "text"
        }
    },
    "properties": [{
        "name": "title",
        "description": "Title of the article",
        "dataType": ["string"]
    },
    {
        "name": "content",
        "description": "Contents of the article",
        "dataType": ["text"]
    },
    {
        "name": "url",
        "description": "URL to the article",
        "dataType": ["string"],
        "moduleConfig": { "text2vec-openai": { "skip": True } }
    }]
}

# add the Article schema
client.schema.create_class(article_schema)

# get the schema to make sure it worked
client.schema.get()

导入数据

在本节中,我们将:

  1. 加载 Simple Wikipedia 数据集
  2. 配置 Weaviate 批量导入(以提高导入效率)
  3. 将数据导入 Weaviate

注意
如前所述。我们不需要手动向量化数据。
text2vec-openai 模块将负责处理。

### STEP 1 - load the dataset

from datasets import load_dataset
from typing import List, Iterator

# We'll use the datasets library to pull the Simple Wikipedia dataset for embedding
dataset = list(load_dataset("wikipedia", "20220301.simple")["train"])

# For testing, limited to 2.5k articles for demo purposes
dataset = dataset[:2_500]

# Limited to 25k articles for larger demo purposes
# dataset = dataset[:25_000]

# for free OpenAI acounts, you can use 50 objects
# dataset = dataset[:50]
### Step 2 - configure Weaviate Batch, with
# - starting batch size of 100
# - dynamically increase/decrease based on performance
# - add timeout retries if something goes wrong

client.batch.configure(
    batch_size=10, 
    dynamic=True,
    timeout_retries=3,
#   callback=None,
)
### Step 3 - import data

print("Importing Articles")

counter=0

with client.batch as batch:
    for article in dataset:
        if (counter %10 == 0):
            print(f"Import {counter} / {len(dataset)} ")

        properties = {
            "title": article["title"],
            "content": article["text"],
            "url": article["url"]
        }
        
        batch.add_data_object(properties, "Article")
        counter = counter+1

print("Importing Articles complete")       
# Test that all data has loaded – get object count
result = (
    client.query.aggregate("Article")
    .with_fields("meta { count }")
    .do()
)
print("Object count: ", result["data"]["Aggregate"]["Article"], "\n")
# Test one article has worked by checking one object
test_article = (
    client.query
    .get("Article", ["title", "url", "content"])
    .with_limit(1)
    .do()
)["data"]["Get"]["Article"][0]

print(test_article['title'])
print(test_article['url'])
print(test_article['content'])

搜索数据

与上述类似,我们将向我们的新索引发送一些查询,并根据与我们现有向量的接近程度返回结果

在此处了解关于 alpha 设置的更多信息

def hybrid_query_weaviate(query, collection_name, alpha_val):
    
    nearText = {
        "concepts": [query],
        "distance": 0.7,
    }

    properties = [
        "title", "content", "url",
        "_additional { score }"
    ]

    result = (
        client.query
        .get(collection_name, properties)
        .with_hybrid(nearText, alpha=alpha_val)
        .with_limit(10)
        .do()
    )
    
    # Check for errors
    if ("errors" in result):
        print ("\033[91mYou probably have run out of OpenAI API calls for the current minute – the limit is set at 60 per minute.")
        raise Exception(result["errors"][0]['message'])
    
    return result["data"]["Get"][collection_name]
query_result = hybrid_query_weaviate("modern art in Europe", "Article", 0.5)

for i, article in enumerate(query_result):
    print(f"{i+1}. { article['title']} (Score: {article['_additional']['score']})")
query_result = hybrid_query_weaviate("Famous battles in Scottish history", "Article", 0.5)

for i, article in enumerate(query_result):
    print(f"{i+1}. { article['title']} (Score: {article['_additional']['score']})")

感谢您的关注,您现在已准备好设置自己的向量数据库并使用嵌入来完成各种很酷的事情 - 尽情享受吧!对于更复杂的使用案例,请继续学习本仓库中的其他 Cookbook 示例。