Rust製のベクトルデータベースQdrantを試してみる

2023.06.09

どうも!オペレーション部の西村祐二です。

ChatGPT関連の専用アプリケーションを作成しようとすると、「ベクトルデータベース」という用語が出てきます。これは私にとって、これまでまったく経験したことのない分野で理解できていない状態でした。

このままでは行き詰まってしまうという思いと、この分野に関してある程度知識を身につけておくと、今後応用がききそうだなと考えました。

そこで今回、他のベクトルデータベースと比べて機能がシンプルそうで、Rustで作られているという特徴から、Qdrantを試してみました。

Qdrantとは

公式ドキュメントから引用し翻訳したもの

Qdrantは「ベクトルの類似性検索エンジンであり、追加のペイロード(つまりベクトル)を格納、検索、管理するための便利なAPIを備えた本番環境で使用できるサービスを提供します。」ペイロードとは、検索を絞り込むのに役立つ追加の情報や、ユーザーに提供できる有用な情報と考えることができます。

Qdrantのアーキテクチャの概要

Qdrant’s Architecture

ドキュメントより引用

用語

Qdrant内で利用される用語ではじめに知っておくと良い最低限の用語をまとめておきます。

なるべく簡潔に記載しているので、正確ではないかもしれません。正確な情報を知りたい場合はドキュメントを確認ください。

Collections

Pointの集合。テーブルのようなもの。

ドキュメント

Points

id、ベクトルデータ、Payloadで構成されるデータの塊。レコードのようなもの。

ドキュメント

Payload

ベクトルデータに追加できる付加情報。JSONで管理される。

ドキュメント

試してみる

Qdrantを起動

QdrantはOSSでDockerを使ってローカルで試すことができます。

$ docker pull qdrant/qdrant

Docker 内でサービスを実行します

docker run -p 6333:6333 \
    -v $(pwd)/qdrant_storage:/qdrant/storage \
    qdrant/qdrant

起動したらlocalhost:6333にアクセスすると下記のような表示になるかと思います。

{"title":"qdrant - vector search engine","version":"1.1.3"}

Pythonのクライアントを使って操作する

Python以外にもRust、Go、Javascriptなどのクライアントライブラリがあります。

$ pip install qdrant-client

Collectionを作成

はじめにCollectionを作成します。名前は「test_collection」としておきます。

ベクトル間の類似性を測定するために利用されるメトリクスとして、現在3つサポートされています。今回は「Dot product」を設定しています。

  • Dot product
  • Cosine similarity
  • Euclidean distance
from qdrant_client import QdrantClient
from qdrant_client.http.models import Distance, VectorParams

client = QdrantClient("localhost", port=6333)
client.recreate_collection(
    collection_name="test_collection",
    vectors_config=VectorParams(size=4, distance=Distance.DOT),
)

作成したCollectionの状態を下記で取得することができます。

collection_info = client.get_collection(collection_name="test_collection")
print(collection_info)

Pointを追加

都市の情報やcountの情報をPayloadとしてもつベクトルデータを登録します。

from qdrant_client import QdrantClient
from qdrant_client.http.models import PointStruct

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

operation_info = client.upsert(
    collection_name="test_collection",
    points=[
        PointStruct(id=1, vector=[0.05, 0.61, 0.76, 0.74], payload={"city": "Berlin"}),
        PointStruct(id=2, vector=[0.19, 0.81, 0.75, 0.11], payload={"city": ["Berlin", "London"]}),
        PointStruct(id=3, vector=[0.36, 0.55, 0.47, 0.94], payload={"city": ["Berlin", "Moscow"]}),
        PointStruct(id=4, vector=[0.18, 0.01, 0.85, 0.80], payload={"city": ["London", "Moscow"]}),
        PointStruct(id=5, vector=[0.24, 0.18, 0.22, 0.44], payload={"count": [0]}),
        PointStruct(id=6, vector=[0.35, 0.08, 0.11, 0.44]),
    ]
)
print(operation_info)

実行結果

operation_id=1 status=<UpdateStatus.COMPLETED: 'completed'>

Pointを取得

登録したPointをidをつかって取得することができます。

points = client.retrieve(
    collection_name="test_collection",
    ids=[1,2,3],
    )
print(points)

実行結果

[Record(id=3, payload={'city': ['Berlin', 'Moscow']}, vector=None), Record(id=1, payload={'city': 'Berlin'}, vector=None), Record(id=2, payload={'city': ['Berlin', 'London']}, vector=None)]

Pointを検索

  • ベクトルの情報を使って検索することができます。
from qdrant_client import QdrantClient

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

search_result = client.search(
    collection_name="test_collection",
    query_vector=[0.2, 0.1, 0.9, 0.7], 
    limit=3
)
print(search_result)

実行結果

スコアの情報が含まれており、検索に利用したベクトルデータと近いPointが上位3つ選択されたことがわかります。

[ScoredPoint(id=4, version=1, score=1.362, payload={'city': ['London', 'Moscow']}, vector=None), ScoredPoint(id=1, version=1, score=1.273, payload={'city': 'Berlin'}, vector=None), ScoredPoint(id=3, version=1, score=1.208, payload={'city': ['Berlin', 'Moscow']}, vector=None)]
  • Filterを使って検索することができます。

ベクトルだけではなく、Payloadに対してフィルタリングするこができます。複雑なフィルタリングがサポートされてます。

このFilter機能がおそらくQdrantの強みになってくるのかなと思います。

下記では、payloadに「city」があり、値が「London」のものだけにフィルターしています。

from qdrant_client.http.models import Filter, FieldCondition, MatchValue
from qdrant_client import QdrantClient

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

search_result = client.search(
    collection_name="test_collection",
    query_vector=[0.2, 0.1, 0.9, 0.7], 
    query_filter=Filter(
        must=[
            FieldCondition(
                key="city",
                match=MatchValue(value="London")
            )
        ]
    ),
    limit=3
)

print(search_result)

実行結果

[ScoredPoint(id=4, version=1, score=1.362, payload={'city': ['London', 'Moscow']}, vector=None), ScoredPoint(id=2, version=1, score=0.871, payload={'city': ['Berlin', 'London']}, vector=None)]

さいごに

今回、Rust製のベクトルデータベースQdrantを試してみました。

Dockerを使ってローカルからサクッと試せてかなりお手軽でした。

操作もシンプルで理解しやすいので、ベクトルデータベースが気になってる人のはじめの一歩として良いのではないかなと思いました。

今後は他の関連ツールと組み合わせて試していきたいと思います。

誰かの参考になれば幸いです。