ChatGPT 私房菜:Rust 强力驱动,向量数据库 Qdrant,跨平台部署简单,成 AI 新宠

数据库仍是 AI 时代的基石

前言

上一篇文章,介绍了使用 redis json + redis search,使内容数据库 redis 支持存储和检索非结构化数据——JSON。

小红书上有读者留言,说“费内存”、“pinecone 不香吗”。再看IP所在地,美国的有之,新加坡的有之。我才明白了,pinecone 是很好用,可那是在国外。他们说的没有错。

向量数据库

今天这篇,说的是 qdrant,支持本地部署,Windows、Linux、macOS,跨平台支持。本地能用,服务端自然也可以,数据就可以完全在自己的“视野”范围内了。

1,仓库介绍

qdrant 的仓库地址:

https://github.com/qdrant/qdrant

使用rust开发,运行速度极快,生产环境稳定。特别是性能突出,官网给出了性能对比图:

benchmarks

最上边的那条线,就是 qdrant。低压力测试下,没有曲线数据,好比是举重运动员要的起始重量。别的运动员,100kg开始,5公斤5公斤地加,等加到 130 公斤时,快累趴了。而 qdrant 上来就要的 150 公斤。如此理也。

2,本地部署

根据你本地的操作系统环境,去 release 页面选择对应平台,编译好的二进制文件。

下面以Windows平台下为例。下载文件后,解压,获取到 qdrant.exe 文件。运行命令查看是否正常:

1
qdrant -h

qdrant client

在当前目录下,创建配置文件 config/config.yaml

1
2
3
4
5
6
7
log_level: INFO

service:
  host: 127.0.0.1
  http_port: 6333
  # Uncomment to enable gRPC:
  #grpc_port: 6334

如果是服务环境,提供外部访问,host 参数设置为“0.0.0.0”。如果用到 grpc 功能,把 “grpc_port” 选项注释打开即可。

然后在命令行,直接运行服务:

1
qdrant

运行成功如下图:

qdrant runtime

项目目录如下:

1
2
3
4
5
6
7
├─config
├─snapshots
│  └─tmp
│      └─upload
└─storage
    ├─aliases
    └─collections

snapshots 存储快照;storage 存储数据。

3,测试服务

服务启动后,监听在配置文件中 http_port 指定的端口。默认 6333 端口。

我们使用 Windows 子系统 Ubuntu 22.04 LTS 版本下测试。使用 curl 执行以下操作:

1
2
3
4
5
6
7
8
curl -X PUT 'http://localhost:6333/collections/test_collection' \
    -H 'Content-Type: application/json' \
    --data-raw '{
        "vectors": {
          "size": 4,
          "distance": "Dot"
        }
    }'

调用 restful api 接口,快速创建一个集合 test_collection,并指定向量的配置。请求成功,返回类似以下json:

1
2
3
4
5
{
  "result": true,
  "status": "ok",
  "time": 0.031095451
}

命令行写参数,发起请求,实在不便。qdrant 给各个语言的开发者,都提供了 sdk。

4,python sdk

python sdk 的封装,已发布为类库,提供安装使用。

1
pip install qdrant-client

实现一个最小可用的demo示例。

1
2
from qdrant_client import QdrantClient
client = QdrantClient(":memory:") 

实例化 QdrantClient 客户端时,使用 memory 与sqlite的用法类似,数据存在内存中,代码释放,数据释放。

如果要连接到上一节的 qdrant 服务器,则使用

1
client = QdrantClient(host="localhost", port=6333)

如果不使用本文所要讲的 openai 的embedding功能,qdrant 也提供了 embed 能力,可以在 CPU 或 GPU 上很好运行。使用的是 ONNX 运行时。只用再安装附加的 Python 类库。

1
pip install qdrant-client[fastembed]

这就可以了。提供的API很简洁,与正常的 QdrantClient 无异。

OpenAI

这一章介绍,把 openai 的向量数据,存储到 qdrant。以 Python 代码为例。

1,准备

首先连接到 qdrant 服务端。

1
2
3
4
5
6
import qdrant_client

client = qdrant_client.QdrantClient(
    host="localhost",
    prefer_grpc=True,
)

2,加载数据

使用一份已向量化的数据集,作为测试数据。

1
2
3
4
5
6
import wget

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

# 大约700M大小
wget.download(embeddings_url)

你也可以手动,用下载器下载。zip 文件解压后,放在当前项目 data 目录下,仅包含一个 vector_database_wikipedia_articles_embedded.csv 文件,原始大小 1.7G。

3,数据读取

使用 pandas 读取数据,使用前,保证电脑内存充足。这 1.7G 的文件,是要整个放到内存里,供程序使用的。

1
2
3
4
5
6
7
8
9
import pandas as pd

from ast import literal_eval

article_df = pd.read_csv('../data/vector_database_wikipedia_articles_embedded.csv')

article_df["title_vector"] = article_df.title_vector.apply(literal_eval)
article_df["content_vector"] = article_df.content_vector.apply(literal_eval)
article_df.head()

加载完成,打印前几行: pandas head csv

4,创建集合

在 qdrant 创建新的集合,并声明好集合的初始化参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from qdrant_client.http import models as rest

vector_size = len(article_df["content_vector"][0])

client.recreate_collection(
    collection_name="Articles",
    vectors_config={
        "title": rest.VectorParams(
            distance=rest.Distance.COSINE,
            size=vector_size,
        ),
        "content": rest.VectorParams(
            distance=rest.Distance.COSINE,
            size=vector_size,
        ),
    }
)

集合 Articles 主要的两个字段 title, content,都事前指定数据量大小。然后遍历 pandas 列表,一行一行写写入到 qdrant 内。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
client.upsert(
    collection_name="Articles",
    points=[
        rest.PointStruct(
            id=k,
            vector={
                "title": v["title_vector"],
                "content": v["content_vector"],
            },
            payload=v.to_dict(),
        )
        for k, v in article_df.iterrows()
    ],
)

upsert 方法,是update / insert的缩写。有则更新,无则创建。在非结构化数据库里,特别常用的一个方法。

5,助手函数

写一个助手函数,输入用户的查询语句,使用 openai 的 embedding 方法处理输入文本,并使用 qdrant client 搜索指定的集合。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import openai

def query_qdrant(query, collection_name, vector_name="title", top_k=20):
    embedded_query = openai.Embedding.create(
        input=query,
        model="text-embedding-ada-002",
    )["data"][0]["embedding"]

    query_results = client.search(
        collection_name=collection_name,
        query_vector=(
            vector_name, embedded_query
        ),
        limit=top_k,
    )

    return query_results

6,搜索向量数据

使用上述 qdrant_search 方法测试:

1
2
3
query_results = query_qdrant("modern art in Europe", "Articles")
for i, article in enumerate(query_results):
    print(f"{i + 1}. {article.payload['title']} (Score: {round(article.score, 3)})")

搜索结果类似下方输出:

1
2
3
4
5
1. Museum of Modern Art (Score: 0.875)
2. Western Europe (Score: 0.868)
3. Renaissance art (Score: 0.864)
4. Pop art (Score: 0.86)
......

这样基本就完成了整个流程。

最后

本文详细介绍了 Qdrant 向量数据库,本地部署的详细方法。使用restful api方法,python客户端方式,连接到 qdrant 服务端。

qdrant 自带 embed 方法,可以节约 openai embedding 的费用。使用 ONNX 运行时,可在 CPU 主机上良好运行。

服务器有条件的,可以部署为独立的服务,支持自己的业务数据。

我是@程序员小助手,专注编程知识,圈子动态的IT领域原创作者。