ベクトル インデックスを作成して管理する

このページでは、近似最近傍(ANN)検索とツリーベースの構造を使用して、データに対するベクトル類似性検索を高速化する Spanner ベクトル インデックスを作成して管理する方法について説明します。

Spanner は、専用のベクトル インデックスを使用して近似最近傍(ANN)ベクトル検索を高速化します。このインデックスは、Google Research の高効率な最近傍アルゴリズムである Scalable Nearest Neighbor(ScaNN)を活用しています。

ベクトル インデックスは、ツリーベースの構造を使用してデータをパーティショニングし、高速な検索を可能にします。Spanner では、2 レベルと 3 レベルの両方のツリー構成が可能です。

  • 2 レベルのツリー構成: リーフノード(num_leaves)には、密接に関連するベクトルのグループと、対応するセントロイドが含まれます。ルートレベルは、すべてのリーフノードのセントロイドで構成されます。
  • 3 レベルのツリー構成: 2 レベルのツリーとコンセプトは似ていますが、ブランチレイヤ(num_branches)が追加されています。このレイヤからリーフノード セントロイドがさらにパーティショニングされ、ルートレベル(num_leaves)が形成されます。

Spanner がインデックスを選択します。ただし、特定のインデックスが最適であることをご存じの場合は、FORCE_INDEX ヒントを使用して、ユースケースに最も適したベクトル インデックスを選択できます。

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

制限事項

  • ベクトル インデックスを事前に分割することはできません。詳細については、事前分割の概要をご覧ください。

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

ベクトル インデックスの再現率とパフォーマンスを最適化するには、次のことをおすすめします。

  • エンベディングを含む行のほとんどがデータベースに書き込まれた後に、ベクトル インデックスを作成します。新しいデータを挿入した後で、ベクトル インデックスを定期的に再構築することが必要になる場合があります。詳細については、ベクトル インデックスを再構築するをご覧ください。

  • STORING 句を使用して、ベクトル インデックスに列のコピーを保存します。列値がベクトル インデックスに保存されている場合、Spanner はインデックスのリーフレベルでフィルタしてクエリのパフォーマンスを向上させます。フィルタ条件で使用される列は保存することをおすすめします。インデックスで STORING を使用する方法の詳細については、インデックスのみをスキャンするインデックスを作成するをご覧ください。

テーブルを作成するときに、エンベディング列は FLOAT32(推奨)または FLOAT64 データ型の配列で、ベクトルのディメンションを示す vector_length アノテーションが必要です。最適なベクトル長は、ワークロード、データセットのサイズ、利用可能なコンピューティング リソースによって異なります。さまざまなディメンションを試して、アプリケーションの精度とパフォーマンスを維持できる最小のサイズを見つけます。

次の DDL ステートメントは、ベクトル長のエンベディング列 DocEmbedding を持つ Documents テーブルを作成します。

CREATE TABLE Documents (
  UserId INT64 NOT NULL,
  DocId INT64 NOT NULL,
  Author STRING (1024),
  DocContents Bytes(MAX),
  DocEmbedding ARRAY<FLOAT32>(vector_length=>128) NOT NULL,
  NullableDocEmbedding ARRAY<FLOAT32>(vector_length=>128),
  WordCount INT64,
) PRIMARY KEY (DocId);

Documents テーブルにデータを入力したら、エンベディング列 DocEmbedding を持つ Documents テーブルに、2 レベルのツリーと 1, 000 個のリーフノードを持つベクトル インデックスを、コサイン距離を使用して作成できます。

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

エンベディング列がテーブル定義で NOT NULL としてマークされていない場合は、ベクトル インデックス定義で WHERE COLUMN_NAME IS NOT NULL 句を使用して宣言する必要があります。ここで、COLUMN_NAME はエンベディング列の名前です。コサイン距離を使用して、null 可能なエンベディング列 NullableDocEmbedding に 3 レベルのツリーと 1,000,000 個のリーフノードを含むベクトル インデックスを作成するには:

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

ベクトル インデックスをフィルタする

フィルタされたベクトル インデックスを作成して、データベース内でフィルタ条件に一致する最も類似したアイテムを見つけることができます。フィルタ付きベクトル インデックスは、指定されたフィルタ条件を満たす行のみをインデックスに登録するため、検索パフォーマンスが向上します。

次の例では、テーブル Documents2Category という列があります。ベクトル検索で「Tech」カテゴリにインデックスを付けるため、カテゴリ条件が満たされない場合に NULL と評価される生成列を作成します。

CREATE TABLE Documents2 (
  DocId INT64 NOT NULL,
  Category STRING(MAX),
  NullIfFiltered BOOL AS (IF(Category = 'Tech', TRUE, NULL)) HIDDEN,
  DocEmbedding ARRAY<FLOAT32>(vector_length=>128),
) PRIMARY KEY (DocId);

次に、フィルタを使用してベクトル インデックスを作成します。TechDocEmbeddingIndex ベクトル インデックスには、「Tech」カテゴリのドキュメントのみがインデックスに登録されます。

CREATE VECTOR INDEX TechDocEmbeddingIndex
  ON Documents2(DocEmbedding)
  STORING(NullIfFiltered)
  WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
  OPTIONS (...);

Spanner は、TechDocEmbeddingIndex に一致するフィルタを含む次のクエリを実行すると、TechDocEmbeddingIndex によって自動的に選択され、高速化されます。このクエリは、「Tech」カテゴリ内のドキュメントのみを検索します。{@FORCE_INDEX=TechDocEmbeddingIndex} を使用して、Spanner に TechDocEmbeddingIndex を明示的に使用させることもできます。

SELECT *
FROM Documents2
WHERE DocEmbedding IS NOT NULL AND NullIfFiltered IS NOT NULL
ORDER BY APPROX_(....)
LIMIT 10;

次のステップ