使用 Weaviate 和 OpenAI vectorize 模块进行向量嵌入搜索

2023 年 2 月 13 日
在 Github 中打开

本笔记本是为以下场景准备的:

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

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

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

什么是 Weaviate

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

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

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

部署选项

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

  • 自托管 – 您可以使用 docker 在本地或任何您想要的服务器上部署 Weaviate。
  • SaaS – 您可以使用 Weaviate Cloud Service (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 实例,我们有 2 个选项:

  1. (推荐路径)Weaviate Cloud Service – 在云中托管您的 Weaviate 实例。免费沙箱对于本 cookbook 来说应该足够了。
  2. 使用 Docker 在本地安装并运行 Weaviate。

选项 1 – WCS 安装步骤

使用 Weaviate Cloud Service (WCS) 创建一个免费的 Weaviate 集群。

  1. 创建一个免费帐户和/或登录到 WCS
  2. 使用以下设置创建一个 Weaviate 集群
    • 沙箱:Sandbox Free
    • Weaviate 版本:使用默认(最新)
    • OIDC 身份验证:Disabled
  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 中,我们将使用 Articles 的数据集,其中包含:

  • 标题
  • 内容
  • 网址

我们想要向量化 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'])

搜索数据

与上面一样,我们将对新的索引发起一些查询,并根据与我们现有向量的接近程度返回结果

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

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

    result = (
        client.query
        .get(collection_name, properties)
        .with_near_text(nearText)
        .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 = query_weaviate("modern art in Europe", "Article")

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

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

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