使用 MyScale 作为 OpenAI 嵌入的向量数据库

本笔记本提供了使用 MyScale 作为 OpenAI 嵌入的向量数据库的分步指南。该过程包括:

  1. 利用 OpenAI API 生成的预计算嵌入。
  2. 将这些嵌入存储在 MyScale 的云实例中。
  3. 使用 OpenAI API 将原始文本查询转换为嵌入。
  4. 利用 MyScale 在创建的集合中执行最近邻搜索。

什么是 MyScale

MyScale 是一个构建在 Clickhouse 之上的数据库,它结合了向量搜索和 SQL 分析,提供了高性能、简化的全托管体验。它旨在促进结构化数据和向量数据上的联合查询和分析,并为所有数据处理提供全面的 SQL 支持。

部署选项

  • 通过使用 MyScale Console,在两分钟内部署并在集群上执行向量搜索和 SQL 分析。

先决条件

要遵循本指南,您需要具备以下条件:

  1. 通过遵循 快速入门指南 部署的 MyScale 集群。
  2. clickhouse-connect 库,用于与 MyScale 进行交互。
  3. OpenAI API 密钥,用于查询的向量化。

安装要求

此笔记本需要 openaiclickhouse-connect 以及其他一些依赖项。使用以下命令安装它们:

! pip install openai clickhouse-connect wget pandas

准备您的 OpenAI API 密钥

要使用 OpenAI API,您需要设置一个 API 密钥。如果您还没有,可以从 OpenAI 获取。

import openai

# 从 OpenAI 网站获取 API 密钥
openai.api_key = "OPENAI_API_KEY"

# 检查我们是否已通过身份验证
openai.Engine.list()

连接到 MyScale

请遵循 连接详细信息 部分,从 MyScale 控制台检索集群主机、用户名和密码信息,并使用它来创建到集群的连接,如下所示:

import clickhouse_connect

# 初始化客户端
client = clickhouse_connect.get_client(host='YOUR_CLUSTER_HOST', port=8443, username='YOUR_USERNAME', password='YOUR_CLUSTER_PASSWORD')

加载数据

我们需要加载 OpenAI 提供的预计算的维基百科文章向量嵌入数据集。使用 wget 包下载数据集。

import wget

embeddings_url = "https://cdn.openai.com/API/examples/data/vector_database_wikipedia_articles_embedded.zip"

# 该文件约为 700 MB,因此需要一些时间
wget.download(embeddings_url)

下载完成后,使用 zipfile 包提取文件:

import zipfile

with zipfile.ZipFile("vector_database_wikipedia_articles_embedded.zip", "r") as zip_ref:
    zip_ref.extractall("../data")

现在,我们可以将数据从 vector_database_wikipedia_articles_embedded.csv 加载到 Pandas DataFrame 中:

import pandas as pd

from ast import literal_eval

# 从 csv 读取数据
article_df = pd.read_csv('../data/vector_database_wikipedia_articles_embedded.csv')
article_df = article_df[['id', 'url', 'title', 'text', 'content_vector']]

# 将向量从字符串读回列表
article_df["content_vector"] = article_df.content_vector.apply(literal_eval)
article_df.head()

索引数据

我们将在 MyScale 中创建一个名为 articles 的 SQL 表来存储嵌入数据。该表将包含一个具有余弦距离度量的向量索引,以及对嵌入长度的约束。使用以下代码创建表并将数据插入表中:

# 创建带有向量索引的文章表
embedding_len=len(article_df['content_vector'][0]) # 1536

client.command(f"""
CREATE TABLE IF NOT EXISTS default.articles
(
    id UInt64,
    url String,
    title String,
    text String,
    content_vector Array(Float32),
    CONSTRAINT cons_vector_len CHECK length(content_vector) = {embedding_len},
    VECTOR INDEX article_content_index content_vector TYPE HNSWFLAT('metric_type=Cosine')
)
ENGINE = MergeTree ORDER BY id
""")

# 分批插入数据到表中
from tqdm.auto import tqdm

batch_size = 100
total_records = len(article_df)

# 批量上传数据
data = article_df.to_records(index=False).tolist()
column_names = article_df.columns.tolist() 

for i in tqdm(range(0, total_records, batch_size)):
    i_end = min(i + batch_size, total_records)
    client.insert("default.articles", data[i:i_end], column_names=column_names)

我们需要在继续搜索之前检查向量索引的构建状态,因为它是在后台自动构建的。

# 检查插入数据的数量
print(f"articles count: {client.command('SELECT count(*) FROM default.articles')}")

# 检查向量索引的状态,确保向量索引已准备就绪并处于“Built”状态
get_index_status="SELECT status FROM system.vector_indices WHERE name='article_content_index'"
print(f"index build status: {client.command(get_index_status)}")
articles count: 25000
index build status: Built

搜索数据

一旦在 MyScale 中建立索引,我们就可以执行向量搜索来查找相似内容。首先,我们将使用 OpenAI API 为我们的查询生成嵌入。然后,我们将使用 MyScale 执行向量搜索。

import openai

query = "Famous battles in Scottish history"

# 从用户查询创建嵌入向量
embed = openai.Embedding.create(
    input=query,
    model="text-embedding-3-small",
)["data"][0]["embedding"]

# 查询数据库以查找与给定查询最相似的 K 个内容
top_k = 10
results = client.query(f"""
SELECT id, url, title, distance(content_vector, {embed}) as dist
FROM default.articles
ORDER BY dist
LIMIT {top_k}
""")

# 显示结果
for i, r in enumerate(results.named_results()):
    print(i+1, r['title'])
1 Battle of Bannockburn
2 Wars of Scottish Independence
3 1651
4 First War of Scottish Independence
5 Robert I of Scotland
6 841
7 1716
8 1314
9 1263
10 William Wallace