PASE はメンテナンスが終了しています。代わりに pgvector 拡張機能を使用してください。詳細については、「pgvector 拡張機能を使用した高次元ベクトル類似度検索の実行」をご参照ください。
PASE (PostgreSQL ANN search extension) は、IVFFlat と HNSW (Hierarchical Navigable Small World) という 2 つのインデックスアルゴリズムを使用して、ApsaraDB RDS for PostgreSQL に近似最近傍 (ANN) ベクトル検索機能を追加します。
PASE は既存のベクトルに対して類似度検索を実行します。特徴ベクトルを抽出または生成する機能はないため、ユーザーが用意する必要があります。
前提条件
開始する前に、以下が揃っていることを確認してください。
-
PostgreSQL 11 以降を実行している ApsaraDB RDS for PostgreSQL インスタンス (PostgreSQL 17 はサポートされていません)
-
このトピックの SQL ステートメントを実行するための特権アカウント
インデックスアルゴリズムの選択
どちらのアルゴリズムも、ビルド時間とメモリを犠牲にしてクエリの再現率を向上させるトレードオフの関係にあります。以下の表を参考に選択してください。
| IVFFlat | HNSW | |
|---|---|---|
| 仕組み | ベクトルをクラスターに分割し、ターゲットに近いクラスターのサブセットをクエリします | 多層の近接グラフを構築し、粗いレイヤーから細かいレイヤーへと走査します |
| 最適な用途 | 最大 100 ms までのレイテンシーが許容される高精度なクエリ | 10 ms 以内の応答が求められる大規模なデータセット (数千万ベクトル) |
| 100% の再現率は可能か? | はい、クエリベクトルが候補データセット内にある場合 | 精度は一定のレベルで頭打ちになり、パラメーターの調整だけでは向上させられません |
| ビルド時間 | 速い | 遅い |
| ストレージ | 低い | 高い (近接グラフの近傍を格納するため) |
| インデックス作成前にデータは必要か? | はい、内部クラスタリング (clustering_type = 1) を使用する場合 — k-means トレーニングステップのためにデータが必要です |
いいえ |
IVFFlat の使用:画像比較など、ある程度のクエリレイテンシーが許容される高精度なワークロードに使用します。
HNSW の使用:完全な再現率よりも低レイテンシーが重要な、大規模なレコメンデーションシステムやセマンティック検索に使用します。
仕組み
IVFFlat
IVFFlat は IVFADC アルゴリズムの簡易版です。
-
IVFFlat は k-means を使用してベクトルをクラスターに分割します。各クラスターには重心があります。
-
クエリベクトルに最も近い
n個の重心を見つけます。 -
それらの
n個のクラスター内のすべてのベクトルを検索し、最も近いk個の結果を返します。
遠いクラスターをスキップすることでクエリは高速化されますが、再現率が低下する可能性があります。クエリに類似したベクトルが、選択されたクラスターの外に存在する可能性があるためです。n を大きくすると、計算コストは増加しますが再現率は向上します。
IVFFlat が IVFADC と異なるのは第 2 フェーズです。IVFADC はプロダクト量子化を使用して力まかせ探索をスキップするため、より高速ですが精度は低くなります。IVFFlat は選択されたクラスター内で力まかせ探索を使用するため、精度と速度のトレードオフを直接コントロールできます。
HNSW
HNSW はグラフベースの ANN アルゴリズムです。
-
HNSW はレイヤー (グラフ) の階層構造を構築します。各レイヤーは、その下のレイヤーをより粗く表現したビューです。
-
検索は最上位レイヤーのランダムな要素から開始されます。
-
HNSW は現在の要素の近傍を見つけ、それらを距離でソートされた固定長の候補リストに追加し、近傍の展開を続けます。リストが安定すると、最上位の要素が次のレイヤーへのエントリポイントになります。
-
このプロセスは、最下層が検索されるまで繰り返されます。
HNSW は、もともと単層グラフ用に設計された Navigable Small World (NSW) アルゴリズムを使用して各レイヤーを構築し、クラスタリングベースのメソッドよりも高速な走査を可能にします。
このトピック全体を通して、機械学習と ANN 検索アルゴリズムに関する基本的な知識があることを前提としています。
PASE のセットアップ
ステップ 1:拡張機能の有効化
CREATE EXTENSION pase;
ステップ 2:ベクトル類似度の計算 (オプションのクイックテスト)
インデックスを作成する前に、<?> 演算子を使用して 2 つのベクトル間の類似度を計算します。
PASE タイプの構造:
SELECT ARRAY[2, 1, 1]::float4[] <?> pase(ARRAY[3, 1, 1]::float4[]) AS distance;
SELECT ARRAY[2, 1, 1]::float4[] <?> pase(ARRAY[3, 1, 1]::float4[], 0) AS distance;
SELECT ARRAY[2, 1, 1]::float4[] <?> pase(ARRAY[3, 1, 1]::float4[], 0, 1) AS distance;
文字列による構築:
SELECT ARRAY[2, 1, 1]::float4[] <?> '3,1,1'::pase AS distance;
SELECT ARRAY[2, 1, 1]::float4[] <?> '3,1,1:0'::pase AS distance;
SELECT ARRAY[2, 1, 1]::float4[] <?> '3,1,1:0:1'::pase AS distance;<?> 演算子は、左辺に float4[] を、右辺に PASE 値を取ります。両方のベクターは、同じ数のディメンションを持つ必要があります。さもないと、PASE は類似度計算エラーを返します。
PASE コンストラクター (float4[], second_param, similarity_method) では:
-
2 番目のパラメーター:予約済みです。
0に設定します。 -
類似度メソッド:
0= ユークリッド距離、1= ドットプロダクト (内積)
ドットプロダクトまたは余弦類似度メソッドを使用する場合は、まず入力ベクターを正規化します。 正規化されたベクターは![]()
を満たします。 正規化されたベクターの場合、ドットプロダクトは余弦値と等しくなります。 ApsaraDB RDS for PostgreSQL は、ユークリッド距離メソッドを直接サポートしています。ドットプロダクトメソッドは、ベクターを正規化した後にのみ使用してください。
ステップ 3:インデックスの作成
IVFFlat インデックス
CREATE INDEX ivfflat_idx ON table_name
USING
pase_ivfflat(column_name)
WITH
(clustering_type = 1, distance_type = 0, dimension = 256, base64_encoded = 0, clustering_params = "10,100");
パラメーター:
| パラメーター | 必須 | デフォルト | 説明 |
|---|---|---|---|
clustering_type |
はい | — | クラスタリングメソッド。0 = 外部 (clustering_params で指定された重心ファイルをロード)。1 = 内部 k-means。PASE を初めて使用する場合は 1 から始めてください。 |
distance_type |
いいえ | 0 |
類似度メソッド。0 = ユークリッド距離。1 = ドット積 (正規化されたベクトルが必要。ドット積の順序はユークリッド距離の順序と逆になります)。ApsaraDB RDS for PostgreSQL はユークリッド距離のみを直接サポートしています。 |
dimension |
はい | — | ベクトル次元数。最大:512。 |
base64_encoded |
いいえ | 0 |
ベクトルエンコーディング。0 = float4[]。1 = Base64 エンコードされた float[]。 |
clustering_params |
はい | — | 内部クラスタリング (clustering_type = 1) の場合:"sampling_ratio,k"。sampling_ratio はサンプリング率で、分母を 1000 とし、範囲は (0, 1000] です。値が 1 の場合、k-means を実行する前に 1:1000 の比率でサンプリングします。サンプリングされたレコードの合計は 100,000 未満にしてください。k は重心の数で、[100, 1000] の範囲の値を使用します。外部クラスタリング (clustering_type = 0) の場合:重心ファイルのディレクトリパス。 |
HNSW インデックス
CREATE INDEX hnsw_idx ON table_name
USING
pase_hnsw(column_name)
WITH
(dim = 256, base_nb_num = 16, ef_build = 40, ef_search = 200, base64_encoded = 0);
パラメーター:
| パラメーター | 必須 | 有効値 | 説明 |
|---|---|---|---|
dim |
はい | [8, 512] |
ベクトル次元数。 |
base_nb_num |
はい | 推奨:[16, 128] |
要素ごとに格納される近傍の数。値を大きくすると再現率は向上しますが、ビルド時間とストレージが増加します。 |
ef_build |
はい | 推奨:[40, 400] |
インデックス構築時の候補ヒープサイズ。値を大きくすると再現率は向上しますが、ビルドが遅くなります。 |
ef_search |
はい | [10, 400] |
クエリ時の候補ヒープサイズ。値を大きくすると再現率は向上しますが、クエリのスループットが低下します。 |
base64_encoded |
いいえ | 0 または 1 |
ベクトルエンコーディング。0 = float4[]。1 = Base64 エンコードされた float[]。 |
ステップ 4:ベクトルのクエリ
IVFFlat クエリ
<#> 演算子を使用し、ORDER BY を含めます。インデックスは ORDER BY がないと有効になりません。
SELECT id, vector <#> '1,1,1'::pase AS distance
FROM table_name
ORDER BY
vector <#> '1,1,1:10:0'::pase
ASC LIMIT 10;
クエリ文字列のフォーマットは vector:n:similarity_method です。
-
`n`:検索するクラスターの数。範囲は
(0, 1000]です。値を大きくすると速度は低下しますが、再現率は向上します。再現率とレイテンシーの要件に基づいて調整してください。 -
`similarity_method`:
0= ユークリッド距離、1= ドット積 (正規化されたベクトルが必要)。
HNSW クエリ
<?> 演算子を使用し、ORDER BY を含めます。
SELECT id, vector <?> '1,1,1'::pase AS distance
FROM table_name
ORDER BY
vector <?> '1,1,1:100:0'::pase
ASC LIMIT 10;
クエリ文字列のフォーマットは vector:ef:similarity_method です。
-
`ef`:クエリ時の候補ヒープサイズ。範囲は
(0, ∞)です。40から始め、ワークロードに適したバランスが見つかるまで少しずつ増やしてください。 -
`similarity_method`:
0= ユークリッド距離、1= ドット積 (正規化されたベクトルが必要)。
注意事項
-
インデックスの肥大化:
SELECT pg_relation_size('index_name');でインデックスサイズを確認し、テーブルサイズと比較してください。インデックスがテーブルより大きく、クエリが遅くなった場合は、インデックスを再構築してください。 -
更新後の精度のドリフト:頻繁なデータ更新により、インデックスが古くなることがあります。100% の再現率が必要な場合は、定期的なスケジュールでインデックスを再構築してください。
-
内部重心を使用した IVFFlat インデックスの作成:
clustering_type = 1を設定し、インデックスを作成する前にテーブルにデータが含まれていることを確認してください。
例:IVFFlat のエンドツーエンド
この例では、テーブルを作成し、3 次元のベクトルをロードし、IVFFlat インデックスを構築し、最近傍クエリを実行します。
-- ステップ 1:拡張機能の有効化
CREATE EXTENSION pase;
-- ステップ 2:テーブルの作成
CREATE TABLE vectors_table (
id SERIAL PRIMARY KEY,
vector float4[] NOT NULL
);
-- ステップ 3:サンプルデータの挿入
INSERT INTO vectors_table (vector) VALUES
('{1.0, 0.0, 0.0}'),
('{0.0, 1.0, 0.0}'),
('{0.0, 0.0, 1.0}'),
('{0.0, 0.5, 0.0}'),
('{0.0, 0.5, 0.0}'),
('{0.0, 0.6, 0.0}'),
('{0.0, 0.7, 0.0}'),
('{0.0, 0.8, 0.0}'),
('{0.0, 0.0, 0.0}');
-- ステップ 4:IVFFlat インデックスの構築
CREATE INDEX ivfflat_idx ON vectors_table
USING
pase_ivfflat(vector)
WITH
(clustering_type = 1, distance_type = 0, dimension = 3, base64_encoded = 0, clustering_params = "10,100");
-- ステップ 5:[1, 1, 1] に最も近い 10 個の近傍をクエリ
SELECT id, vector <#> '1,1,1'::pase AS distance
FROM vectors_table
ORDER BY
vector <#> '1,1,1:10:0'::pase
ASC LIMIT 10;
付録
ドット積またはコサイン類似度の計算
正規化されたベクトルのドット積は、そのコサイン値と等しくなります。この関数は HNSW インデックスを使用して、ドット積による上位 k 個の結果を取得します。
CREATE OR REPLACE FUNCTION inner_product_search(
query_vector text,
ef integer,
k integer,
table_name text
)
RETURNS TABLE (id integer, uid text, distance float4) AS $$
BEGIN
RETURN QUERY EXECUTE format('
SELECT a.id, a.vector <?> pase(ARRAY[%s], %s, 1) AS distance
FROM (
SELECT id, vector
FROM %s
ORDER BY vector <?> pase(ARRAY[%s], %s, 0) ASC
LIMIT %s
) a
ORDER BY distance DESC;',
query_vector, ef, table_name, query_vector, ef, k
);
END
$$ LANGUAGE plpgsql;
IVFFlat での外部重心ファイルの使用
これは高度なオプションです。外部の重心ファイルをサーバーディレクトリにアップロードし、clustering_type = 0 のときに clustering_params で参照します。ファイル形式は次のとおりです。
次元数|重心の数|重心ベクトルデータセット
例:
3|2|1,1,1,2,2,2
リファレンス
-
Product Quantization for Nearest Neighbor Search — Hervé Jégou, Matthijs Douze, Cordelia Schmid
-
Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs — Yu.A. Malkov, D.A. Yashunin