ClickHouse/docs/ja/engines/table-engines/mergetree-family/annindexes.md
2024-11-18 11:58:58 +09:00

14 KiB
Raw Blame History

近似最適近傍探索インデックス [エクスペリメンタル]

最適近傍探索は、N次元ベクトル空間で与えられた点に対して最も近いM個の点を見つける問題です。この問題を解決する最も単純なアプローチは、ベクトル空間内のすべての点と参照点の距離を計算するブルートフォース検索です。この方法は完全な精度を保証しますが、実際のアプリケーションにおいては通常、速度が遅すぎます。したがって、最適近傍探索の問題は、多くの場合近似アルゴリズムで解決されます。近似最適近傍探索技術は、埋め込みメソッドと組み合わせて、大量のメディア(画像、曲、記事など)をミリ秒単位で検索することが可能です。

ブログ:

SQLでは、最適近傍問題を次のように表現できます:

SELECT *
FROM table
ORDER BY Distance(vectors, Point)
LIMIT N

vectorsは、Array(Float32)またはArray(Float64)型のN次元の値例えば埋め込みを含みます。関数Distanceは、二つのベクトル間の距離を計算します。距離関数としては、しばしばユークリッドL2距離が選ばれますが、他の距離関数も可能です。Pointは参照点、例えば(0.17, 0.33, ...)で、Nは検索結果の数を制限します。

このクエリは、参照点に最も近いトップN個の点を返します。N引数は返される値の数を制限し、事前にMaxDistanceを決定するのが難しい場合に便利です。

ブルートフォース検索では、vectors内のすべての点とPoint間の距離を計算する必要があるため、クエリは高価です点の数に対して線形です。このプロセスを速めるために、近似最適近傍探索インデックスANNインデックスは検索空間のコンパクトな表現クラスタリング、検索ツリーなどの使用を保存します。これにより、近似的な答えをより迅速にサブ線形時間で計算することが可能です。

ベクトル類似インデックスの作成と使用

Array(Float32)カラムに対するベクトル類似インデックスを作成する構文:

CREATE TABLE table
(
  id Int64,
  vectors Array(Float32),
  INDEX index_name vectors TYPE vector_similarity(method, distance_function[, quantization, hnsw_max_connections_per_layer, hnsw_candidate_list_size_for_construction]) [GRANULARITY N]
)
ENGINE = MergeTree
ORDER BY id;

