ベクトル クエリのフィルター

ベクトル クエリのベクトル フィルター モードを設定し、フィルター処理をクエリの実行前または実行後どちらで行うのかを指定できます。

フィルターによって、ベクトル クエリのスコープが決まります。 フィルターは、インデックスで filterable という属性が付いている非ベクトルの文字列と数値のフィールドに対して設定されて、それを反復処理しますが、フィルターの目的によって、ベクトル クエリが "何" に対して実行されるかが決まります (検索可能スペース全体、または検索結果の内容)。

この記事では、各フィルター モードについて説明し、それぞれをいつ使用するのかに関するガイダンスを示します。

事前フィルター

事前フィルターでは、クエリ実行前にフィルターが適用され、ベクトル検索アルゴリズムで類似コンテンツを見つける検索対象領域を狭めます。 ベクトル クエリでは、preFilter が既定です。

Diagram of prefilters.

事後フィルター モード

事後フィルターでは、クエリ実行後にフィルターが適用され、検索結果を絞り込みます。

Diagram of post-filters.

ベクトル フィルター モードのベンチマーク テスト

どちらのフィルター モードのパフォーマンスが他方より適切であるかの条件を理解するために、大中小のインデックスに対して一連のテストを実行し、クエリの出力を評価しました。

  • 小 (10 万件のドキュメント、2.5 GB のインデックス、1536 個のディメンション)
  • 中 (100 万件のドキュメント、25 GB のインデックス、1536 個のディメンション)
  • 大 (10 億件のドキュメント、1.9 TB のインデックス、96 個のディメンション)

小と中のワークロードでは、1 個のパーティションと 1 個のレプリカで構成される Standard 2 (S2) のサービスを使用しました。 大のワークロードでは、12 個のパーティションと 1 個のレプリカで構成される Standard 3 (S3) のサービスを使用しました。

インデックスには同一の構成、1 つのキー フィールド、1 つのテキスト フィールド、1 つのフィルター可能な数値フィールドを用意しました。 次のインデックスは、2023-07-01-preview 構文を使って定義されています。

def get_index_schema(self, index_name, dimensions):
    return {
        "name": index_name,
        "fields": [
            {"name": "id", "type": "Edm.String", "key": True, "searchable": True},
            {"name": "content_vector", "type": "Collection(Edm.Single)", "dimensions": dimensions,
              "searchable": True, "retrievable": True, "filterable": False, "facetable": False, "sortable": False,
              "vectorSearchConfiguration": "defaulthnsw"},
            {"name": "text", "type": "Edm.String", "searchable": True, "filterable": False, "retrievable": True,
              "sortable": False, "facetable": False},
            {"name": "score", "type": "Edm.Double", "searchable": False, "filterable": True,
              "retrievable": True, "sortable": True, "facetable": True}
        ],
        "vectorSearch":
        {
            "algorithmConfigurations": [
                {"name": "defaulthnsw", "kind": "hnsw", "hnswParameters": {"metric": "euclidean"}}
            ]
        }
    }

クエリでは、事前フィルターと事後フィルターの両方に同一のフィルターを使用しました。 パフォーマンスの差異がフィルターの複雑さによるものではなく、フィルター モードに起因するように、単純なフィルターを使用しました。

出力は、1 秒あたりのクエリ数 (QPS) で測定しました。

重要なポイント

  • 事前フィルターでは、ほぼ毎回、事後フィルターより時間がかかりましたが、小インデックスでのパフォーマンスではほぼ同等の速さでした。

  • 大きいデータセットでの事前フィルターは、極端に遅れをとっていました。

  • 事前フィルターの方がほぼ毎回速度が遅いのに既定である理由 事前フィルターでは、インデックスに存在する場合、k の結果が返されることが保証されます。この場合、偏りによって速度よりもリコールと精度が優先されます。

  • 事後フィルターは次のお客様に適してします:

    • 選択よりも速度を優先する場合 (事後フィルターでは k より少ない結果が返されます)
    • フィルター処理の対象が過度に選択的でない場合
    • インデックスが事前フィルターでは処理できないようなサイズの場合

詳細

  • 1536 個のディメンションにおいて 10 万個のベクトルで構成されるデータセットの場合:

    • データセットの 30% 以上をフィルター処理する場合、事前フィルターと事後フィルターで違いはありません。
    • データセットの 0.1% 以下をフィルター処理する場合、事前フィルターの方が事後フィルターよりも約 50% 遅くなります。
  • 1536 個のディメンションにおいて 100 万個のベクトルで構成されるデータセットの場合:

    • データセットの 30% 以上をフィルター処理する場合、事前フィルターの方が約 30% 遅くなります。
    • データセットの 2% 以下をフィルター処理する場合、事前フィルターの方が約 7 倍遅くなります。
  • 96 個のディメンションにおいて 10 億個のベクトルで構成されるデータセットの場合:

    • データセットの 5% 以上をフィルター処理する場合、事前フィルターの方が約 50% 遅くなります。
    • データセットの 10% 以下をフィルター処理する場合、事前フィルターの方が約 7 倍遅くなります。

次のグラフでは、事前フィルターの QPS を事後フィルターの QPS で割って計算した事前フィルターの相対 QPS を示しています。

Chart showing QPS performance for small, medium, and large indexes for relative QPS.

縦軸は、事後フィルターの QPS に対する事前フィルターの QPS です。 たとえば、0.0 の値は事前フィルターの方が 100% 遅いことを、縦軸の 0.5 は事前フィルターの方が 50% 遅いことを、1.0 は事前フィルターと事後フィルターが同等であることを意味しています。

横軸は、フィルター処理のレート、またはフィルターを適用した後に候補となるドキュメントの割合を表しています。 たとえば、1.00% は検索コーパスの 1% がフィルター処理の条件で選択されたことを意味しています。