46.5kデモを予約するログイン無料で始める

ピミルバスとは?

May 20, 20231 min read

#はじめに

PymilvusはMilvusおよびZilliz Cloudのために構築されたPython SDKです。全てのSDKで共有されている共通のMilvus protobufを使用するgRPCベースのクライアントです。初心者から上級者まで、Milvusが提供する全ての機能にアクセスでき、最も人気のあるSDKの一つです。

問題点

Milvusのベクトルデータベースに関する重要な考え方は、ユーザーが特定のユースケースに合わせてシステムを微調整できるよう、可能な限り多くのレバーを提供することです。しかし、近似探索はその核心において近似的なものであり、万能なアルゴリズムは存在しない。各アルゴリズムは、計算対スピード対リコール/精度で優れている。さらに、各アルゴリズムは、前のカテゴリーの異なるバランスを達成するように構成することができる。例えば、HNSW vs. IVF-PQ。IVF-PQは計算とスピードに最適化されており、メモリ負荷と検索時間を大幅に削減するが、その代償として想起率は大幅に低下する。HNSWはその逆で、スピードと想起のために計算を犠牲にしている。エキサイティングなのは、両者の長所をコンフィギュレーションによって入れ替えることができることだ。このコンフィギュレーションはすべてインデックス・レベルでの話である。クラスター・レベルでは、コストとスピードのバランスを取る必要があり、これは各ユーザーによって異なります。高可用性を気にせず、レプリケーションを望まないユーザーもいるだろう。一貫性を気にせず、むしろスピードの恩恵を受けて一貫性を保ちたいと考えるユーザーもいるだろう。ストレージコストを削減するためにデータにTTLを付けたいユーザーもいれば、すべての操作をバックアップしたいユーザーもいるだろう。このようなカスタマイズ性は、億単位のデータセットでバランスを取ることが有益な大規模ユーザーにとっては素晴らしいが、小規模ユーザーにとっては、これらのレバーは混乱を招くだけだ。

これらのレバーが問題であることに加え、現時点ではZilliz CloudとMilvusはインデックスと接続パラメーターの関係で互換性がない。

MilvusClientとは

MilvusClientは多くのユーザーにとってAPIを簡素化する試みである。多くのユーザは接続、スキーマ、インデックス作成、ロード、パラメータ等を扱いたくありません。MilvusClientはPymilvus SDKをMilvusでもZillizでも同じシンプルなAPIでラッピングすることで、これらの側面をすべて隠します。現時点では

  • insert_data()
  • upsert_data()
  • search_data()
  • query_data()
  • get_vectors_by_pk()
  • delete_by_pk()
  • add_partition()
  • remove_partition()

これらの関数は、基本的なユーザーが必要とする主要な関数である。現在のところ、操作はPymilvusによって提供されていますが、それらが正しく動作するようにするためには、余計なことがたくさんあります。

insert_data:

MilvusClientを使用すると、コレクションのスキーマを作成する必要はありません。代わりに、挿入されたデータからスキーマが自動生成されます。これには、必要なFieldSchemaを決定し、それをどのように整理するかが含まれます。多くのユーザーは、このスキーマを作成するのが面倒だと感じているようです。動的スキーマがサポートされれば、ユーザーに変更を与えることなく実装を変更することができます。

python def _infer_fields(self, data): """入力データに基づいてすべてのフィールドを推論する。""" # TODO: 3.7用の順序付きdictを仮定する fields = {} # 入力の各データ型を把握する。 for key, value in data.items(): # メタデータの対応するデータ型を推論する dtype = infer_dtype_bydata(value) # データ型に互換性がない if dtype in (DataType.UNKNOWN, DataType.NONE): logger.error( "Failed to parse schema for collection %s, unrecognized dtype for key: %s"、 self.collection_name、 key、 ) raise ValueError(f"{key} のデータ型が認識できません。")

    # フィールド名の下にエントリを作成する
    fields[key] = {} # フィールド名の下にエントリを作成する
    fields[key]["name"] = key
    fields[key]["dtype"] = dtype

    # 特定のデータ型用のkwargsを付ける領域
    if dtype == DataType.VARCHAR:
        fields[key]["max_length"] = 65_535

    return fields

MilvusClientでは、この分野の他のプロジェクトと同じようなデータ形式、つまり辞書のリストにこだわりたかった。このフォーマットは理解しやすく、LlamaIndexやLangChainにあるDocumentsと同等である。しかし、このフォーマットはpymilvusとは互換性がありません。pymilvusのinsertは列データをリストのリストとして受け取るからです。この列データ形式は、スキーマが行った正確な順序にリストを並べることになり、柔軟性がないため、作業が容易ではありません。加えて、不正確なフォーマットのデータに対して表示されるエラーメッセージは、もっと良いものであるべきです。

python
for k in data:
    for key, value in k.items():
        if key in self.fields:
            insert_dict.setdefault(key, []).append(value)

for i in self.tqdm(range(0, len(data), batch_size), disable=not progress_bar):
    # dictをリストのリストに変換して挿入する
    try:
        insert_batch = [
            insert_dict[キー][i : i + batch_size]。
            for key in self.fields
            if key != ignore_pk
                ]

スキーマやJSONなどのために将来やってくるすべての変更によって、単純なインサートのラッパーを持つことで、重い仕事をこなし、ユーザーにシンプルなAPIを残すことができる。

upsert_data:

バージョン2.2では、Milvusにupsertは存在しません。upsertを行うにはdelete -> insertを行う必要があります。多くのユーザがこの機能を求めているため、クライアントに含めることにしました。一旦この機能がpymilvusに追加されれば、ユーザーのコードを変更することなく、簡単に変更できるようになります。

パイソン pks = [x[self.pk_field] for x in data] (データ中のx) self.delete_by_pk(pks, timeout)

ret = self.insert_data( data=data、 timeout=timeout、 batch_size=batch_size、 partition=partition、 progress_bar=progress_bar、 )


## search_data:

searchコマンドの主な修正は、デフォルトの検索パラメータと、出力を辞書のリストに変換することである。

``python
 ret = [].
for hits in res:
    for hits in res: query_result = [].
    for hit in hits:
        ret_dict = {x: hit.entity.get(x) for x in return_fields}.
        query_result.append({"score": hit.score, "data": ret_dict})
    ret.append(query_result)

query_data:

queryコマンドの主な変更点は、出力を辞書のリストに変換したことである。

get_vectors_by_pk:

pymilvusにおけるベクトルの抽出はクエリによって行われます。多くのユーザーが知らないのは、クエリは主キーフィルターに基づいて行われる必要があるということです。これに加えて、もしvarchar主キーを使用する場合、クエリ内の式はエスケープされた引用符を持つ必要があります。

パイソン

Varchar pksは値を二重引用符で囲む必要がある

if self.fields[self.pk_field] == DataType.VARCHAR: ids = ['"' + str(entry) + '"' for entry in pks]. expr = f"""{self.pk_field} in [{','.join(ids)}]"""" else: ids = [str(entry) for entry in pks]. expr = f"{self.pk_field} in [{','.join(ids)}]""


## delete_by_pk:

get_vectors_by_pk に似ている。

## add_partition と delete_partition:

パーティションロジックでは、パーティションの変更にはコレクションのアンロードとロードが必要であることをユーザーは知る必要があります。これは舞台裏で処理されます。

## 結論

全体として、このクライアントの主な目的は、pymilvus側に存在しないか最適化されていない、使いやすい操作を追加することです。pymilvusが改善されるにつれて、これらの操作を舞台裏で最適化し、使いやすいAPIを維持できるようになるだろう。
Updated on Jul 09, 2026