パラメータ:

  • method: 現在はhnswのみサポートしています。
  • distance_function: L2Distanceユークリッド距離 - ユークリッド空間内の二点間の線の長さ)またはcosineDistanceコサイン距離- ゼロでない二つのベクトル間の角度)。
  • quantization: ベクトルを精度を落として保存するためのf64f32f16bf16、またはi8(オプション、デフォルト: bf16
  • hnsw_max_connections_per_layer: HNSWグラフードごとの隣接ード数、HNSWペーパーMとして知られています(オプション、デフォルト: 32
  • hnsw_candidate_list_size_for_construction: HNSWグラフを構築する際の動的候補リストのサイズ、元のHNSWペーパーef_constructionとして知られています(オプション、デフォルト: 128

hnsw_max_connections_per_layerおよびhnsw_candidate_list_size_for_constructionの値が0の場合、これらのパラメータのデフォルト値が使用されます。

例:

CREATE TABLE table
(
  id Int64,
  vectors Array(Float32),
  INDEX idx vectors TYPE vector_similarity('hnsw', 'L2Distance') -- 代替構文: TYPE vector_similarity(hnsw, L2Distance)
)
ENGINE = MergeTree
ORDER BY id;

ベクトル類似インデックスはUSearchライブラリに基づいており、HNSWアルゴリズムを実装しています。すなわち、各点がベクトルを表し、辺が類似性を表す階層的なグラフです。このような階層構造は大規模なコレクションに非常に効率的です。全体のデータセットから0.05以下のデータを取得しながら、99の再現率を提供することが頻繁にあります。これは高次元ベクトル作業で特に役立ちます。高次元ベクトルはロードと比較にコストがかかるからです。このライブラリはまた、最新のArmNEONとSVEおよびx86AVX2とAVX-512CPUで距離計算をさらに加速するためのハードウェア固有のSIMD最適化や、RAMにロードせずに不変の永続ファイル周りを効率的にナビゲートするためのOS固有の最適化も備えています。

USearchインデックスは現在エクスペリメンタルであり、使用するにはまずSET allow_experimental_vector_similarity_index = 1を設定する必要があります。

ベクトル類似インデックスは現在二つの距離関数をサポートしています:

  • L2Distance、ユークリッド距離とも呼ばれ、ユークリッド空間内の二点間の線分の長さ(Wikipedia)。
  • cosineDistance、コサイン類似性とも呼ばれ、二つの(ゼロでない)ベクトル間の角度のコサイン(Wikipedia)。

ベクトル類似インデックスはベクトルを精度を落とした形式で保存することができます。サポートされているスカラー型はf64f32f16bf16、およびi8です。インデックス作成時にスカラー型を指定しなかった場合、デフォルトでbf16が使用されます。

正規化されたデータには通常L2Distanceがより良い選択であり、そうでない場合はスケールを補うためcosineDistanceが推奨されます。インデックス作成時に距離関数が指定されなかった場合、デフォルトでL2Distanceが使用されます。

:::note すべての配列は同じ長さである必要があります。エラーを回避するために、例えばCONSTRAINT constraint_name_1 CHECK length(vectors) = 256といったCONSTRAINTを使用することができます。また、INSERT文で空のArraysや指定されていないArray値(つまりデフォルト値)はサポートされていません。 :::

:::note 現在、ベクトル類似インデックスはテーブルごとに設定される非デフォルトのindex_granularity設定では機能しません。こちらを参照。必要があれば、config.xmlで値を変更する必要があります。 :::

ベクトルインデックス作成は遅いことが知られています。このプロセスを速めるために、インデックス作成を並列化することができます。スレッドの最大数は、サーバー設定のmax_build_vector_similarity_index_thread_pool_sizeで設定できます。

ANNインデックスはカラムの挿入およびマージ時に構築されます。その結果、INSERTOPTIMIZEステートメントは通常のテーブルに比べて遅くなります。ANNインデックスは基本的に不変またはまれにしか変更されないデータにのみ使用され、読み取り要求が書き込み要求よりも多い場合に理想的です。

:::tip ベクトル類似インデックスの構築コストを削減するには、新しく挿入されたパーツにスキップインデックスの構築を無効にするmaterialize_skip_indexes_on_insertを設定してください。検索は正確な検索にフォールバックしますが、挿入されたパーツは通常テーブル全体のサイズに比べて小さいので、その影響は無視できる程度です。

ANNインデックスはこの種のクエリをサポートします:

WITH [...] AS reference_vector
SELECT *
FROM table
WHERE ...                       -- WHERE句はオプション
ORDER BY Distance(vectors, reference_vector)
LIMIT N
SETTINGS enable_analyzer = 0;   -- 一時的な制限で、解除される予定

:::tip 大きなベクトルを出力するのを避けるため、クエリパラメータを使用することができます。例えば、

clickhouse-client --param_vec='hello' --query="SELECT * FROM table WHERE L2Distance(vectors, {vec: Array(Float32)}) < 1.0"

:::

HNSWパラメータhnsw_candidate_list_size_for_search(デフォルト: 256を異なる値で検索するために、SELECTクエリをSETTINGS hnsw_candidate_list_size_for_search = <value>を指定して実行します。

制限事項: 近似アルゴリズムを使用して最適近傍を決定するために、制限が必要です。そのため、LIMIT句のないクエリはANNインデックスを利用できません。また、ANNインデックスはクエリにmax_limit_for_ann_queries(デフォルト: 100万行より小さいLIMIT値がある場合にのみ使用されます。これは、近似近傍検索のために外部ライブラリによる大規模なメモリ割り当てを防ぐための安全策です。

スキップインデックスとの違い 通常のスキップインデックスと同様に、ANNインデックスはグラニュールごとに構築され、各インデックスブロックはGRANULARITY = [N]グラニュール(通常のスキップインデックスの場合はデフォルトで[N] = 1で構成されます。例えば、テーブルの主キーインデックスグラニュラリティが8192index_granularity = 8192設定)であり、GRANULARITY = 2の場合、各インデックスブロックは16384行を含みます。しかし、近似近傍検索のためのデータ構造とアルゴリズム通常外部ライブラリから提供されるは本質的に行指向です。それらは一連の行をコンパクトに表現し、ANNクエリに対する行も返します。これは通常のスキップインデックスがインデックスブロックの粒度でデータを飛び越える方法とは異なる振る舞いを引き起こします。

ユーザーがカラムにANNインデックスを定義すると、ClickHouseは内部的に各インデックスブロックに対してANN「サブインデックス」を作成します。サブインデックスは「ローカル」であり、自身の含むインデックスブロック内の行のみを認識しています。前述の例で、カラムが65536行あると仮定すると、四つのインデックスブロック八つのグラニュールにまたがるが得られ、それぞれにANNサブインデックスが作成されます。サブインデックスは理論的には、インデックスブロック内でN個の最も近い点を持つ行を直接返すことができます。しかし、ClickHouseはデータをディスクからメモリにグラニュールの粒度でロードするため、サブインデックスは一致する行をグラニュール粒度に外挿します。これは通常のスキップインデックスがインデックスブロックの粒度でデータを飛び越える方法と異なります。

GRANULARITYパラメータは、いくつのANNサブインデックスが作成されるかを決定します。大きなGRANULARITY値は、少ないが大きなANNSサブインデックスを意味し、一つのサブインデックスがあるカラムまたはカラムのデータパートの場合まで、大きさを増やします。その場合、サブインデックスはカラム行全体またはその一部のすべてのグラニュールを直接返せる「グローバル」なビューを持ちます関連する行があるグラニュールは最大でLIMIT [N]個です。次のステップでは、ClickHouseがこれらのグラニュールをロードし、ブルートフォースで距離計算を行うことにより、実際の最良の行を確認します。小さいGRANULARITY値では、各サブインデックスが最大LIMIT Nまでのグラニュールを返します。結果として、より多くのグラニュールをロードして後処理する必要があります。一方、大きなGRANULARITY値を使用することが一般的に推奨されます。これは、ANNインデックスの処理性能が異なるだけで、検索精度には両方とも等しく良い影響を与えます。GRANULARITYがANNインデックスで指定されていない場合、デフォルト値は1億です。