# ベクトル検索

ベクトル検索は、データをベクトルとして表現する検索方法です。画像検索、ビデオ検索、テキスト検索などのアプリケーションや、画像分類やクラスタリングなどの機械学習アプリケーションでよく使用されます。

このセクションでは、ベクトル検索を高速化するためのベクトルインデックスの作成と操作方法、さらに異なるタイプのベクトルインデックスの設定方法について紹介します。

# コマンドリファレンス

# ベクトルを含むテーブルの作成

現在、私たちは2種類のベクトルをサポートしています:float32の配列とバイナリ文字列です。それに応じて、埋め込みベクトルはMyScale内でArray(Float32)またはFixedStringとして表されます。

WARNING

ベクトルのバイナリ文字列タイプは、DBバージョン1.3.1以降でのみ適用可能です。

TIP

埋め込みベクトル列のすべてのベクトルは、必ず同じ次元を持たなければなりません。

  • float32の配列のベクトルの場合、エラーを避けるためにCONSTRAINTを使用します。例えば、CONSTRAINT constraint_name_1 CHECK length(data) = 128
  • バイナリ文字列のベクトルの場合、固定長FixedString(N)タイプとして表します。その結果、これらのベクトルの次元制約を指定する必要はありません。なぜなら、各ベクトルの次元は一貫しており、8 * Nと等しくなければならないからです。

以下は、異なるタイプのベクトルデータのためのテーブルを作成する例です。

-- 128次元のfloat32ベクトルの配列と長さ制約のあるベクトル列を持つテーブルを作成する
CREATE TABLE test_vector
(
    id    UInt32,
    data  Array(Float32),
    CONSTRAINT check_length CHECK length(data) = 128,
    date  Date,
    label Enum8('person' = 1, 'building' = 2, 'animal' = 3)
) ENGINE = MergeTree ORDER BY id
-- 128次元(16バイト)のバイナリ文字列ベクトルを持つテーブルを作成する
CREATE TABLE test_binary_vector
(
    id    UInt32,
    binary_data  FixedString(16),
    date  Date,
    label Enum8('person' = 1, 'building' = 2, 'animal' = 3)
) ENGINE = MergeTree ORDER BY id

注意

データ型の名前は大文字と小文字が区別され、大文字と小文字の区別が正しくない場合はエラーが返されます。

# ベクトルインデックスの作成

ベクトル検索を実行する前に、ベクトルインデックスを作成する必要があります。ベクトルインデックスを作成するための構文は以下の通りです:

ALTER TABLE [db.]table_name
ADD VECTOR INDEX index_name column_name
TYPE index_type
(
  'param1 = value1',
  'param2 = value2',
  ...
)
  • index_name: ベクトルインデックスの名前。
  • column_name: ベクトルインデックスが作成される列の名前。この列は、Array(Float32)またはFixedStringのいずれかのタイプでなければなりませんArray(Float32)タイプの場合、配列の長さを指定する制約が必要です。
  • index_type: ベクトルインデックスの特定のタイプ。

TIP

float32ベクトルの配列については、最適な結果を得るためにMSTGアルゴリズムの使用を強くお勧めします。ただし、以下の他のインデックスタイプも利用可能です。

  • FLAT: 比較のためのブルートフォースアルゴリズム
  • ScaNN: Google Research が開発したスケーラブル最近傍探索アルゴリズム
  • IVFファミリー: IVFFLATIVFSQIVFPQを含む
  • HNSWファミリー: HNSWFLATHNSWSQHNSWPQを含む

バイナリ文字列ベクトルの場合、利用可能なインデックスタイプはBinaryFLATBinaryMSTGであり、最適な結果を得るためにBinaryMSTGアルゴリズムの使用を強くお勧めします。

NOTE

MSTGBinaryMSTG のベクトルインデックスタイプは、SaaS/BYOC バージョンでのみ提供されており、その他のタイプはすべてのバージョン(オープンソース、SaaS、BYOCを含む)で利用可能です。

