PolarSearch のベクトル検索機能を使用すると、REST API を使用して、テキストや画像などの非構造化データに対して効率的な類似検索を実行できます。この機能は、大規模なデータセット内で最も類似した結果を迅速かつ正確に検出し、アプリケーションのインテリジェンスを効果的に向上させます。
特徴
ベクトル検索は、類似検索とも呼ばれ、ベクトル間の距離を比較することで最も類似したデータを見つける技術です。これは、キーワードの完全一致に依存する従来の検索とは根本的に異なります。
中心となる考え方は、テキスト、画像、音声などの非構造化データを、大規模言語モデル (LLM) などのディープラーニングモデルを使用して、ベクトル埋め込みと呼ばれる数値表現に変換することです。これらの多次元ベクトルは、データの深い意味情報を捉えます。
クエリを開始すると、PolarSearch はクエリ内容をベクトルに変換し、k-最近傍 (k-NN) 検索を実行します。このコアアルゴリズムは、データセット内でクエリベクトルに最も距離が近い k 個のベクトルを見つけます。k の値はユーザーが定義します。たとえば、k=5 の場合、PolarSearch は最も類似した 5 つの結果を見つけます。その後、PolarSearch はこれらの k 個の最も類似した結果を返します。
効率的な取得を実現するために、PolarSearch はベクトルインデックスとベクトルストレージの最適化という 2 つのコアコンポーネントに依存しています。
ベクトルインデックス: 大規模なデータセットでのフルスケール計算を避けるために、事前にベクトルインデックスを構築する必要があります。インデックスは、ベクトルデータの特徴に基づいてクエリに最適化されたデータ構造を構築します。クエリ中に、インデックスは検索範囲を大幅に絞り込むことができ、これにより取得パフォーマンスが大幅に向上します。PolarSearch は複数のタイプのベクトルインデックスをサポートしています。次のセクションでは、業界で主流の HNSW および IVF インデックスを紹介します。
階層的航行可能小世界 (HNSW): 高いパフォーマンスと高い取得率を提供するグラフベースのインデックスですが、それに応じてメモリオーバーヘッドも高くなります。クエリのレイテンシーが極めて低く、高い精度が要求され、データセットのサイズがメモリ容量内に収まるシナリオに適しています。
転置ファイル (IVF): メモリ使用量が少ないクラスタリングベースの転置インデックスです。メモリが限られている超大規模なデータセットを扱う必要があるシナリオに適していますが、検索精度は通常 HNSW よりもわずかに低くなります。
ベクトルストレージの最適化: ベクトルデータ、特に高次元ベクトルは、大量のメモリとストレージスペースを消費します。PolarSearch は、リソース消費を削減するための複数の最適化技術を提供します。
ベクトル量子化: この技術は、データ精度を低下させてデータを圧縮し、スペース使用量を大幅に削減します。圧縮率と精度のバランスを取ります。PolarSearch は、積量子化 (PQ)、スカラー量子化 (SQ)、およびバイナリ量子化 (BQ) をサポートしています。
ディスクベースのストレージ: 低メモリ環境向けに、一部のインデックスデータをディスクに保存することができます。これにより、ベクトル検索サービスをより低いメモリコストで実行できますが、クエリのレイテンシーはわずかに増加します。
注意
PolarSearch のベクトル検索機能を使用する際は、次の点に注意してください。
インデックスのトレーニング要件:
IVFインデックスとPQ(積量子化) 技術は、使用前に個別のトレーニングステップが必要です。モデルをトレーニングするために、代表的なベクトルデータのサンプルを提供する必要があります。そうしないと、インデックスは正しく機能しません。メモリオーバーヘッド:
HNSWインデックスは優れたパフォーマンスを提供しますが、そのグラフ構造は完全にメモリにロードする必要があり、高いメモリオーバーヘッドが発生します。このオプションを選択する前に、クラスターのメモリリソースを評価する必要があります。パフォーマンスとコストのトレードオフ: ディスクベースのベクトル検索は、クエリのレイテンシーをわずかに増加させます。ビジネスシナリオに基づいて、このトレードオフを評価する必要があります。
自動トレーニング: バイナリ量子化 (BQ) のトレーニングプロセスは、インデックス構築中に自動的に処理されます。追加のトレーニング操作を実行する必要はありません。
ユーザーガイド
準備
ベクトル検索に REST API を使用するには、まずインテリジェント検索 (PolarSearch) 機能を有効にする必要があります。新規または既存のクラスターで PolarSearch 機能を有効にする方法の詳細については、「PolarSearch ユーザーガイド」をご参照ください。
ステップ 1: ベクトルインデックスの作成
ベクトルを保存および検索するには、まず特定の構成でインデックスを作成する必要があります。これには 2 つの主要な操作が含まれます。
k-NN の有効化とベクトルフィールドの定義: インデックスの
settingsで、knnパラメーターをtrueに設定します。これは、インデックスがベクトル検索に使用されることを PolarDB に伝えるメインスイッチです。コアパラメーター
engine: このパラメーターは `faiss` に設定する必要があります。説明Faiss (Facebook AI Similarity Search) は、Meta AI によって開発された高性能なオープンソースライブラリです。大規模なベクトルデータの効率的な類似検索とクラスタリングのために設計されています。PolarSearch は、Faiss をコアベクトル検索エンジンとして使用しています。
dimension: ベクトルのディメンションを指定します。この値は、モデルによって生成されるベクトルのディメンションと完全に一致する必要があります。data_type: ベクトルのデータ型を定義します。デフォルト値はfloatです。byteまたはbinaryを選択してストレージを最適化することもできます。space_type: ベクトル類似度の計算方法を定義します。これは距離メジャーとも呼ばれます。サポートされているオプションは次のとおりです。space_type距離メジャー
説明
l2L2 (ユークリッド距離)
差の二乗和の平方根を計算します。値の大きさに敏感です。
l1L1 (マンハッタン距離)
ベクトルディメンション間の差の絶対値の合計を計算します。
cosinesimilコサイン類似度
大きさではなく方向性に焦点を当て、ベクトル間の角度を測定します。
innerproduct内積
ベクトルのドット積を計算します。ソートによく使用されます。
hammingハミング距離
バイナリベクトル内の異なる要素の数を計算します。
chebyshevL∞ (チェビシェフ距離)
ベクトルディメンション間の差の最大絶対値のみを考慮します。
ベクトルフィールドの定義 (HNSW または IVF): インデックスの
mappingsで、knn_vector型のフィールドを定義します。このフィールドは、ベクトルデータを保存するために特別に使用されます。このフィールドでは、ベクトルのディメンション、類似度計算方法、およびコアインデックス方法を構成できます。選択ガイド
HNSW と IVF は、パフォーマンス、リソース消費、および精度において異なる強みを持ち、それぞれ異なるビジネスシナリオに適しています。迅速な選択のために、次の表を参照できます。
比較
HNSW
IVF
クエリレイテンシー
極めて低い。HNSW は、短い検索パスを持つ階層的なグラフ構造を通じて結果を迅速に特定します。
低い。IVF は、まずクラスターを特定し、その中で検索する必要があるため、比較的長いパスになります。
取得率 (精度)
高い。グラフの接続性が優れているため、最近傍を見逃す可能性が低くなります。
中から高。クエリポイントがクラスターの境界にあるエッジ効果により、精度が若干失われる可能性があります。これは
nprobesパラメーターを調整することで軽減できます。メモリ使用量
高い。完全なグラフ構造をメモリにロードする必要があります。
低い。IVF は主に重心と転置インデックスを保存します。メモリオーバーヘッドは HNSW よりもはるかに低いです。
構築時間
より長い。高品質なグラフ構造を構築するには、複雑な計算が必要です。
より速い。ただし、IVF は重心を生成するために追加のトレーニングステップが必要です。
シナリオ
クエリパフォーマンスと精度に極端な要件があり、十分なメモリリソースがあるシナリオ。例としては、リアルタイムのセマンティック検索や顔認識などがあります。
大規模なデータセット、限られたメモリリソース、およびわずかな精度低下を許容できるコスト重視のシナリオ。例としては、大規模な製品推奨や大規模な画像ギャラリーの取得などがあります。
使用例
HNSW
HNSW は IndexHNSWFlat を通じて実装され、パフォーマンスと取得率に高い要件があるシナリオに適しています。
コアパラメーター
パラメーター | 値の範囲 | 説明 |
| 正の整数。 | グラフ内の各ノードの最大近傍数 (出次数)。この値はグラフの密度を決定し、インデックスの品質とメモリ使用量に影響を与える最も重要なパラメーターです。
|
| 正の整数で、通常は | インデックス構築中の動的近傍リストのサイズ。グラフ構築中の検索の深さと幅を制御します。この値は主にインデックスの構築時間と最終的な品質に影響します。
|
| 正の整数。 | クエリ中の動的近傍リストのサイズ。クエリ中の検索の深さを制御します。 説明 このパラメーターはインデックス作成時には指定されず、インデックスの
|
HNSW インデックスを作成する際、<my-index> をインデックス名に、<my_vector_field> をフィールド名に置き換えてください。また、dimension、data_type、space_type、m、ef_construction などの他のコアパラメーターも必要に応じて構成する必要があります。
REST API
// HNSW インデックス作成の例。<my-index> をインデックス名に置き換えます。
PUT /<my-index>
{
"settings": {
"index": {
"knn": true
}
},
"mappings": {
"properties": {
"<my_vector_field>": {// <my_vector_field> をフィールド名に置き換えます。
"type": "knn_vector",
"dimension": 128,
"data_type": "float",
"method": {
"name": "hnsw",
"engine": "faiss",
"space_type": "l2",
"parameters": {
"m": 16,
"ef_construction": 256
}
}
}
}
}
}Java クライアント
private static void createVectorIndex(OpenSearchClient client) throws IOException {
Property vectorProperty = Property.of(p -> p.knnVector(
KnnVectorProperty.of(kvp -> kvp
.dimension(128)
.dataType("float")
.method(new KnnVectorMethod.Builder()
.name("hnsw")
.engine("faiss")
.spaceType("l2")
.parameters(Map.of(
"m", JsonData.of(16),
"ef_construction", JsonData.of(256)
))
.build()
)
)
));
TypeMapping mapping = TypeMapping.of(m -> m
.properties("<my_vector_field>", vectorProperty)
.properties("text", Property.of(p -> p.text(TextProperty.of(t -> t))))
.properties("category", Property.of(p -> p.keyword(k -> k)))
);
CreateIndexRequest request = new CreateIndexRequest.Builder()
.index(<my-index>)
.settings(s -> s.knn(true))
.mappings(mapping)
.build();
client.indices().create(request);
}IVF
IVF は IndexIVFFlat を通じて実装され、超大規模なデータセットと限られたメモリを持つシナリオに適しています。
コアパラメーター
パラメーター | 値の範囲 | 説明 |
| 正の整数。 | 重心の数。インデックスはベクトル空間全体を
|
| 正の整数で、通常は | クエリ中に検索する重心 (クラスター) の数。これは、クエリ速度と取得率の間のトレードオフを調整するための最も直接的なパラメーターです。
|
IVF インデックスを作成する際、<my-index> をインデックス名に、<my_vector_field> をフィールド名に置き換えてください。また、dimension、data_type、space_type、nlist、nprobes などの他のコアパラメーターも必要に応じて構成する必要があります。
// IVF インデックス作成の例。<my-index> をインデックス名に置き換えます。
PUT /<my-index>
{
"settings": {
"index": {
"knn": true
}
},
"mappings": {
"properties": {
"<my_vector_field>": {// <my_vector_field> をフィールド名に置き換えます。
"type": "knn_vector",
"dimension": 4,
"data_type": "byte",
"method": {
"name": "ivf",
"engine": "faiss",
"space_type": "l2",
"parameters": {
"nlist": 1024,
"nprobes": 10 // nprobes は通常クエリ時に指定されます。これは単なる例です。
}
}
}
}
}
}ステップ 2: ベクトルデータのインデックス作成
ベクトルデータやその他のメタデータを含むドキュメントを準備し、作成したインデックスにそれらをインデックスします。
REST API
POST /_bulk
{ "index": { "_index": "my-index", "_id": "doc_1" } }
{ "my_vector_field": [5.2, 4.4] }
{ "index": { "_index": "my-index", "_id": "doc_2" } }
{ "my_vector_field": [5.2, 3.9] }
{ "index": { "_index": "my-index", "_id": "doc_3" } }
{ "my_vector_field": [4.9, 3.4] }
{ "index": { "_index": "my-index", "_id": "doc_4" } }
{ "my_vector_field": [4.2, 4.6] }
{ "index": { "_index": "my-index", "_id": "doc_5" } }
{ "my_vector_field": [3.3, 4.5] }Java クライアント
private static void indexSampleData(OpenSearchClient client) throws IOException {
List<Map<String, Object>> documents = new ArrayList<>();
documents.add(Map.of("text", "a book about data science", "category", "books", "<my_vector_field>", List.of(1.0f, 2.0f, 3.0f, 4.0f)));
documents.add(Map.of("text", "an intelligent smartphone with a great camera", "category", "electronics", "<my_vector_field>", List.of(8.0f, 7.0f, 6.0f, 5.0f)));
documents.add(Map.of("text", "a technical manual for a smart device", "category", "electronics", "<my_vector_field>", List.of(3.0f, 4.0f, 5.0f, 6.0f)));
for (int i = 0; i < documents.size(); i++) {
IndexRequest<Map<String, Object>> request = new IndexRequest.Builder<Map<String, Object>>()
.index(<my-index>)
.id("doc_" + i)
.document(documents.get(i))
.build();
client.index(request);
}
}ステップ 3: ベクトル検索の実行
これで、ベクトル検索リクエストを送信して、大規模なデータセットからクエリベクトルに最も類似した結果を見つけることができます。
基本的な k-NN 検索
これは最も基本的なベクトル検索です。インデックス全体でクエリベクトルに最も近い k 個の結果を見つけます。
REST API
POST /<my-index>/_search
{
"size": 3,
"query": {
"knn": {
"<my_vector_field>": {
"vector": [3.1, 4.1, 5.1, 6.1],
"k": 3
}
}
}
}Java クライアント
// クエリベクトルを準備します。
List<Float> queryVector = List.of(3.1f, 4.1f, 5.1f, 6.1f);
private static void performBasicKnnSearch(OpenSearchClient client, List<Float> queryVector) throws IOException {
System.out.println("\n--- 1. Performing Basic k-NN Search ---");
System.out.println("Querying for vectors most similar to: " + queryVector);
// 最も類似した 3 つの結果を検索します。
int k = 3;
KnnQuery knnQuery = new KnnQuery.Builder()
.field("<my_vector_field>")
.vector(queryVector)
.k(k)
.build();
SearchRequest searchRequest = new SearchRequest.Builder().index(<my-index>).query(new Query.Builder().knn(knnQuery).build()).size(k).build();
SearchResponse<Map> response = client.search(searchRequest, Map.class);
printResults(response);
}フィルター条件付き k-NN 検索 (ハイブリッド検索)
多くのシナリオでは、ベクトル検索を実行する前に、1 つ以上の条件で検索範囲を絞り込む必要がある場合があります。これがハイブリッド検索の中心的な考え方です。これを行うには、KnnQuery の filter 句を使用できます。フィルター自体は、完全一致のための term やフルテキストインデックスのための match など、任意の標準 OpenSearch クエリにすることができます。
テキスト一致を使用したフィルター
これは、古典的な「キーワード + ベクトル」のハイブリッド検索シナリオに適しています。たとえば、まず説明に「新しいスマートフォン」を含むすべてのドキュメントを検索し、次にそれらをベクトル類似度でソートすることができます。
REST API
POST /<my-index>/_search
{
"size": 3,
"query": {
"knn": {
"<my_vector_field>": {
"vector": [3.1, 4.1, 5.1, 6.1],
"k": 3,
"filter": {
"match": {
"text": "book"
}
}
}
}
}
}Java クライアント
// クエリベクトルを準備します。
List<Float> queryVector = List.of(3.1f, 4.1f, 5.1f, 6.1f);
private static void performHybridSearchWithText(OpenSearchClient client, List<Float> queryVector) throws IOException {
System.out.println("\n--- 2. Performing Hybrid Search (k-NN + Text Match) ---");
// クエリキーワードを準備します。
String textQuery = "book";
System.out.println("Filtering for documents containing '" + textQuery + "', then finding most similar vectors.");
// 最も類似した 3 つの結果を検索します。
int k = 3;
MatchQuery matchQuery = new MatchQuery.Builder().field("text").query(q -> q.stringValue(textQuery)).build();
KnnQuery hybridKnnQuery = new KnnQuery.Builder()
.field("<my_vector_field>")
.vector(queryVector)
.k(k)
.filter(new Query.Builder().match(matchQuery).build())
.build();
SearchRequest searchRequest = new SearchRequest.Builder().index(<my-index>).query(new Query.Builder().knn(hybridKnnQuery).build()).size(k).build();
SearchResponse<Map> response = client.search(searchRequest, Map.class);
printResults(response);
}完全一致値を使用したフィルター (Term フィルター)
これは、特定のラベル、カテゴリ、または ID でフィルターをかけるシナリオに適しています。たとえば、「electronics」カテゴリ内でのみ最も類似した製品を検索することができます。
REST API
POST /<my-index>/_search
{
"size": 3,
"query": {
"knn": {
"<my_vector_field>": {
"vector": [5, 4],
"k": 3,
"filter": {
"term": {
"category": "electronics"
}
}
}
}
}
}Java クライアント
// クエリベクトルを準備します。
List<Float> queryVector = List.of(3.1f, 4.1f, 5.1f, 6.1f);
private static void performFilteredSearchWithTerm(OpenSearchClient client, List<Float> queryVector) throws IOException {
System.out.println("\n--- 3. Performing Filtered Search (k-NN + Term Filter) ---");
// クエリカテゴリを準備します。
String categoryFilter = "electronics";
System.out.println("Filtering for documents in category '" + categoryFilter + "', then finding most similar vectors.");
// 最も類似した 3 つの結果を検索します。
int k = 3;
TermQuery termQuery = new TermQuery.Builder().field("category").value(v -> v.stringValue(categoryFilter)).build();
KnnQuery filteredKnnQuery = new KnnQuery.Builder()
.field("<my_vector_field>")
.vector(queryVector)
.k(k)
.filter(new Query.Builder().term(termQuery).build())
.build();
SearchRequest searchRequest = new SearchRequest.Builder().index(<my-index>).query(new Query.Builder().knn(filteredKnnQuery).build()).size(k).build();
SearchResponse<Map> response = client.search(searchRequest, Map.class);
printResults(response);
}ストレージ最適化の設定
ベクトルデータ、特に高次元の浮動小数点数ベクトルは、大量のメモリを消費します。PolarSearch は、複数のストレージ最適化技術を提供します。これらの技術は、量子化によるベクトルの圧縮や記憶媒体の変更を通じて、メモリコスト、クエリパフォーマンス、検索精度のバランスを取ります。
推奨事項
次の表を参照して、ビジネスシナリオに最適な最適化ポリシーをすばやく見つけることができます。
最適化ポリシー | 圧縮率 | 精度への影響 | トレーニング要件 | CPU オーバーヘッド | シナリオ |
スカラー量子化 (SQ) | 低 (2 倍に固定) | 最小限 | トレーニング不要 | 低 | 検索精度が非常に高く、精度への影響を最小限に抑えつつ、中程度のメモリ最適化が必要なシナリオで使用します。 |
バイナリ量子化 (BQ) | 高 (8 倍から 32 倍) | 大きい | トレーニング不要 | 中 | メモリを最大限に節約するために、ある程度の精度の低下を許容できるメモリに敏感なシナリオで使用します。 |
積量子化 (PQ) | 最高 | 中 | トレーニングが必要 | 中 | 極端な圧縮率を必要とする大規模なデータセットに使用します。これは、精度とメモリのバランスを取るためにモデルのトレーニングに時間を費やすことを厭わないシナリオ向けです。 |
ディスクベースのベクトルストレージ | - | 大きい | トレーニング不要 | 高 | メモリが極端に制限されているコストに敏感なシナリオで使用します。これらのケースでは、ディスク I/O の影響を受けるクエリのレイテンシよりもメモリ使用量の最小化を優先します。 |
手順
スカラー量子化 (SQ)
仕組み: このメソッドは、標準の 32 ビット浮動小数点 (float) ベクトルを 16 ビット浮動小数点 (fp16) ベクトルに変換してストレージに保存し、メモリ使用量を半分に削減します。距離計算中に、ベクトルは 32 ビットにデコードして戻されるため、精度への影響は最小限です。
メモリ推定:
数式:
Memory (GB) ≈ 1.1 × (2 × dimension + 8 × m) × num_vectors / 1024³パラメーターの説明:
dimension: ベクトルのディメンション。m: Hierarchical Navigable Small World (HNSW) インデックスのmパラメーター。各ノードの最大近傍数を指定します。num_vectors: ベクトルの総数。1.1: 約 10% のシステムオーバーヘッドの係数。
例: 100 万個のベクトルがあり、各ベクトルのディメンションが 256 で、
mパラメーターが 16 であると仮定します。メモリ要件は次のように推定されます:1.1 × (2 × 256 + 8 × 16) × 1,000,000 ≈ 0.656 GB
例:
// HNSW + スカラー量子化 (SQ) の例 PUT /<my-sq-index> { "settings": { "index": { "knn": true } }, "mappings": { "properties": { "<my_vector_field>": { "type": "knn_vector", "dimension": 128, "method": { "name": "hnsw", "engine": "faiss", "parameters": { "m": 16, "ef_construction": 256, "encoder": {// SQ を有効化 "name": "fp16" } } } } } } }
バイナリ量子化 (BQ)
仕組み: このメソッドは、浮動小数点ベクトルの各ディメンションをバイナリビット (0 と 1) に圧縮してストレージに保存し、非常に高い圧縮率を実現します。トレーニングプロセスは、インデックスの構築時に自動的に完了します。
メモリ推定:
数式:
Memory (GB) ≈ 1.1 × ((dimension × bits / 8) + 8 × m) × num_vectors / 1024³パラメーターの説明:
dimension: ベクトルのディメンション。bits: 各ディメンションを表すために使用されるバイナリビットの数。有効な値は 1、2、4 です。bitsの値が小さいほど、圧縮率は高くなりますが、精度の低下は大きくなります。m: HNSW インデックスのmパラメーター。num_vectors: ベクトルの総数。
例: 100 万個のベクトルがあり、各ベクトルのディメンションが 256 で、
mパラメーターが 16 であると仮定します。以下のセクションでは、異なる圧縮値に対するメモリ要件の推定値を示します。1 ビット量子化 (32 倍圧縮): 1 ビット量子化では、各ディメンションは 1 ビットで表され、これは 32 倍の圧縮率に相当します。メモリ要件は次のように推定されます:
1.1 × ((256 × 1 / 8) + 8 × 16) × 1,000,000 ≈ 0.176 GB2 ビット量子化 (16 倍圧縮): 2 ビット量子化では、各ディメンションは 2 ビットで表され、これは 16 倍の圧縮率に相当します。メモリ要件は次のように推定されます:
1.1 × ((256 × 2 / 8) + 8 × 16) × 1,000,000 ≈ 0.211 GB
例:
// HNSW + バイナリ量子化 (BQ) の例 PUT /<my-bq-index> { "settings" : { "index": { "knn": true } }, "mappings": { "properties": { "<my_vector_field>": { "type": "knn_vector", "dimension": 128, "method": { "name": "hnsw", "engine": "faiss", "parameters": { "m": 16, "ef_construction": 512, "encoder": { "name": "binary", "parameters": {// BQ を有効化、1 ビット量子化を使用 "bits": 1 } } } } } } } }
積量子化 (PQ)
積量子化 (PQ) は、高度なベクトル圧縮技術です。SQ や BQ よりも高い圧縮率を実現しますが、圧縮モデルを構築するために別途トレーニングステップが必要です。
仕組み:
ベクトルのチャンク化: まず、256 ディメンションのベクトルなどの元の高次元ベクトルを、等しい長さの
m個の低次元サブベクトルにチャンク化します。たとえば、256 ディメンションのベクトルをm=32でチャンク化すると、32 個の 8 ディメンションのサブベクトルが生成されます。コードブックのトレーニング: 次に、システムは各サブベクトル空間に対して個別のコードブックを学習します。このコードブックには
2^code_size個の重心が含まれます。このトレーニングプロセスは通常、k-means クラスタリングアルゴリズムを使用して実行されます。量子化エンコーディング: トレーニング後、新しいベクトルがエンコードされると、その各サブベクトルは置き換えられます。元の浮動小数点値を格納する代わりに、システムはサブベクトルのコードブック内で最も近い重心の ID を格納します。
code_sizeが 8 の場合、ID の範囲は 0 から 255 であり、これは正確に 1 バイトで格納できます。最終結果: 元のベクトルは重心 ID のシーケンスに変換されます。このメソッドは非常に高い圧縮率を実現します。
トレーニング要件: PQ のパフォーマンスは、トレーニングデータの品質に大きく依存します。最終的に取得するデータと類似したデータ分布を持つトレーニング用のベクトルセットを提供する必要があります。
トレーニングデータソース: トレーニングデータは、インデックスを作成する予定のベクトルデータのサブセットにすることができます。
推奨されるトレーニングデータ量:
HNSW と併用する場合: 推奨されるトレーニングベクトル数は
2^code_size × 1,000です。転置ファイル (IVF) と併用する場合: 推奨されるトレーニングベクトル数は
max(1,000 × nlist, 2^code_size × 1,000)です。
メモリ推定: HNSW+PQ を例にとります。HNSW と PQ を組み合わせると、圧縮されたベクトル、HNSW グラフ構造、および PQ コードブックのオーバーヘッドが含まれるため、メモリ計算式は複雑になります。
数式:
Memory (bytes) ≈ 1.1 × ( (per_vector_cost) × num_vectors + (codebook_cost) )per_vector_cost = (pq_code_size / 8 × pq_m) + 24 + (8 × hnsw_m)codebook_cost = num_segments × (2^pq_code_size) × 4 × dimension
パラメーターの説明:
num_vectors: ベクトルの総数。dimension: 元のベクトルのディメンション。pq_m: ベクトルがチャンク化されるセグメントの数。dimensionはpq_mで割り切れる必要があります。pq_code_size: 各サブベクトルコードブックのサイズ (ビット単位)。一般的な値は 8 です。hnsw_m: HNSW インデックスのmパラメーター。各ノードの最大近傍数です。num_segments: インデックスが分割されるセグメントの数を表す低レベルの技術的パラメーター。推定には、クラスターシャードの数や、100 などの保守的な値を使用できます。1.1: 約 10% のシステムオーバーヘッドの係数。24と8: HNSW グラフ構造における各ノードの固定オーバーヘッドとポインターオーバーヘッド。4: コードブック内の重心座標が 4 バイトの 32 ビット浮動小数点数として格納されることを表します。
例: 100 万個のベクトル (
num_vectors) があると仮定します。各ベクトルのディメンション (dimension) は 256 です。各ベクトルのチャンク数 (pq_m) は 32 です。各サブベクトルコードブックのサイズ (pq_code_size) は 8 です。HNSW インデックスのmパラメーターは 16 で、num_segmentsは 100 です。単一ベクトルのオーバーヘッド (
per_vector_cost) を計算します:圧縮ベクトルサイズ = pq_code_size / 8 × pq_m = 8 / 8 × 32 = 32 バイト。
HNSW グラフオーバーヘッド = 24 + 8 × hnsw_m = 24 + 8 × 16 = 152 バイト。
per_vector_cost = 32 + 152 = 184 バイト
合計コードブックオーバーヘッド (
codebook_cost) を計算します:codebook_cost = num_segments × (2^pq_code_size) × 4 × dimension.
codebook_cost = 100 × (2^8) × 4 × 256 = 100 × 256 × 4 × 256 = 26,214,400 バイト。
合計メモリを計算します:
合計メモリ ≈ 1.1 × (per_vector_cost × num_vectors + codebook_cost)
合計メモリ ≈ 1.1 × (184 × 1,000,000 + 26,214,400) ≈ 231,235,840 バイト ≈ 0.215 GB
例:
// HNSW + 積量子化 (PQ) の例 PUT /<my-hnswpq-index> { "settings" : { "index": { "knn": true } }, "mappings": { "properties": { "<my_vector_field>": { "type": "knn_vector", "dimension": 128, // ディメンションは m で割り切れる必要があります "method": { "name": "hnsw", "engine": "faiss", "parameters": { "m": 16, // HNSW の m パラメーター "ef_construction": 512, "encoder": { "name": "pq", "parameters": { "m": 4, // PQ の m パラメーター: 128 ディメンションのベクトルを 32 ディメンションの 4 つのセグメントにチャンク化 "code_size": 8 } } } } } } } }
ディスクベースのベクトルストレージ
仕組み: ディスクベースのベクトル検索は、内部量子化技術を使用してベクトルを圧縮し、主要なグラフ構造をヒープメモリではなくディスクに保存します。このメモリ最適化により、メモリを大幅に節約できますが、検索のレイテンシはわずかに増加します。高い取得率は維持されます。
メモリ推定: 固定された数式はありません。実際の物理メモリ使用量は、アクセスモードに基づいてオペレーティングシステムによって動的に管理されます。
例:
// ディスクベースのストレージの例 PUT /<my-ondisk-index> { "settings" : { "index": { "knn": true } }, "mappings": { "properties": { "<my_vector_field>": { "type": "knn_vector", "dimension": 128, "mode": "on_disk" // ディスクベースモードを有効化 } } } }
付録: 完全なサンプルコード
このセクションでは、Java クライアント の完全なサンプルコードを提供します。このコードは、ベクトルインデックスの作成からベクトル検索の実行までの全手順を示しています。
仕組み
mvn clean package
mvn exec:java -Dexec.mainClass="com.example.VectorSearchDemo"