
イントロダクション
LLM(大規模言語モデル)をきっかけに、機械学習、セマンティック検索、生成AIアプリケーション向けのベクトル検索への関心が爆発的に高まっています。ベクトルデータベースの市場規模は、 2022年以前はほぼゼロだったのが、 2026年には約30億ドルに達すると予測されています。ベクトル検索の初期の実装では、機能性に重点が置かれ、ベクトル要素の表現には標準的な32ビット(4バイト)浮動小数点型が使用されていました。
ベクトルデータベースにデータが追加されるにつれて、コスト、特にメモリ使用量が懸念事項となりました。ベクトル類似性チェックは、ベクトル要素の精度にそれほど敏感ではないことが分かりました。この特性により、32ビット浮動小数点ベクトル要素の代わりに16ビット浮動小数点ベクトル要素を使用することが可能になります。さらに、IntelとARMの命令セットは、2022~2023年頃からネイティブ演算で16ビット浮動小数点をサポートしています。実際、Intelハードウェアの高速変換関数(F16C)は2012年(Ivy Bridge)にまで遡ります。
ベクトル演算の精度低下に対する許容度と、16ビット浮動小数点演算に対するハードウェアサポートの組み合わせにより、SingleStoreはバージョン9.1において、ベクトル検索の 計算コストとストレージコストを大幅に削減しつつ、同等のリコール性能を維持することに成功しました。
バージョン9.1では、SingleStoreによりストレージコストがほぼ半減し、コンピューティングコストが約40%削減されました。しかも、リコール性能はほぼ同等です!
16ビット浮動小数点命令を活用した新しいデータ型VECTOR(<N>, F16)は、これらの利点を提供します。2024年以降、他のいくつかのベクトル検索対応データベース製品もfloat16のサポートを追加しましたが、注目すべき製品の中にはまだサポートしていないものもあります。
SingleStore でのベクトル検索を VECTOR(<N>, F16) を使用してより安価に、より速く、そしてより優れたものにした方法をお伝えできることを嬉しく思います。
より安価に
テキスト埋め込みモデルを含むベクトル埋め込みを扱う場合、関連する数値について理解しておくことが重要です。最新の埋め込みモデルは通常、32ビット浮動小数点形式または単精度でベクトルを出力します。SingleStoreでは、32ビット浮動小数点(F32)形式と16ビット浮動小数点(F16)形式の両方でベクトルを保存およびインデックス付けできるようになりました。
F32(32ビット浮動小数点数)は、約±1.2×10⁻³⁸から±3.4×10³⁸までという、非常に広い範囲の値を表現できます。これは、極めて小さな数から天文学的に大きな数までを網羅しています。また、約7桁の精度を提供します。
F16(16ビット浮動小数点数)は、「半精度」とも呼ばれ、±6.1 × 10⁻⁵から±6.5 × 10⁴という、より小さいながらも十分な値の範囲をカバーします。F32と比較すると、F16は精度が低く、小数点以下3~4桁程度ですが、メモリ使用量は半分で済み、処理速度も速くなります。
F32とF16の浮動小数点表現の主な違いは、F32の方がより広い範囲の値を格納でき、精度が高い点です。
ベクトル埋め込みでは、F32やF16の理論的な限界に近い値を見ることはほとんどありません。ほとんどの埋め込みモデルは、出力ベクトルを単位長(長さ=1)に正規化します。正規化とは、原点から任意の埋め込みベクトルまでのユークリッド距離を計算すると、値が1になることを意味します。
この正規化により、埋め込みベクトルの個々の成分は、-1 から 1 というより狭い範囲に収まります。実際には、埋め込みの次元数によっては、0 にさらに近い値になることがよくあります。次元数が多いほど、同じ総「エネルギー」(単位長さ)がより多くの次元に分散されるため、個々の成分の典型的な大きさは小さくなります。F32 ベクトルで可能な値の全範囲は、通常、ベクトル埋め込みには必要ありません。
さらに、F16はF32よりも精度が低いものの、多くのベクトル検索アプリケーションでは許容範囲内です。セマンティック検索や生成AI検索システムなどのベクトル検索アプリケーションでは、正確な距離は必要なく、正しい順位付けのみが必要です。そのため、精度が多少低下しても、ベクトル検索は正しい順序を維持します。また、埋め込みベクトル自体も高精度ではなく、ノイズや精度低下に対して高い耐性を持っています。
http://corpus-texmex.irisa.fr/の GIST 1M データセットを使用して、SingleStore テーブルにおける F16 ベクトルと F32 ベクトルの使用ストレージを比較しました。このデータセットには、次元 960 のベクトルが 100 万個 含まれています。予想どおり、F16 は F32 のストレージの約 50% を使用しています。
ベクトルタイプ | ストレージサイズ |
F16 | 1.79GB |
F32 | 3.58GB |
より速く
F16 ベクトルは、 DOT_PRODUCT (正規化されたベクトルのコサイン類似度を計算する) や EUCLIDEAN_DISTANCEなどの操作において、F32 ベクトルと比較して検索とインデックス作成のパフォーマンスが高速です。
ストレージテストと同様に、パフォーマンス テストも GIST 1M データセットを使用して実施しました。これらのベクトルは 2 つのテーブルに格納されました。1 つはVECTOR(960, F16) の列、もう 1 つはVECTOR(960, F32)の列です。F16 ベクトルは、SingleStore の組み込み変換を使用して F32 の列からキャストすることで取得されました。
kNN 検索
結果によると、 DOT_PRODUCTF16ベクトルを使用した正確なkNN(k近傍法)検索は、F32を使用した場合よりも38%高速であり、EUCLIDEAN_DISTANCEを使用した同様のクエリは、F16を使用した場合の方がF32を使用した場合よりも37%高速であることが示されています。
オペレーション | F16 | F32 | 向上率 |
| 491.8 ms | 794.6 ms | 38.1% |
| 500.6 ms | 797.6 ms | 37.2% |
F16ベクトルに対するDOT_PRODUCT パフォーマンスをテストするために使用したクエリを以下に示します。@vec_f16 はクエリベクトルを保持する変数です。F32およびF32に対するクエリもEUCLIDEAN_DISTANCE同様であり、方法論のセクションに記載されています。
1SELECT DOT_PRODUCT (f16_col, @vec_f16) AS score 2FROM t_f16 3ORDER BY score DESC LIMIT 10;
ANN インデックス検索
ANNインデックスは、F16ベクトルとF32ベクトルの両方でサポートされています。結果によると、インデックスの構築時間と検索時間はF16ベクトルとF32ベクトルでほぼ同じです。ベクトルの保存容量を節約しながら、検索パフォーマンスを維持、あるいは向上させることができます。
IVF_PQFSとHNSW_FLATインデックスを比較したのは、これらが推奨するインデックスだからです。IVF_PQFSは積量子化(PQ)を使用してストレージ容量を削減し、より小さなインデックスを生成します。HNSW_FLATはベクトルを圧縮せず、高い精度(再現率)を持ちますが、より大きなインデックスを生成します。
SingleStoreはFaiss ライブラリを使用していますが、FaissライブラリはF16ハードウェア命令をサポートしていません。そのため、インデックス構築時にベクトルはF32にアップキャストされ、インデックスにはF32ベクトルが格納されます。ベクトルインデックスのサイズは、ベクトルインデックス構築時にF16ベ クトルがF32ベクトルに変換されるため、F16とF32で同じです。IVF_PQFSインデックスの場合、積量子化を使用することでストレージ容量を大幅に削減できます。
インデックス検索
検索時間は以下のとおりです。F16でのインデックス検索は若干高速ですが、これはF16ベクトルのメモリ使用量が小さいため、ワーキングセットのより多くの部分がキャッシュに収まり、キャッシュの利用効率が向上するためと考えられます。
F16 | F32 | 向上率 | |
| 33.25 ms | 34.73 ms | 4.3% |
| 30.71 ms | 33.50 ms | 8.3% |
F16 ANNの検索時間をテストするために使用したクエリを以下に示します。@qvecクエリベクトルが含まれています。F32のクエリとインデックス作成コマンドについては、「方法論」セクションに記載されています。
1SET @qvec = UNHEX('<gist_query_vector_hex>'):>VECTOR(960, F16); 2 3-- ANN search: find 100 approximate nearest neighbors4SELECT id, EUCLIDEAN_DISTANCE(f16_col, @qvec) AS dist 5FROM t_f16 6ORDER BY dist ASC7LIMIT 100;
インデックス構築
インデックス構築時間は以下のとおりです。F16ベクトルの場合、F16からF32への型変換に伴うオーバーヘッドのため、インデックス構築は若干遅くなります。
F16 | F32 | 向上率 | |
| 12.11 s | 11.06 s | -9.5% |
| 463.3 s | 429.25 s | -7.3% |
IVF_PQFSおよびHNSW_FLATインデックスを作成するためのクエリは、以下の「方法論」セクションに示されています。
より優れた
なるほど、ベクトル検索の精度向上は不可能だとお考えだったのですね。しかし、ここまでお読みいただいたということは、その答えを知りたいからでしょう。確かに、得られる結果の質はF32ベクトルを使用した場合とほぼ同じなので、精度が向上したとは言えません。しかし、F16ベクトルを使用することでコストを削減できます。なぜなら、F16ベクトル検索は、F32検索とほぼ同じ速度で、半分のハードウェアとストレージで実行でき、テストデータセットではほぼ同等の再現率を実現できるからです。価格は半分で、性能は同等です!
しかしながら、科学、医療画像処理、金融アプリケーションなど、より高い数値精度が求められる用途においては、F32の使用は依然として重要です。しかし、ほとんどの用途においては、特に大規模なデータセットの場合、F16の方が優れた選択肢であることが、私たちの実験によって示されています。
リコール
GIST 1Mデータセットには正解データが含まれています。再現率は、クエリ結果を正解データと比較することで算出しました。結果は以下のとおりです。
F16とF32は、どちらのインデックスタイプにおいても統計的に同等の再現率を示します。1%未満の変動は、近似最近傍探索(ANN)とベンチマーク手法に内在する誤差範囲内です。実際には、F16はF32と同等の検索品質を提供します。
|
|
|
| |
リコール | 96.4% | 95.8% | 97.9% | 98.0% |
F16ベクトルの実践
ベクトル検索を使用して新しいアプリケーションを構築する場合、アプリケーションに高い精度が必要ない場合は、F16 の利点を得るために必要なことは、そのVECTOR(<N>, F16)型の列を持つテーブルを作成し、ベクトルデータの操作に関するドキュメントで説明されているようにそれらを使用することだけです。
F32 ベクトルを使用する既存のアプリケーションを F16 に移行する場合、テーブルを ALTER して新しい F16 ベクトル列を追加し、UPDATE コマンド(または、より小さなバッチの場合は一連の UPDATE コマンド)を使用するか、新しいテーブルを作成し、INSERT…SELECT コマンド(必要に応じてバッチでも可)を使用してデータを移行することができます。この操作を行うための SQL の例は、後のセクション「F32 から F16 への移行」に記載されています。
結論
SingleStore 9.1で新たに搭載されたF16ベクトルサポートにより、ベクトルの保存容量を削減しながら、パフォーマンスを維持、あるいは向上させることができます。まさに一石二鳥です。
私たちは以前にも、ベクトルデータベースはベクトルデータベースであってはならないと主張しました。
ベクトルデータベースがベクトルデータベースであってはならない理由
ベクトルデータベースが依然としてベクトルデータベースであってはならない理由
ベクトルとベクトル検索は、データ型とクエリ処理手法であり、根本的な変更ではありません。ベクトル処理は、最新のSQLデータベースに効果的に統合できます。SingleStoreを使用することで、ベクトル処理の利点に加え、フルデータプラットフォームのあらゆる利点を享受できます。
付録
付録には補足情報が含まれています。テストプロセスを説明する方法論のセクションと、F32ベクターからF16ベクターへの移行方法の例が記載されています。
方法論
テストは、http://corpus-texmex.irisa.fr/ の GIST 1M データセットを使用して実施されました。このデータセットには、960 次元の 100 万個のベクトルが含まれています。
設定
- GIST 1Mベクトルは2つのテーブルに格納されました。1つはVECTOR(960, F16)列を持つテーブル、もう1つはVECTOR(960, F32)列を持つテーブルです。
1CREATE TABLE t_f16 (f16_col VECTOR(960,F16), id INT);2CREATE TABLE t_f32 (f32_col VECTOR(960,F32), id INT);
- F16ベクトルは、SingleStoreに組み込まれているF32からF16への変換機能を使用するINSERT INTOステートメントを用いて、F32列から取得されました。
1INSERT INTO t_f16 2SELECT * FROM t_f32;
- すべてのテストは、SingleStore Helios S-00を使用して実施されました。
- テーブルサイズを計算するために、以下のクエリが使用されました。
- すべてのテストは5回実行され、結果は中央の3つの値の平均値です。
1SELECT2 cs.database_name,3 cs.table_name,4 dp.role,5 FORMAT(SUM(cs.uncompressed_size) / 1024/1024, 0) AS uncompressed_mb,6 FORMAT(SUM(cs.compressed_size) / 1024/1024, 0) AS compressed_mb7FROM information_schema.columnar_segments AS cs8JOIN information_schema.distributed_partitions AS dp9 ON cs.database_name = dp.database_name10 AND cs.partition = dp.ordinal11 AND cs.node_id = dp.node_id12WHERE cs.database_name = '<db-name>'13 AND dp.role = 'Master'14GROUP BY15 cs.database_name,16 cs.table_name,17 dp.role;
DOT_PRODUCTとEUCLIDEAN_DISTANCEのパフォーマンス
DOT_PRODUCTおよびEUCLIDEAN_DISTANCEの性能は以下のように評価されました。
このクエリを使用して、F16テスト用のクエリベクトルが選択されました。
1SELECT f32_col INTO @vec_f32 2FROM t_f32 3ORDER BY f32_col DESC LIMIT 1;4 5SET @vec_f16 = @vec_f32 :> VECTOR(960, F16);
F16のパフォーマンス結果を得るために、以下のクエリの実行時間を計測しました。
1SELECT DOT_PRODUCT (f16_col, @vec_f16) AS score 2FROM t_f16 3ORDER BY score DESC LIMIT 10;4 5SELECT EUCLIDEAN_DISTANCE (f16_col, @vec_f16) AS dist 6FROM t_f16 7ORDER BY dist DESC LIMIT 10;8
このクエリを使用して、F32テスト用のクエリベクトルが選択されました。
1SELECT f32_col INTO @vec_f32 2FROM t_f32 3ORDER BY f32_col DESC LIMIT 1;4
F32のパフォーマンス結果を得るために、以下のクエリの 実行時間を計測しました。
1SELECT DOT_PRODUCT(f32_col, @vec_f32) AS score 2FROM t_f32 3ORDER BY score DESC LIMIT 10;4 5SELECT EUCLIDEAN_DISTANCE(f32_col, @vec_f32) AS dist 6FROM t_f32 7ORDER BY dist DESC LIMIT 10;
ANN 検索性能
インデックス付きANNクエリのパフォーマンスは、同様のクエリを使用して、ただしインデックス付きのテーブルに対して評価されました。
F16列のインデックス作成:
1-- Build IVF_PQFS index 2ALTER TABLE t_f16 ADD VECTOR INDEX (f16_col)3INDEX_OPTIONS '{"index_type":"IVF_PQFS", "metric_type":"EUCLIDEAN_DISTANCE", "nlist":128, "nprobe":100, "m":240}'; 4 5-- Build HNSW_FLAT index 6ALTER TABLE t_f16 ADD VECTOR INDEX (f16_col)7INDEX_OPTIONS '{"index_type":"HNSW_FLAT", "metric_type":"EUCLIDEAN_DISTANCE", "M":16, "efConstruction":128, "ef":200}';
F32列のインデックス作成:
1-- Build IVF_PQFS index 2ALTER TABLE t_f32 ADD VECTOR INDEX (f32_col)3INDEX_OPTIONS '{"index_type":"IVF_PQFS", "metric_type":"EUCLIDEAN_DISTANCE", "nlist":128, "nprobe":100, "m":240}'; 4 5-- Build HNSW_FLAT index 6ALTER TABLE t_f32 ADD VECTOR INDEX (f32_col)7INDEX_OPTIONS '{"index_type":"HNSW_FLAT", "metric_type":"EUCLIDEAN_DISTANCE", "M":16, "efConstruction":128, "ef":200}';
F16列のANN検索(gist_query_vector_hex はGIST1Mベンチマークデータセットからの960次元クエリベクトルの16進数エンコード表現):
1SET @qvec = UNHEX('<gist_query_vector_hex>'):>VECTOR(960, F16); 2 3SELECT id, EUCLIDEAN_DISTANCE(f16_col, @qvec) AS dist 4FROM t_f16 5ORDER BY dist ASC LIMIT 100;
ANNでF32列を検索する:
1SET @qvec = UNHEX('<gist_query_vector_hex>'):>VECTOR(960, F32); 2 3SELECT id, EUCLIDEAN_DISTANCE(f32_col, @qvec) AS dist 4FROM t_f32 5ORDER BY dist ASC LIMIT 100;
F32からF16への移行
SingleStoreはF32からF16ベクトルへの変換をサポートしているため、F32からF16への移行が簡単に行えます。そのため、ストレージコストをすぐに半分に削減できます。
F32ベクトルを使用するテーブルをF16ベクトルに変換するには:
- テーブルに
VECTOR(<N>, F16)型を示す新しい列を追加します。 - UPDATE … SET ステートメントを使用して、F32 の値を F16 の値に変換します。
- F32列とそれに関連するインデックスを削除します。
- 必要に応じて、新しい列の名前を変更し、インデックスを作成してください。
F32ベクトルに関する 以下のCREATE TABLEとADD INDEX記述が与えられています。
1CREATE TABLE t_vecs(c_orig VECTOR(1024, F32));2 3... Insert Data ...4 5ALTER TABLE t_vecs ADD VECTOR INDEX i_orig (c_orig)6 INDEX_OPTIONS '...';
以下のクエリを使用して、F16ベクトルの新しい列を追加し、それらのF16ベクトルの値をF32ベクトルの値に設定します。
UPDATE … SET コマンドは、SingleStore に組み込まれている型変換機能を使用して、F32 ベクトルを F16 ベクトルに変換 します。
1ALTER TABLE t_vecs ADD column c_new VECTOR(1024,F16);2UPDATE t_vecs SET c_new = c_orig;3
新しい列が作成され、更新されたら、古い列とインデックスを削除し、新しい列の名前を元の列名に変更し、インデックスが存在していた場合はインデックスを再作成します。
新しい列と再作成されたインデックスは、元の列とインデックスと同じ名前を使用するため、既存のクエリは変更なしで実行されます。
1DROP INDEX i_orig ON t_vecs;2ALTER TABLE t_vecs DROP COLUMN c_orig;3 4ALTER TABLE t_vecs CHANGE c_new c_orig;5ALTER TABLE t_vecs ADD VECTOR INDEX i_orig (c_orig)6 INDEX_OPTIONS '...';