例えば、test_float_vectorテーブルのvector列にMSTGタイプのベクトルインデックスidxを作成するには、次のコマンドを使用します:

ALTER TABLE test_float_vector ADD VECTOR INDEX idx vector TYPE MSTG

TIP

特定のタイプのパラメータの詳細については、ベクトルインデックス構成オプションの説明セクションを参照してください。パフォーマンスチューニングのアドバイスについては、パフォーマンスチューニングのアドバイスセクションを参照してください。

# ベクトルインデックスの削除

ベクトルインデックスを削除するには、次の構文を使用します。これにより、インデックスが削除され、関連するメモリとディスクリソースが解放されます。インデックスが現在構築中である場合、構築プロセスも即座に停止されます。

ALTER TABLE [db.]table_name DROP VECTOR INDEX index_name

# ベクトルインデックスのステータスの確認

ベクトルインデックスの現在のステータスを確認するには、system.vector_indicesシステムテーブルを使用できます。次の構文を使用すると、すべての既存のベクトルインデックスを表示できます:

SELECT * FROM system.vector_indices

テーブル名やその他の条件で結果をフィルタリングするために、WHERE句を使用することもできます。たとえば、test_float_vectorなどの特定のテーブルのベクトルインデックスを表示するには、次のコマンドを使用します:

SELECT table, name, status FROM system.vector_indices
WHERE table = 'test_float_vector'

これにより、ベクトルインデックスの現在のステータスなどの情報が出力されます。ステータスは次のいずれかです:

  1. Built:このステータスは、インデックスが正常に構築され、使用する準備ができていることを示します。
  2. InProgress:このステータスは、インデックスが現在構築中または更新中であることを意味します。この間、インデックスは不完全な状態であり、インデックスされていないデータのベクトル検索は力まかせアルゴリズムにフォールバックし、非常に遅くなります。
  3. Error:インデックスが構築または使用中にエラーが発生した場合、Errorステータスになります。これは、無効な入力データやシステムの障害など、さまざまな理由によるものです。このステータスの場合、エラーが解決するまで通常は使用できません。

Errorステータスのベクトルインデックスの場合、次のコマンドで失敗理由を表示できます:

SELECT table, name, latest_failed_part, latest_fail_reason
FROM system.vector_indices WHERE status = 'Error'

# 基本的なベクトル検索

ベクトル検索を実行するためには、distance()関数を使用します。この関数は、指定したベクトルと指定した列内のすべてのベクトルデータとの距離を計算し、上位の候補を返します。distance()関数の基本的な構文は以下の通りです:

distance('param1 = value1', 'param2 = value2')(column_name, query_vector)
  • paramsは、検索固有のパラメータを表します。paramsには、IVFFLATベクトルインデックスの場合のように、インデックス固有のパラメータも含めることができます。これにより、検索の範囲を定義するためにnprobe = 1などを指定できます。
  • column_nameは、検索対象のベクトルデータが含まれる列を指します。
  • query_vector は検索されるベクトルです。

TIP

  • float32ベクトルの配列の場合、クエリベクトルに小数点を含めることが重要です。これにより、Array(UInt64)型として認識されることを防ぎ、クエリの実行時にエラーが発生することを防ぎます。例えば、float32ベクトルの128D配列は [3.0, 9, ..., 4] の形式です。
  • バイナリ文字列ベクトルの場合、他のタイプと比べて表示や入力が容易ではないかもしれませんが、特定の関数の助けを借りて操作することができます。
    • char()関数を使用してバイナリベクトルを生成します。この関数は、渡された引数の数と同じ長さのバイナリ文字列を返し、各バイトは対応する引数の値を持ちます。例えば、128Dバイナリ文字列ベクトルは char(128, 254, ... 127, 100) の形式です。
    • unbin()関数を使用してバイナリベクトルを生成します。この関数は、引数の各ペアの二進数を数値として解釈し、その数値で表されるバイトに変換します。例えば、128Dバイナリ文字列ベクトルは unbin('01010...01010') の形式です。
    • unhex()関数を使用してバイナリベクトルを生成します。この関数は、引数の各ペアの16進数を数値として解釈し、その数値で表されるバイトに変換します。例えば、128Dバイナリ文字列ベクトルは unhex('FFEEDDCC...112233') の形式です。
  • distance()関数は、上位の候補を取得するためにorder byとlimit句とともに使用する必要があります。
  • order by句のdistance関数の列のソート方向は、ベクトルインデックスのメトリックタイプ(Cosine、IPなど)に対応している必要があります。そうでない場合、エラーが報告されます。メトリックタイプがIPの場合、ソート方向はDESCである必要があります。

