Spanner でベクトル エンベディングにインデックスを作成してクエリを実行する

このページでは、近似コサイン距離、近似ユークリッド距離、近似ドット積ベクトル関数を使用して、ベクトル インデックスを作成し、ベクトル エンベディングをクエリする方法について説明します。これらの関数を使用すると、Spanner で近似最近傍探索(ANN)を見つけることができます。データセットのサイズが小さい場合、K 最近傍探索(KNN)を使用して正確な k 近傍ベクトルを見つけることができます。ただし、データセットが大きくなると、KNN 検索のレイテンシと費用も増加します。ANN を使用して、レイテンシとコストを大幅に削減して、近似 k 近傍法を検索できます。

近似 k 近傍法

ANN 検索では、k 返されるベクトルは正の上位 k 近傍法ではありません。場合によっては、上位の k 近傍法に含まれないいくつかのベクトルが返されることがあります。これは再現率の損失と呼ばれます。許容できる再現率の損失はユースケースによって異なりますが、ほとんどの場合、データベースのパフォーマンスを向上させるために再現率を少し失うことは許容可能なトレードオフです。

Spanner の近似距離関数の詳細については、以下をご覧ください。

ベクトル インデックス

Spanner は、特別なベクトル インデックスを使用して ANN ベクトル検索を高速化します。このインデックスは、Google Research のスケーラブル最近傍探索(ScaNN)を利用します。これは非常に効率的な最近傍アルゴリズムです。

ベクトル インデックスは、ツリーベースの構造を使用してデータを分割し、検索を高速化します。Spanner には、2 レベルと 3 レベルの両方のツリー構成が用意されています。

  • 2 レベルのツリー構成: リーフノード(num_leaves)には、密接に関連するベクトルのグループと、それに対応するセントロイドが含まれます。ルートレベルは、すべてのリーフノードのセントロイドで構成されます。
  • 3 レベルツリー構成: 概念的には 2 レベル ツリーと似ていますが、追加分岐レイヤ(num_branches)を導入します。追加分岐からリーフノードのセントロイドが分割され、ルートレベル(num_leaves)。

また、特定の距離指標でベクトル インデックスを作成する必要があります。distance_typeCOSINEDOT_PRODUCTEUCLIDEAN のいずれかに設定することで、ユースケースに最適な距離指標を選択できます。

詳細については、VECTOR INDEX ステートメントをご覧ください。

制限事項

Spanner ベクトル インデックスには次の制限があります。

  • ALTER VECTOR INDEX はサポートされていません。

ベクトル インデックスを作成する

再現率とパフォーマンスを高めるためにベクトル インデックスを最適化するには、エンベディングを含むほとんどの行がデータベースに書き込まれた後、ベクトル インデックスを作成することをおすすめします。新しいデータを挿入した後で、ベクトル インデックスの再構築も必要になる場合があります。詳細については、ベクトル インデックスを再構築するをご覧ください。

コサイン距離を使用して、埋め込み列 DocEmbedding がある Documents テーブル上で 2 段階ツリーと 1,000 個のリーフノードを持つベクトル インデックスを作成するには、次のようにします。

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(DocEmbedding),
  OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);

3 レベルのツリーと 1000,000 個のリーフノードを持つベクトル インデックスを作成するには:

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(NullableDocEmbedding)
  WHERE NullableDocEmbedding IS NOT NULL
  OPTIONS (distance_type = 'COSINE', tree_depth = 3, num_branches=1000, num_leaves = 1000000);

埋め込み列が null 値を許容する場合は、WHERE column_name IS NOT NULL 句で宣言する必要があります。

CREATE VECTOR INDEX DocEmbeddingIndex
  ON Documents(NullableDocEmbedding)
  WHERE NullableDocEmbedding IS NOT NULL
  OPTIONS (distance_type = 'COSINE', tree_depth = 2, num_leaves = 1000);

ベクトル エンベディングのクエリ

ベクトル インデックスをクエリするには、次の 3 つの近似距離関数のいずれかを使用します。

  • APPROX_COSINE_DISTANCE
  • APPROX_EUCLIDEAN_DISTANCE
  • APPROX_DOT_PRODUCT

