[翻訳] バイナリベクトルで OpenSearch のコストを削減する
数億の高次元ベクトルを一瞬で検索でき、しかもこれまでよりも少ないストレージとメモリで実現できるとしたらどうでしょうか。これは不可能に聞こえるかもしれませんが、バイナリベクトル (OpenSearch の大規模ベクトル検索における最新の進歩) を使えば現実のものとなります。データが爆発的に増加している世界では、レコメンデーションシステムや高度な検索エンジンを構築する場合でも、メモリを削減しながら大規模なデータセットを処理することが重要です。本記事では、バイナリベクトルが従来の FP32 ベクトルと比較してどのようなパフォーマンスを発揮するかを探ります。特に、ランダムに生成された 768 次元、1 億ベクトルのデータセットを使用して、ストレージ、メモリ使用量、検索速度、そしてバイナリベクトルがベクトル検索へのアプローチをどのように変える可能性があるかを見ていきます。
FP32 ベクトルとバイナリベクトルの違い
FP32 ベクトルは、その高い精度と、通常浮動小数点形式でベクトルを生成する多くの大規模言語モデル (LLM) とのシームレスな統合により、長い間ベクトル検索の標準でした。しかし、この精度にはストレージとメモリの増加という代償が伴います。データニーズが増加するにつれて、このトレードオフを正当化することが難しくなる場合があります。対照的に、バイナリベクトルは以下の画像に示すように 1 と 0 のみを使用します。

このバイナリ形式により、バイナリベクトルはよりコンパクトで処理が高速になります。LLM は大規模データセットでの効率を向上させるために、バイナリ埋め込みを生成するケースが増えており、ストレージ、メモリ、レイテンシの大幅な削減を実現しています。
OpenSearch でのバイナリベクトルの使用
OpenSearch ソリューションでバイナリベクトルを使用する方法を見ていきましょう。
データ準備
まず、バイナリベクトルデータが必要です。幸いなことに、多くのモデルがバイナリ形式で埋め込みを生成するようになっています。例えば、Cohere Embed v3 モデルはバイナリベクトルを生成します。
バイナリベクトルは [0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0] のような 1 と 0 の配列です。ただし、OpenSearch ではバイナリベクトルを int8 バイト形式にパックする必要があります。例えば、上記のビット配列は以下の画像に示すように [108, -116] に変換されます。

多くの埋め込みモデルはすでに int8 バイト形式でバイナリベクトルを生成するため、追加のパッキングは通常不要です。ただし、データがビット配列として保存されている場合は、numpy ライブラリを使用して簡単にバイト配列に変換できます。
import numpy as np
bit_array = [0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0]
bit_array_np = np.array(bit_array, dtype=np.uint8)
byte_array = np.packbits(bit_array_np).astype(np.int8).tolist()
取り込みと検索
データがバイト配列として保存されたら、それを OpenSearch に取り込む必要があります。
まず、インデックスマッピングでデータ型を binary に設定し、ベクトルの次元が 8 の倍数であることを確認します (そうでない場合は、ベクトルをゼロでパディングします)。
PUT /test-binary-hnsw
{
"settings": {
"index": {
"knn": true
}
},
"mappings": {
"properties": {
"my_vector": {
"type": "knn_vector",
"dimension": 16,
"data_type": "binary",
"space_type": "hamming",
"method": {
"name": "hnsw",
"engine": "faiss"
}
}
}
}
}
OpenSearch のバイナリベクトルは、インデックス作成と検索にハミング距離を使用します。
次に、インデックス作成と検索の両方でバイナリベクトルをバイト形式にパックします。それ以外は、バイナリベクトルの使用は FP32 ベクトルの操作と同様です。以下の例では、ベクトル値 [0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0] と [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1] を持つ 2 つのドキュメントをインデックスします。
PUT _bulk
{"index": {"_index": "test-binary-hnsw", "_id": "1"}}
{"my_vector": [7, 8]}
{"index": {"_index": "test-binary-hnsw", "_id": "2"}}
{"my_vector": [10, 11]}
最後に、クエリベクトル [0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0] に最も近いベクトルを検索します。
GET /test-binary-hnsw/_search
{
"size": 1,
"query": {
"knn": {
"my_vector": {
"vector": [108, -116],
"k": 1
}
}
}
}
パフォーマンス比較
バイナリベクトルを使用することで実現されるリソース削減を見てみましょう。ベンチマークテストでは、バイナリベクトル用に 8 倍性能の低いハードウェアを使用しても、FP32 ベクトルとバイナリベクトル間で同様の取り込み速度とクエリ時間が観察されました。