float32ベクトルの配列の検索クエリの典型的な例は次のようになります:

SELECT id, date, label,
  distance(data, [3.0, 9, 45, 22, 28, 11, 4, 3, 77, 10, 4, 1, 1, 4, 3, 11, 23, 0, 0, 0, 26, 49, 6, 7, 5, 3, 3, 1, 11, 50, 8, 9, 11, 7, 15, 21, 12, 17, 21, 25, 121, 12, 4, 7, 4, 7, 4, 41, 28, 2, 0, 1, 10, 42, 22, 20, 1, 1, 4, 9, 31, 79, 16, 3, 23, 4, 6, 26, 31, 121, 87, 40, 121, 82, 16, 12, 15, 41, 6, 10, 76, 48, 5, 3, 21, 42, 41, 50, 5, 17, 18, 64, 86, 54, 17, 6, 43, 62, 56, 84, 116, 108, 38, 26, 58, 63, 20, 87, 105, 37, 2, 2, 121, 121, 38, 25, 44, 33, 24, 46, 3, 16, 27, 74, 121, 55, 9, 4]) AS dist
FROM test_float_vector
ORDER BY dist LIMIT 10

このクエリは、test_float_vectorテーブルのベクトル列とクエリベクトル[3.0, 9, ..., 4]の間の距離を返します。ORDER BY dist LIMIT 10句は、上位10件の最も近い結果を返すことを指定しています。

出力:

id date label dist
3 "2024-08-11" "animal" 0
790110 "2001-10-14" "person" 102904
396372 "1987-12-15" "animal" 108579
401952 "1975-08-24" "animal" 117388
603558 "1999-09-26" "animal" 118487
25589 "1978-08-29" "animal" 119259
12632 "2019-02-25" "animal" 119662
800289 "2000-07-09" "building" 119673
16298 "1997-03-11" "animal" 120011
395903 "2020-08-19" "animal" 121352

クエリの結果は、iddatelabel、およびクエリベクトルとの距離を示すdistの3つの列からなるテーブルです。

したがって、バイナリ文字列ベクトルの検索クエリの典型的な例は次のようになります:

SELECT id, date, label,
  distance(binary_data, unhex('ABCDEF0123456789ABCDEF0123456789')) AS dist
FROM test_binary_vector
ORDER BY dist LIMIT 10

# フィルター付きのベクトル検索

フィルター付きのベクトル検索では、他の列の値や距離値に基づいて結果を絞り込むことができます。たとえば、次のクエリは、id列が100000より大きい行についてのみ、test_float_vectorテーブルのベクトル列とクエリベクトル[3.0, 9, ..., 4]の間の距離を返します:

SELECT id, date, label,
  distance(data, [3.0, 9, 45, 22, 28, 11, 4, 3, 77, 10, 4, 1, 1, 4, 3, 11, 23, 0, 0, 0, 26, 49, 6, 7, 5, 3, 3, 1, 11, 50, 8, 9, 11, 7, 15, 21, 12, 17, 21, 25, 121, 12, 4, 7, 4, 7, 4, 41, 28, 2, 0, 1, 10, 42, 22, 20, 1, 1, 4, 9, 31, 79, 16, 3, 23, 4, 6, 26, 31, 121, 87, 40, 121, 82, 16, 12, 15, 41, 6, 10, 76, 48, 5, 3, 21, 42, 41, 50, 5, 17, 18, 64, 86, 54, 17, 6, 43, 62, 56, 84, 116, 108, 38, 26, 58, 63, 20, 87, 105, 37, 2, 2, 121, 121, 38, 25, 44, 33, 24, 46, 3, 16, 27, 74, 121, 55, 9, 4]) AS dist
