検索拡張生成 (RAG) がどのように機能するかという理由から、ベクトル検索は生成 AI ツールの重要なコンポーネントです。
2023 年にはベクター検索製品やプロジェクトが爆発的に増加しており、その中から選択することが真剣な取り組みとなっています。オプションを調査するときは、次の難しい問題と、それらを解決するためのさまざまなアプローチを考慮する必要があります。ここでは、これらの課題について説明し、DataStax Astra DB および Apache Cassandra のベクトル検索の実装のために DataStax がどのように課題に取り組んだかについて説明します。
これらの困難な問題の中心には、研究者が「
多くのベクトル検索アルゴリズムは、単一マシンのメモリに収まるデータセット用に設計されており、これが現在でもテストされている唯一のシナリオです。
他のドメインと同様に、スケールアウトにはレプリケーションとパーティショニングの両方が必要であり、また、デッド レプリカの交換やネットワーク分割後のレプリカの修復などを処理するサブシステムも必要です。
これは私たちにとって簡単なことでした。スケールアウト レプリケーションは Cassandra の基本であり、それを Cassandra-5.0 の新機能 SAI (Storage-Attached Indexing – を参照) と組み合わせることで、
「ガベージ コレクション」とは、インデックスから古い情報を削除すること、つまり削除された行をクリーンアップし、インデックス付きベクトル値が変更された行を処理することを意味します。これは言及する価値がないように思えるかもしれません – これはリレーショナル データベースの世界では 40 年間にわたって多かれ少なかれ解決されてきた問題です – しかし、ベクトル インデックスはユニークです。
重要な問題は、低遅延の検索と高い再現率の結果の両方を提供する、私たちが知っているすべてのアルゴリズムがグラフベースであることです。他にも多数のベクトル インデックス付けアルゴリズムが利用可能です。
グラフ インデックスの課題は、行やドキュメントが変更されたときに古い (ベクトルに関連付けられた) ノードを単純に削除できないことです。これを数回以上行うと、グラフはクエリ ベクトルとより類似性の高い領域に BFS (幅優先検索) を向けるという目的を実行できなくなります。
したがって、このガベージ コレクションを実行するには、ある時点でインデックスを再構築する必要がありますが、いつ、どのように整理するのでしょうか?変更が行われたときに常にすべてを再構築すると、実行される物理書き込みが大幅に増加します。これは書き込み増幅と呼ばれます。一方、再構築しない場合は、クエリ時に古い行をフィルタリングして除外する追加の作業 (「読み取り増幅」) を実行することになります。
これは、Cassandra が長年取り組んできたもう 1 つの問題領域です。 SAI インデックスはメイン ストレージのライフサイクルに関連付けられているため、Cassandra にも参加します
DataStax Astra DB は、Apache Cassandra 上に構築され、クラウド アプリケーション ワークロード用のプラットフォームを提供します。
これは、次のようなワークロードを意味します。
1 秒あたり数千から数百万のリクエストを同時に大量に実行し、通常はそれぞれ数行ずつ取得します。これが、たとえ資金的に余裕があったとしても、Snowflake 上で Netflix を実行できなかった理由です。Snowflake や同様の分析システムは、それぞれが数秒から数分、あるいはそれ以上実行される少数の同時リクエストのみを処理するように設計されています。
メモリよりも大きい データセットが 1 台のマシンのメモリに収まる場合、使用するツールはほとんど問題になりません。 Sqlite、MongoDB、MySQL – それらはすべて正常に動作します。そうでない場合、事態はさらに困難になります。そして悪いニュースは、ベクトル埋め込みは通常数 KB、または一般的なデータベース ドキュメントよりも約 1 桁大きいため、比較的すぐにメモリを超えるサイズに到達してしまうことです。
アプリケーションの中核 データがそれほど重要ではない、または実際の記録ソースから再構築できるため、データが失われても気にしないのであれば、やはり、どのツールを使用するかは関係ありません。 Cassandra や Astra DB などのデータベースは、何があってもデータの可用性と耐久性を維持できるように構築されています。
上でも言いましたが、よく知られているのは、
関連する問題は、ann-benchmarks が一度に 1 種類の操作しか実行しないことです。最初にインデックスを構築し、次にインデックスをクエリします。検索に挟まれた更新の処理はオプションであり、おそらくハンディキャップになる可能性があります。更新に対処する必要がないことがわかっている場合は、人工的なベンチマークで適切に見える、単純化するための多くの仮定を立てることができます。
インデックスに対して複数の同時操作を実行できること、またはインデックスの構築後に更新できることを重視している場合は、インデックスがどのように機能するのか、どのようなトレードオフが関係しているのかを理解するために、もう少し詳しく調べる必要があります。
まず、私が知っている汎用ベクトル データベースはすべて、グラフベースのインデックスを使用しています。これは、最初のベクトルが挿入されるとすぐにグラフ インデックスのクエリを開始できるためです。他のほとんどのオプションでは、クエリを実行する前にインデックス全体を構築するか、少なくともデータの予備スキャンを実行して統計的プロパティを学習する必要があります。
ただし、グラフ インデックス カテゴリ内にも重要な実装の詳細がまだあります。たとえば、最初は、MongoDB Elastic や Solr と同じように、Lucene の HNSW インデックス実装を使用することで時間を節約できるのではないかと考えました。しかし、Lucene が提供するのはシングルスレッドの非同時インデックス構築のみであることがすぐにわかりました。つまり、構築中にクエリを実行したり (これがこのデータ構造を使用する主な理由の 1 つです!)、複数のスレッドで同時に構築することもできません。
HNSW の論文では、きめ細かいロック手法が機能する可能性があることを示唆していますが、私たちはさらに改良を加えて、ノンブロッキング インデックスを構築しました。これはオープンソース化されています
JVector は、同時更新を少なくとも 32 スレッドまで線形に拡張します。このグラフは x 軸と y 軸の両方で対数スケールになっており、スレッド数が 2 倍になるとビルド時間が半分になることがわかります。
さらに重要なことは、JVector のノンブロッキング同時実行性は、検索と更新を組み合わせることで、より現実的なワークロードにメリットをもたらします。以下は、さまざまなデータセットにおける Astra DB のパフォーマンス (JVector を使用) と Pinecone の比較です。 Astra DB は、静的データセットの場合は Pinecone よりも約 10% 高速ですが、新しいデータのインデックス作成も 8 倍から 15 倍高速です。より高いスループットとより低いレイテンシに関する推奨事項に基づいて、Pinecone を使用した利用可能な最適なポッド層 (ポッド タイプ: p2、ポッド サイズ: x8、レプリカあたり 2 つのポッド) を選択しました。 Pinecone は、これがどのような物理リソースに対応するかを明らかにしていません。 Astra DB 側では、デフォルトの PAYG デプロイメント モデルを選択しましたが、サーバーレスであるため、リソースの選択について心配する必要はありませんでした。テストは以下を使用して実行されました。
Astra DB は、より高い再現率と精度を維持しながらこれを実行します (
から始めました。
HNSW インデックスは一連のレイヤーであり、ベース レイヤーより上の各レイヤーのノード数は前のレイヤーの約 10% です。これにより、上位層がスキップ リストとして機能し、すべてのベクトルを含む下位層の右近傍を検索できるようになります。
ただし、この設計は、(すべてのグラフ インデックスに共通して) 「ディスク キャッシュが私たちを救ってくれる」と言い逃れることはできないことを意味します。これは、通常のデータベース クエリ ワークロードとは異なり、グラフ内のすべてのベクトルがほぼ等しい確率で、検索に関連していること。 (例外は上位層であり、キャッシュすることができます。)
Lucene を使用していた頃、64 GB の RAM を搭載した私のデスクトップ上で、Wikipedia 記事チャンクの 1 億ベクトル データセット (ディスク上に約 120 GB) を提供するプロファイルは次のようになります。
Cassandra は、ディスクからベクトルを読み取るのを待つことにほぼすべての時間を費やします。
この問題を解決するために、DiskANN と呼ばれるより高度なアルゴリズムを実装し、スタンドアロンの組み込みベクトル検索エンジンとしてオープンソース化しました。
クライアント/サーバー コンポーネントを持たない純粋な組み込みシナリオでの HNSW と DiskANN の比較を次に示します。これは、Lucene (HNSW) および JVector (DiskANN) での Deep100M データセットの検索速度を測定します。 Lucene インデックスは、インデックスと生のベクトルを含めて 55 GB です。 JVector インデックスは 64GB です。検索は私の 24GB MacBook で実行されました。この MacBook には、RAM にインデックスを保持するのに必要なメモリの約 3 分の 1 が搭載されています。
データベース システムのコンテキストにおける構成可能性とは、一貫した方法でさまざまな機能をシームレスに統合する機能を指します。これは、ベクトル検索などの新しいカテゴリの機能の統合を議論する場合に特に重要です。おもちゃ以外のアプリケーションには、常に古典的な両方が必要です
シンプルなことを考えてみましょう
より現実的な使用例では、当社のソリューション エンジニアの 1 人が最近、製品カタログにセマンティック検索を追加したいと考えているアジアの企業と協力していましたが、用語ベースのマッチングも可能にしたいと考えていました。たとえば、ユーザーが [「赤」ボール バルブ] を検索する場合、ベクトル埋め込みが意味的にどれほど類似していても、説明が「赤」という用語に一致する項目に検索を制限したいと考えます。したがって、重要な新しいクエリは (セッション管理、注文履歴、ショッピング カートの更新などの従来の機能に加えて)、次のようになります。引用された用語をすべて含む製品に製品を制限し、ユーザーの検索に最も類似したものを見つけます。
この 2 番目の例は、アプリケーションが従来のクエリ機能とベクトル検索の両方を必要とするだけでなく、多くの場合、同じクエリ内でそれぞれの要素を使用できる必要があることを明らかにしています。
この若い分野における現在の最先端技術は、私がクラシック クエリと呼んでいるものを「通常の」データベースで実行し、ベクトル クエリをベクトル データベースで実行し、両方が有効な場合にその 2 つをアドホックにつなぎ合わせることです。同時に必要となります。これはエラーが発生しやすく、時間がかかり、コストがかかります。唯一の利点は、より良い解決策が見つかるまで実行できることです。
Astra DB では、Cassandra SAI 上に、より優れたソリューションを構築 (そしてオープンソース化) しました。 SAI では、Cassandra の安定版と圧縮のライフ サイクルにすべて関連付けられたカスタム インデックス タイプを作成できるため、開発者は Astra DB で簡単にブール述語、用語ベースの検索、およびベクトル検索をオーバーヘッドなしで組み合わせることができます。個別のシステムの管理と同期。これにより、生成 AI アプリケーションを構築する開発者は、より高度なクエリ機能を利用できるようになり、生産性が向上し、市場投入までの時間が短縮されます。
ベクトル検索エンジンは、スケールアウト、ガベージ コレクション、同時実行性、ディスクの効果的な使用、構成可能性など、複数のアーキテクチャ上の課題を伴う重要な新しいデータベース機能です。 Astra DB のベクトル検索を構築する際に、Cassandra の機能を活用して、生成 AI アプリケーションの開発者にクラス最高のエクスペリエンスを提供することができたと信じています。
- Jonathan Ellis 著、DataStax
リード画像ソース。