近似距離関数を使用する場合の制限事項は次のとおりです。

  • ベクトル インデックスを使用するには、クエリヒントを指定する必要があります。
  • 距離関数(パラメータやリテラルなど)の引数の 1 つとして定数式を使用する必要があります。
  • 近似距離関数を使用するクエリまたはサブクエリは、特定の形式で指定する必要があります。距離関数は唯一の ORDER BY キーで、制限を指定する必要があります。

制限の詳細なリストについては、近似距離関数のリファレンス ページをご覧ください。

[1.0, 2.0, 3.0] に最も近い 100 個のベクトルを検索するには:

SELECT DocId
FROM Documents@{FORCE_INDEX=DocEmbeddingIndex}
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], DocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

埋め込み列が null 値を許容する場合:

SELECT DocId
FROM Documents@{FORCE_INDEX=DocEmbeddingIndex}
WHERE NullableDocEmbedding IS NOT NULL
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], NullableDocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

ベスト プラクティス

ベクトル インデックスを最適化し、クエリ結果を向上させるには、次のベスト プラクティスに従ってください。

ベクトル検索オプションを調整する

最適なベクトル検索の値は、ユースケース、ベクトル データセット、クエリベクトルによって異なります。特定のワークロードに最適な値を見つけるには、反復調整の実行が必要になる場合があります。

適切な値を選択する際は、次のガイドラインに従ってください。

  • tree_depth(ツリーレベル): インデックス登録されるテーブルの行数が 1, 000 万行未満の場合は、2tree_depth を使用します。それ以外の場合、3tree_depth は最大約 100 億行までのテーブルをサポートします。

  • num_leaves: データセットの行数の平方根を使用します。値が大きいと、ベクトル インデックスのビルド時間が長くなる可能性があります。num_leavestable_row_count/1000 より大きい値に設定しないでください。リーフが小さくなり、パフォーマンスが低下します。

  • num_leaves_to_search: このオプションは、検索されるインデックスのリーフノード数を指定します。num_leaves_to_search を増やすと、再現率が向上しますが、レイテンシとコストも増加します。num_leaves_to_search の値として、CREATE VECTOR INDEX ステートメントで定義されたリーフの総数の 1% となる数値を使用することをおすすめします。フィルタ句を使用している場合は、この値を増やして検索範囲を広げます。

許容範囲の再現率を実現しても、クエリのコストが高すぎて最大 QPS が低下する場合は、次の手順で num_leaves を増やしてみてください。

  1. num_leaves を元の値の倍数 k に設定します(例: 2 * sqrt(table_row_count))。
  2. num_leaves_to_search を元の値の同じ倍数 k に設定します。
  3. num_leaves_to_search の削減を試みて、再現率を維持しながら費用と QPS を改善してみます。

再現率を向上させる

再現率が悪化する可能性は、以下のようにいくつかあります。

  • num_leaves_to_search が小さすぎる: 一部のクエリベクトルでは、最近傍を見つけるのが難しくなる場合があります。そのため、num_leaves_to_search を増やしてより多くのリーフを検索すると、再現率が向上します。最近のクエリは、これらの難しいベクトルが多く含まれるようにシフトしている可能性があります。

  • ベクトル インデックスの再構築が必要: ベクトル インデックスのツリー構造は、作成時にデータセットに合わせて最適化され、その後は静的になります。したがって、最初のベクトル インデックスの作成後に大幅に異なるベクトルが追加されると、ツリー構造が最適ではなく、再現率が低下する可能性があります。

ベクトル インデックスを再構築する

ダウンタイムなしでベクトル インデックスを再構築するには:

  1. 現在のベクトル インデックスと同じ埋め込み列に新しいベクトル インデックスを作成し、必要に応じてパラメータ(OPTIONS など)を更新します。
  2. インデックスの作成が完了したら、新しいインデックスを指すように FORCE_INDEX ヒントを変更して、ベクトル検索クエリを更新します。これにより、クエリで新しいベクトル インデックスが使用されます。(新しいクエリで num_leaves_to_search の再調整が必要になる場合もあります)。
  3. 古いベクトル インデックスを削除します。

次のステップ