FROM test_float_vector WHERE id > 100000
ORDER BY dist LIMIT 10

出力:

id date label dist
790110 "2001-10-14" "person" 102904
396372 "1987-12-15" "animal" 108579
401952 "1975-08-24" "animal" 117388
603558 "1999-09-26" "animal" 118487
800289 "2000-07-09" "building" 119673
395903 "2020-08-19" "animal" 121352
600737 "1972-08-25" "animal" 125027
790101 "1990-02-22" "person" 129224
790265 "2019-05-26" "building" 133267
198290 "1974-04-22" "building" 134178

distance値でフィルタリングするには、次のようにWHERE句を使用します:

SELECT id, date, label,
  distance(data, [3.0, 9, 45, 22, 28, 11, 4, 3, 77, 10, 4, 1, 1, 4, 3, 11, 23, 0, 0, 0, 26, 49, 6, 7, 5, 3, 3, 1, 11, 50, 8, 9, 11, 7, 15, 21, 12, 17, 21, 25, 121, 12, 4, 7, 4, 7, 4, 41, 28, 2, 0, 1, 10, 42, 22, 20, 1, 1, 4, 9, 31, 79, 16, 3, 23, 4, 6, 26, 31, 121, 87, 40, 121, 82, 16, 12, 15, 41, 6, 10, 76, 48, 5, 3, 21, 42, 41, 50, 5, 17, 18, 64, 86, 54, 17, 6, 43, 62, 56, 84, 116, 108, 38, 26, 58, 63, 20, 87, 105, 37, 2, 2, 121, 121, 38, 25, 44, 33, 24, 46, 3, 16, 27, 74, 121, 55, 9, 4]) AS dist
FROM test_float_vector WHERE dist < 110000
ORDER BY dist LIMIT 10

このクエリは、test_float_vectorテーブルのベクトル列とクエリベクトル[3.0, 9, ..., 4]の間の距離を返しますが、距離が110000未満の行についてのみです。

出力:

id date label dist
3 "2024-08-11" "animal" 0
790110 "2001-10-14" "person" 102904
396372 "1987-12-15" "animal" 108579

# ベクトルインデックスの設定オプションの説明

ベクトルインデックスには、インデックス作成パラメータ検索パラメータの2種類のパラメータがあります。

インデックス作成パラメータは、インデックス作成時に指定されます。たとえば、

ALTER TABLE [db.]table_name
ADD VECTOR INDEX index_name column_name
TYPE index_type
(
  'creation_param1 = value1',
  'creation_param2 = value2',
  ...
)

検索パラメータは、検索中に指定されます。例えば、

SELECT
  id,
  distance('search_param1 = value1', 'search_param2 = value2')(column_name, query_vector) as dist
FROM [db.]table_name
ORDER BY dist LIMIT 10

# 一般的なインデックス作成パラメータ

以下のパラメータは、任意のタイプのベクトルインデックスの作成に使用できます。

  • metric_type: このパラメータは、ベクトル検索で使用される距離メトリックを決定します。
    • Float32ベクトルの配列の場合、3つのオプションが利用可能で、L2がデフォルト値です。
      • L2: L2メトリック、別名ユークリッド距離。
      • Cosine: コサイン距離は、コサイン類似度に基づいています。コサイン距離の公式は次のように計算されます:

        コサイン類似度は、距離値を1から引くことで得られます。
      • IP: 内積(IP)メトリック。IPメトリックを使用する場合は、より高いIP値がより大きな類似性を示すため、ORDER BY ... DESCを使用する必要があります。
    • 二進数文字列ベクトルの場合、1つのオプションが利用可能で、Hammingがデフォルト値です。
      • Hamming: 二つの同じ長さのベクトル間のハミング距離は、対応する位置で異なる記号の数です。
      • Jaccard: ジャカード距離は非類似性を測定します。ジャカード距離の公式は次のように計算されます:

        ジャカード係数は、距離値を1から引くことで得られます。

