😺

Amazon S3 Vectors 徹底検証:1,000万規模での実際の処理時間は?

に公開

はじめに

今回の記事では、Amazon S3 Vectorsを使用した様々な検証を行います。

S3 Vectorsは数十億のベクトルに対応可能1秒未満のクエリパフォーマンスなどの触れ込みですが、実際にどの程度のベクトルだとどの程度の処理時間になるのか、また精度上の問題はないのか、などのノウハウが実用上は必要になってきます。

ベクトル数・次元数など様々な条件での検証を行い、特に精度・速度面について数値で比較することで、実用に耐え得るものかを確かめたいと思います。

本編

S3 Vectorsとは

Amazon S3 Vectorsは、Amazon S3上でネイティブにベクトルデータの保存・検索を可能にする機能です。高耐久・高可用性を維持しながら、従来のベクトルDBよりも最大90%のコスト削減を実現します。S3のスケーラビリティを活かして数十億規模のベクトルをサブ秒で検索可能です。Bedrockなどの埋め込みモデルにも対応し、またOpenSearchとの連携も柔軟に行えます。

個人的には、安い管理が楽というメリットが実用上は大きく、処理時間の要件がよっぽどシビアでない限りはS3 Vectorsでいいかなというくらいの神サービスだと思っています。

実験条件

データセット

今回は「画像検索」での検証を行いますが、コンテンツをベクトル化して検索するだけなので基本的にはテキストでも考え方は同じです。

データセットとしては古典的ですが、「UKBench」を使用します。

  • 10,200枚の画像から構成され、この画像はDB、クエリ共通で使用する
  • 2,550個の異なるオブジェクトが含まれており、各物体の画像が4枚ずつ含まれている
  • DB、クエリ共通なので、検索結果1位は基本的に自分自身となる
  • 検索結果上位4件を確認して、同じオブジェクト(正解画像)が何枚含まれているかをカウントし、その平均値スコアとする

本来、本データセットの精度は最小で0.0、最大で4.0 (すべてのクエリで上位4件が正解) のスコアになるのですが、今回は分かりやすくするためこれを4で割り、0.0~1.0の値で表示します。
つまり検索結果トップ4件のうち、正解は何%あるか(Recall@4)という値になります。

また、画像枚数をかさましするために、ディストラクター(関係のないデータ)として、「Microsoft COCO 2017」からランダムクロップした画像を使用します。

ベクトル化

DINOv3を使用します。いくつかモデルの種類がありますが、下記の3種類を使います。

Model ベクトル次元数
ViT-S/16 distilled 384
ViT-B/16 distilled 768
ViT-L/16 distilled 1024

環境

us-east-1のS3ベクトルバケットを使用します。検索には、同リージョンのCloudShellを使用しました。

また、比較として利用したローカルマシンのスペックは下記のとおりです。

  • CPU: Intel Core i7-13700KF 16c-24t
  • MEM: 32GB

ベクトルの登録・検索などの処理はすべてPythonからboto3を使用して実施しました。

実験

ベクトル数の影響

まずは、DBのベクトル数を1万~1000万まで増やしたときの、検索精度と処理時間の推移です。なお、ベクトル次元数は384、topK (上位何件の検索結果を取得するか) は5です。
embedding (ベクトル算出) 時間は含めず、メモリに展開済みのベクトルをquery_vectorsにより1ベクトルずつ送信し、結果を受領するまでの時間を計測しています。

ベクトル数 10,200 100,000 500,000 1,000,000 10,000,000
検索時間/クエリ (ミリ秒) 112 137 170 207 382
精度 0.96775 0.97265 0.969125 0.968925 0.90775

1,000万ベクトルで検索時間は約0.4秒であり、1秒未満での検索は十分に達成できています。
ベクトル数に応じて検索時間が線形に増えるわけではないので、数千万ベクトルの単位であればおそらく1秒未満を達成できるでしょう。

一方で、1つのベクトルインデクスに格納できるベクトル数は最大5,000万なので、それ以上になると複数のベクトルインデックスを横断した検索を行う必要が出てきます。直列に検索していると当然1秒を超えてしまうので、複数のベクトルインデックスに対して並列に検索を行い、それら結果を効率的に集約する工夫が必要になるかもしれません。

他手法との比較

参考として、その他の(近似)近傍探索との比較を行います。

  • Faiss
    IndexHNSWFlatを使用します。m=32、efConstruction=512としました。

  • NMSLIB
    methodはhnswを使用します。パラメータはデフォルトのままとしました。

  • 近傍探索
    Pythonの@演算子で内積を計算します。