クラスター設定
ベンチマークでは、768 次元のランダム生成された 1 億ベクトルデータセットを使用し、FP32 ベクトルとバイナリベクトルを比較しました。クラスターはデータノードを除いて同一でした。バイナリベクトルは 2 倍小さく 4 倍少ないノードを使用し、86% のコスト削減を実現しました。以下の表はベンチマーク設定の概要です。

パフォーマンス結果
8 倍小さいハードウェアでも、バイナリベクトルはより強力なマシン上の FP32 ベクトルと同等のインデックス作成速度とクエリ時間を実現しました。バイナリベクトルでは、メモリ使用量が 92% 削減され、ストレージが 97% 削減され、大幅な節約が実現しました。結果は以下の表に示されています。

精度
再現率に関しては、完全検索と比較して約 0.97 の再現率が期待できます。OpenSearch は近似最近傍検索に HNSW アルゴリズムを使用しますが、結果の精度はデータセットに依存します。一部のモデルは高精度のバイナリベクトルを生成します。例えば、Cohere Embed v3 は FP32 埋め込みと比較して 94.7% の検索品質の一致を報告しています。したがって、高品質のバイナリ埋め込みを生成するモデルを使用する場合、バイナリベクトルは FP32 ベクトルとほぼ同じ精度に達することができます。
バイナリベクトルの課題: 不十分な場合
モデルが FP32 ベクトルのみを生成するが、OpenSearch でバイナリベクトルを使用したい場合、プロセスは少し複雑になります。以下の例は、FP32 ベクトルを使用して OpenSearch でバイナリベクトル検索を使用する方法と、関連する課題を示しています。
この例では、Hugging Face の Cohere Simple データセットを使用しました。データが FP32 形式だったため、ゼロと負の値を 0 に、正の値を 1 に設定してバイナリ形式に変換しました。以下の画像は変換プロセスを示しています。

再現率に関しては、バイナリベクトルは 0.73196 のスコアを達成しました。0.93865 の再現率に達するには、3 倍のオーバーサンプリングが必要でした。さらに、リスコアリングには元のベクトル形式の保存が必要であり、元のベクトルが OpenSearch の外部に保存されていない限り、ディスク使用量が増加します。以下の画像はリスコアリングを伴うオーバーサンプリングを示しています。再現率はデータセットによって異なる場合があることに注意してください。

量子化、オーバーサンプリング、リスコアリングにより、バイナリベクトルは大幅に少ないメモリを使用しながら FP32 ベクトルと同様の再現率を達成できます。ただし、これらのステップを OpenSearch の外部で管理するのは面倒な場合があります。ディスクベースベクトル検索は、必要なすべてのステップに高度な量子化技術を自動的に使用することでプロセスを簡素化します。ぜひお試しください。
まとめ
バイナリベクトルは FP32 ベクトルの効率的な代替手段を提供し、小型のハードウェアで強力なパフォーマンスを維持しながら、メモリとストレージの使用量を 90% 以上削減します。この効率性により、バイナリベクトルは速度とリソース節約が重要なレコメンデーションシステムや検索エンジンなどの大規模ベクトル検索アプリケーションに最適です。大規模なデータセットを扱っている場合、バイナリベクトルはコストを増やすことなく検索機能をスケールする実用的な方法を提供します。
次のステップ
バイナリベクトルサポートは OpenSearch 2.16 以降で利用可能です。詳細な手順については、OpenSearch のバイナリベクトルドキュメントをご覧ください。
浮動小数点ベクトルを使用している場合、ディスクベースベクトル検索は再現率を失うことなくバイナリベクトル検索のメモリ効率を提供します。バイナリ量子化、オーバーサンプリング、リスコアリングを自動的に実行し、バイナリベクトル検索の低メモリ使用量を維持します。
ぜひバイナリベクトルをお試しいただき、ディスクベースベクトル検索に関する今後のブログ記事にご期待ください。
OpenSearch Project(OSS) の Publicationです。 OpenSearch Tokyo User Group : meetup.com/opensearch-project-tokyo/
Discussion