# ScaNN / MSTG / BinaryMSTG パラメータ

ScaNN は、Google が高次元ベクトル空間での高速かつスケーラブルな最近傍探索のために開発したアルゴリズムです。

MyScaleが開発したMulti-Scale Tree Graph(MSTG)アルゴリズムは、標準的なベクトル検索操作とフィルタリングされたベクトル検索操作の両方において、高いデータ密度と高いパフォーマンスを提供するために設計された独自のソリューションです。

インデックス作成パラメータ:

ScaNNMSTGBinaryMSTG は、インデックスの作成中に metric_type 以外のパラメータを受け付けません。

検索パラメータ:

  • alpha = float: このパラメータは、検索操作の精度を制御します。値が高いほど検索はより正確になりますが、QPSは低下します。デフォルト値は3で、有効な範囲は1から4です。

# FLAT / BinaryFLAT パラメータ

FLAT および BinaryFLAT は、ベクトルインデックスの最も単純な形態であり、追加の最適化パラメーターなしで生データに基づいて距離を直接計算します。プロトタイピングや検索結果の正確性を確保するためには便利ですが、比較的遅いパフォーマンスのため、本番環境での使用はお勧めできません。

# IVFFLATパラメータ

IVFFLATは、クラスタリングを使用してベクトルをより効率的に検索するために、ベクトルをより小さなクラスタに分割する階層的なインデックスです。

インデックス作成パラメータ:

  • ncentroids = int: すべてのベクトルデータを分割するクラスタの数を決定します。ncentroidsの値が大きいほど、テーブルの構築時間が遅くなります。デフォルト値は1024です。

検索パラメータ:

  • nprobe = int: 検索操作中に検索するクラスタの数を指定します。値が大きいほど検索は遅くなりますが、精度が向上します。デフォルト値は1です。

推奨されるパラメータの値:

ncentroidsには、データ量の平方根に近い値を選ぶことを推奨します。値が大きすぎるとパフォーマンスに影響が出る可能性があるためです。nprobeには、ncentroidsの0.1-10%の値を推奨します。

# IVFPQパラメータ

インデックス作成パラメータ:

  • ncentroids = int: IVFFLATを参照してください。

  • M = int: 元のベクトルの次元をMに削減します。Mは元のベクトルの次元で割り切れる必要があります。デフォルト値は16です。

  • bit_size = int: 元のベクトルを置き換えるために使用されるProduct Quantization(PQ)エンコーディングテーブルのサイズを指定します。有効な値は4の倍数で、デフォルト値は8です。

検索パラメータ:

  • nprobe = int: IVFFLATを参照してください。

推奨されるパラメータの値:

ncentroidsnprobeの推奨値は、IVFFLATと同様です。PQの圧縮率は、(bit_size * M) / (original_dimension * original_element_size)で計算されます。128次元のfloat32ベクトルの場合、M = 16bit_size = 8の場合、対応する圧縮率は(16*8)/(128*32) = 1/32です。圧縮率が極端に高いと、検索結果の精度に大きな影響を与える可能性があるため、この問題を回避するためにbit_sizeを8に保ち、Mを元の次元の1/4以内に保つことを推奨します。

# IVFSQ パラメータ

インデックス作成パラメータ:

  • ncentroids = int: IVFFlatを参照してください。

  • bit_size = string: 許容される値は 8bit, 6bit, 4bit, 8bit_uniform, 8bit_direct, 4bit_uniform, QT_fp16 で、デフォルト値は 8bit です。

スカラー量子化(SQ)アルゴリズムは、各ベクトル次元を圧縮しながら次元数を保持するために使用されます。bit_size を 8 に設定すると、圧縮率は約 25% になります。アルゴリズムの精度は、8bit_direct8bit_uniform8bit の順に高くなりますが、インデックスの構築速度は精度に反比例します。8bit_direct は、static_cast を使用して float を uint_8 に変換します。8bit_uniform は、すべての float 値を均等に 256 個のビンに分割します。8bit は、各次元を 256 個のビンに均等に分割します。4bit_uniform は、データを 16 個のビンに分割して量子化します。QT_fp16 は、半精度浮動小数点数を使用する SQ アルゴリズムのバリエーションであり、詳細はリンク https://gist.github.com/rygorous/2156668 (opens new window) で確認できます。