なお、例えば10,200 x 10,200のコサイン類似度を一度に計算するのではなく、
クエリ側はfor文で1ベクトルずつ内積計算を実施しています。

下記は1クエリあたりの検索時間 (ミリ秒) の比較です。

ベクトル数 10,200 100,000 500,000 1,000,000 10,000,000
Faiss(ローカル実行) 0.03 0.06 0.09 0.10 0.27
NMSLIB(ローカル実行) 0.02 0.03 0.04 0.05 0.09
S3 Vectors 112 137 170 207 382
近傍探索(ローカル実行) 0.05 2.78 13.0 25.6 381

ローカル実行は通信も発生しないため、さすがに文字通り桁違いです。
そこで、ベクトル数10,200の検索時間を1に正規化して、処理時間の増加率を見てみます。

S3 Vectorsは通信などの固定時間が含まれるため、これはこれでS3 Vectorsに有利な点があるものの、ベクトル数の増加に対してS3 Vectorsの処理時間の増加は比較的緩やかです。

また、各手法の精度の推移も見てみます。

ベクトル数増加に対するS3 Vectorsの精度低下はFaissと同程度であり、一概に比較できるものではないものの悪くない精度と言えるのではないでしょうか。

なお上記結果は、FaissとNMSLIBと比べるとFaissは高精度・NMSLIBは高速と見えがちですが、ライブラリの違いというよりはHNSWのパラメータの違いによるものが大きいと思われます。

次元数の影響

ベクトルの次元数を増やしていった場合の変化です。S3 Vectorsは最大4096次元まで対応しているため、1024次元ベクトルを4つ並べて作った4096次元ベクトルまで評価しています。
ベクトル数はすべて10万です。

次元数 384 768 1024 4096
検索時間/クエリ (ミリ秒) 137 151 158 215
精度 0.97265 0.98295 0.988325 0.98795

次元数の増加に伴う見かけ上の処理時間の増加は緩やかであることが分かります。
検索時間向上を目的とした次元圧縮などは、効果が薄そうです。

まとめ

今回はAmazon S3 Vectorsについて実用上重要となる、ベクトル数・次元数など様々な条件での検証を行いました。結果をまとめると概ね下記の通りです。

  • 384次元のベクトル1,000万で、検索時間は約0.4秒
  • 数千万ベクトルの単位であれば1秒未満を達成しそう
  • 数十億レベルでも、複数のベクトルインデックスに対して効率的に検索・集約できれば1秒未満が期待できそう
  • ベクトル数増加に対する精度低下は、Faissなどの近似近傍探索と比べて大きく劣るようなものではない
  • ベクトルの次元数が検索時間に与える影響は、そこまで大きくない

「数十億のベクトルに対応可能」、「1秒未満のクエリパフォーマンス」という触れ込みは、おそらくほとんどの場合で達成できるのだろうという所感です。

今回の検証結果は期待以上だと思っており、実用上、多くのユースケースでS3 Vectorsが最適なのではないかと思います。

【参考】TIPS

検証の過程で得られたノウハウや、気になったことを参考情報として記載します。

  • DBへのベクトル登録 (put_data) は一度に500件までで、低次元では処理時間は1秒程度
    1,000万ベクトルであれば、半日もあれば登録できました。
    ただし次元数を4096まで上げると、500まとめて登録するとエラーとなったため、合計データサイズに対するフィルタか何かがあるものと思います。

  • インデクシング時間は大きくなさそう
    数百万ベクトルを登録した直後でも、検索を行うとわりと即座に結果が返ってきました。
    内部アルゴリズムの詳細は分かりませんが、全ベクトル登録後に何らかのインデックス作成や学習などの処理が走るのではなく、個々のベクトルを登録中にインデックスが整理されているのかもしれません。

  • 検索 (query_vectors) のtopKは、なぜかたまにK-1件になる
    検索を実施する際に上位何件の検索結果を取得するかをtopKで指定しますが、なぜかたまに (5回に1回くらい) K-1個の結果しか返ってこないことがあります。
    同じ検索を繰り返しても再現性はなく、またKの値を変更しても発生します。謎です。

  • 登録されているベクトルの削除は1件ずつであり、遅い
    削除 (delete_vectors) ではkeyを指定して削除を行いますが、1秒で3~4件しか消せません。つまり登録の100倍以上遅いです。
    あまりに削除対象が多い場合は、ベクトルインデックスを作り直した方がいいでしょう。

Discussion