検索パラメータ:

  • nprobe = int: IVFFlatを参照してください。

# HNSWFLAT パラメータ

階層型ナビゲーション可能な小世界(HNSW)アルゴリズムは、高次元空間において与えられたクエリポイントの最近傍点を迅速に見つけるための近似最近傍探索アルゴリズムの一種です。これは、データポイントを多段階のグラフデータ構造に組織化することで実現されます。HNSW アルゴリズムは、「小世界」ネットワークの原理を利用して、グラフをナビゲートし、クエリポイントに最も近いデータポイントを効率的に見つけます。大規模なデータセットや高次元空間において、高いパフォーマンスとスケーラビリティで知られています。

インデックス作成パラメータ:

  • m = int: このパラメータは、HNSW ダイアグラム内の各データポイントの近傍数を決定し、インデックスの品質に影響を与えます。m を大きくすると、検索結果の精度が向上しますが、インデックスの構築時間も増加します。デフォルト値は 16 です。
  • ef_c = int: このパラメータは、インデックスの作成時に HNSW が使用する優先度キューのサイズを決定し、インデックスの品質に影響を与えます。ef_c を大きくすると、検索結果の精度が向上しますが、インデックスの構築時間も増加します。デフォルト値は 100 です。

検索パラメータ:

  • ef_s = int: このパラメータは、検索操作中に HNSW が使用する優先度キューのサイズを決定します。ef_s を大きくすると、検索結果の精度が向上しますが、検索時間も増加します。デフォルト値は 50 です。

推奨されるパラメータ値:

一般的には、m を 8-128 の範囲で設定し、ef_c を 50-400 の範囲で設定することを推奨します。ef_c を倍にすると、インデックスの構築時間がほぼ倍になります。ef_s の値は検索要件に応じて調整し、ef_c と同じ値の範囲を使用することを推奨します。低遅延の要件がある場合は、より低い値を選択することができます。

# HNSWSQ パラメータ

インデックス作成パラメータ:

  • m = int: HNSWFLATを参照してください。
  • ef_c = int: HNSWFLATを参照してください。
  • bit_size = string: IVFSQを参照してください。

検索パラメータ:

  • ef_s = int: HNSWFLATを参照してください。

推奨されるパラメータ値:

bit_size の選択については、IVFSQを参照し、その他のパラメータについてはHNSWFLATを参照してください。

# パフォーマンスチューニングのアドバイス

一般的に、パフォーマンスを最適化するために MSTG/BinaryMSTG インデックスとインデックス作成のデフォルト値の使用を推奨します。検索時には、希望するスループットと精度に基づいて alpha 値を調整できます。

MyScaleでは、データは自動的に複数のパーツに分割され、内部的には各パーツごとに1つのベクトルインデックスが作成されます。最適なパフォーマンスを得るためには、ベクトルインデックスを作成する前にテーブルを1つのデータパーツに最適化することをおすすめします。次のコマンドを使用して、テーブルを最適化することができます。

OPTIMIZE TABLE test_vector FINAL

したがって、操作の推奨される順序は次のとおりです。

  1. CREATE TABLE ... を使用してテーブルを作成します。
  2. INSERT INTO ... を使用してデータを挿入します。
  3. テーブルを最適化します。
  4. ALTER TABLE ... ADD VECTOR INDEX を使用してベクトルインデックスを作成します。
  5. ベクトルインデックスの構築が完了するのを待ちます。
  6. 検索を開始します。

インデックス作成後にテーブルを最適化すると、多くのメモリを消費する可能性があることに注意してください。したがって、インデックス作成後にテーブルを最適化しないでください。

Last Updated: Mon Apr 29 2024 08:19:07 GMT+0000