AIアプリケーションは、多くの場合、コンピューティング、データ、および金銭コストの点で小規模に開始できます。ユーザー コミットの増加によって本番アプリケーションの増やすが大きくなるにつれて、大量のデータの保存や取得に関連するコストなどの主要な要因は、最適化の重要な機会になります。これらの課題は、次の点に焦点を当てて対処できます。
効率的なベクトル検索アルゴリズム
自動量化プロセス
最適化された埋め込み戦略
RAG(検索拡張生成)とエージェントベースのシステムはどちらも、セマンティック類似検索を実行するためにベクトルデータ (画像、ビデオ、テキストなどのデータ オブジェクトの数値表現)に依存しています。 RG またはエージェント駆動型ワークフローを使用するシステムは、高速応答時間を維持し、 取得レイテンシ を最小限に抑え、 インフラストラクチャ コストを制御するために、大規模で高次元のデータセットを効率的に処理する必要があります。
チュートリアルについて
このチュートリアルでは、増やすな 高度なAIワークロードを設計、配置、管理するために必要な手法を取得し、 最適なパフォーマンスとコスト効率 を確保します。
具体的には、このチュートリアルでは、次の方法を学習します。
Vyage AI の
voyage-3-large
は、量子化も認識されている汎用の多言語埋め込みモデルであり、 を使用して埋め込みを生成し、 MongoDBデータベースに取り込みます。埋め込みを自動的に定量化してデータ型の精度を低くし、メモリ使用量と クエリレイテンシ の両方を最適化します。
浮動小数点数32、int8、バイナリ埋め込みを比較するクエリを実行し、データ型の精度を効率と検索精度で比較します。
量化された埋め込みの再現率(保持とも呼ばれる)を測定し、量子化された ANN 検索が全精度の ENN 検索と同じドキュメントをどの程度効率的に取得するかを評価します。
注意
バイナリ数量化はリソース消費の削減を必要とするシナリオに最適ですが、精度の低下に対応するために再スコアリングのパスが必要になる場合があります。
スカラー量化は実質的な中間値を提供し、パフォーマンスと精度のバランスが必要なほとんどのユースケースに適しています。
浮動小数:32
前提条件
Atlas の サンプル データ セット からの映画データを含むコレクションを使用します。
High-CPU
S20
以上の検索階層を使用する 2 以上の検索ノードを持つM20
以上の Atlas クラスター
手順
必要なライブラリをインポートし、環境変数を設定します。
.ipynb
拡張機能のファイルを保存して、インタラクティブPythonノートを作成します。ライブラリをインストールします。
このチュートリアルでは、次のライブラリをインポートする必要があります。
PyMongo
voyageai
データの埋め込みを生成するためにAI Pythonクライアントを投票します。
pandas
データセット
準備ができたデータセットへのアクセスを提供する Huge Page ライブラリ。
matplotlib
ライブラリをインストールするには、次のコマンドを実行します。
pip install --quiet -U pymongo voyageai pandas datasets matplotlib 環境変数を安全に取得して設定します。
次の
set_env_securely
ヘルパー関数は、環境変数を安全に取得して設定します。次のコードをコピーして貼り付けて実行し、プロンプトが表示されたら、Vyage AI APIキーや Atlas クラスター接続文字列などのシークレット値を設定します。1 import getpass 2 import os 3 import voyageai 4 5 # Function to securely get and set environment variables 6 def set_env_securely(var_name, prompt): 7 value = getpass.getpass(prompt) 8 os.environ[var_name] = value 9 10 # Environment Variables 11 set_env_securely("VOYAGE_API_KEY", "Enter your Voyage API Key: ") 12 set_env_securely("MONGO_URI", "Enter your MongoDB URI: ") 13 MONGO_URI = os.environ.get("MONGO_URI") 14 if not MONGO_URI: 15 raise ValueError("MONGO_URI not set in environment variables.") 16 17 # Voyage Client 18 voyage_client = voyageai.Client()
Atlas クラスターにデータを取り込みます。
このステップでは、次のデータセットから最大 250000
のドキュメントを読み込みます。
Wikipedia-22 -12 -en-valyage-埋め込みデータセットには、Vorage 102432AI のvoyage-3-large
モデルから事前に生成された 次元浮動小数点数 埋め込みを持つ Wikipedia の記事のフラグメントが含まれています。これは、メタデータを含むプライマリドキュメントコレクションです。このデータセットは、セマンティック検索でベクトル量子化の影響をテストするためのさまざまなベクトルコーパスとして機能します。このデータセット内の各ドキュメントには、次のフィールドが含まれています。
| ドキュメントの ObjectId ( |
| ドキュメントの一意の識別子。 |
| ドキュメントのタイトル。 |
| ドキュメントのテキスト。 |
| ドキュメントのURL 。 |
| ドキュメントの Wikipedia のID 。 |
| ドキュメントの ビュー 数。 |
| ドキュメント内のID。 |
| ドキュメント内の言語の数。 |
| ドキュメントの 1024 次元ベクトル埋め込み。 |
Wikipedia-22 -12 -en-en 注釈データセットには、再現率測定関数用の注釈付き参照データが含まれています。このデータは、精度の検証と検索の品質への数量化の影響を評価するためにベンチマーク データセットとして使用されます。このデータセットの各ドキュメントには、ベクトル検索のパフォーマンスを評価するために使用される標準フィールドである次のフィールドが含まれています。
| ドキュメントの ObjectId ( |
| ドキュメントの一意の識別子。 |
| ドキュメントの Wikipedia のID 。 |
| ドキュメントのキー フレーズ、質問、部分情報、文章を含むクエリ。 |
| ドキュメントのベクトル検索のパフォーマンスを評価するために使用されるキー フレーズの配列。 |
| ドキュメントのベクトル検索のパフォーマンスを評価するために使用される部分情報の配列。 |
| ドキュメントのベクトル検索のパフォーマンスを評価するために使用される質問の配列。 |
| ドキュメントのベクトル検索のパフォーマンスを評価するために使用される文の配列。 |
データをクラスターにロードするための関数を定義します。
ノートにある次のコードをコピーして貼り付け、実行します。サンプルコードでは、次の関数を定義しています。
generate_bson_vector
を使用して、データセットの埋め込みをBSONバイナリ ベクトルに変換し、ベクトルの効率的なストレージと処理を可能にします。get_mongo_client
Atlas クラスター接続文字列を取得します。insert_dataframe_into_collection
Atlas クラスターにデータを取り込むため。
1 import pandas as pd 2 from datasets import load_dataset 3 from bson.binary import Binary, BinaryVectorDtype 4 import pymongo 5 6 # Connect to Cluster 7 def get_mongo_client(uri): 8 """Connect to MongoDB and confirm the connection.""" 9 client = pymongo.MongoClient(uri) 10 if client.admin.command("ping").get("ok") == 1.0: 11 print("Connected to MongoDB successfully.") 12 return client 13 print("Failed to connect to MongoDB.") 14 return None 15 16 # Generate BSON Vector 17 def generate_bson_vector(array, data_type): 18 """Convert an array to BSON vector format.""" 19 array = [float(val) for val in eval(array)] 20 return Binary.from_vector(array, BinaryVectorDtype(data_type)) 21 22 # Load Datasets 23 def load_and_prepare_data(dataset_name, amount): 24 """Load and prepare streaming datasets for DataFrame.""" 25 data = load_dataset(dataset_name, streaming=True, split="train").take(amount) 26 return pd.DataFrame(data) 27 28 # Insert datasets into MongoDB Collection 29 def insert_dataframe_into_collection(df, collection): 30 """Insert Dataset records into MongoDB collection.""" 31 collection.insert_many(df.to_dict("records")) 32 print(f"Inserted {len(df)} records into '{collection.name}' collection.") データをクラスターにロードします。
ノート PC 内の次のコードをコピーして貼り付け、実行して、データセットを Atlas クラスターにロードします。このコードは、次のアクションを実行します。
データセットを取得します。
埋め込みをBSON形式に変換します。
Atlas クラスターにコレクションを作成し、データを挿入します。
1 import pandas as pd 2 from bson.binary import Binary, BinaryVectorDtype 3 from pymongo.errors import CollectionInvalid 4 5 wikipedia_data_df = load_and_prepare_data("MongoDB/wikipedia-22-12-en-voyage-embed", amount=250000) 6 wikipedia_annotation_data_df = load_and_prepare_data("MongoDB/wikipedia-22-12-en-annotation", amount=250000) 7 wikipedia_annotation_data_df.drop(columns=["_id"], inplace=True) 8 9 # Convert embeddings to BSON format 10 wikipedia_data_df["embedding"] = wikipedia_data_df["embedding"].apply( 11 lambda x: generate_bson_vector(x, BinaryVectorDtype.FLOAT32) 12 ) 13 14 # MongoDB Setup 15 mongo_client = get_mongo_client(MONGO_URI) 16 DB_NAME = "testing_datasets" 17 db = mongo_client[DB_NAME] 18 19 collections = { 20 "wikipedia-22-12-en": wikipedia_data_df, 21 "wikipedia-22-12-en-annotation": wikipedia_annotation_data_df, 22 } 23 24 # Create Collections and Insert Data 25 for collection_name, df in collections.items(): 26 if collection_name not in db.list_collection_names(): 27 try: 28 db.create_collection(collection_name) 29 print(f"Collection '{collection_name}' created successfully.") 30 except CollectionInvalid: 31 print(f"Error creating collection '{collection_name}'.") 32 else: 33 print(f"Collection '{collection_name}' already exists.") 34 35 # Clear collection and insert fresh data 36 collection = db[collection_name] 37 collection.delete_many({}) 38 insert_dataframe_into_collection(df, collection) Connected to MongoDB successfully. Collection 'wikipedia-22-12-en' created successfully. Inserted 250000 records into 'wikipedia-22-12-en' collection. Collection 'wikipedia-22-12-en-annotation' created successfully. Inserted 87200 records into 'wikipedia-22-12-en-annotation' collection. 注意
埋め込みをBSONベクトルに変換し、データセットを Atlas クラスターに取り込むには、時間がかかる場合があります。
Atlas クラスターにログインし、 Data Explorerでコレクションを視覚的に検査して、データセットが正常にロードされたことを確認します。
コレクションに Atlas ベクトル検索インデックスを作成します。
この手順では、embedding
フィールドに次の 3 つのインデックスを作成します。
スカラー量子インデックス | スカラー量化メソッド を使用して埋め込みを定量化します。 |
バイナリ量子化インデックス | バイナリ量子化メソッドを使用して埋め込みを定量化します。 |
Float32 ANN Index | 埋め込みを定量化するには、float32 ANN メソッドを使用します。 |
Atlas ベクトル検索インデックスを作成するための関数を定義します。
ノートに以下をコピーして貼り付け、実行します。
1 import time 2 from pymongo.operations import SearchIndexModel 3 4 def setup_vector_search_index(collection, index_definition, index_name="vector_index"): 5 new_vector_search_index_model = SearchIndexModel( 6 definition=index_definition, name=index_name, type="vectorSearch" 7 ) 8 9 # Create the new index 10 try: 11 result = collection.create_search_index(model=new_vector_search_index_model) 12 print(f"Creating index '{index_name}'...") 13 14 # Wait for initial sync to complete 15 print("Polling to check if the index is ready. This may take a couple of minutes.") 16 predicate=None 17 if predicate is None: 18 predicate = lambda index: index.get("queryable") is True 19 while True: 20 indices = list(collection.list_search_indexes(result)) 21 if len(indices) and predicate(indices[0]): 22 break 23 time.sleep(5) 24 print(f"Index '{index_name}' is ready for querying.") 25 return result 26 27 except Exception as e: 28 print(f"Error creating new vector search index '{index_name}': {e!s}") 29 return None インデックスを定義します。
次のインデックス構成では、異なる数量化戦略が実装されます。
vector_index_definition_scalar_quantized
この構成では、次のスカラー量子化(int8)を使用します。
各ベクトル次元を 32 ビット浮動小数点数から 8 ビット整数に縮小
精度とメモリ効率の適切なバランスを維持
メモリの最適化が必要なほとんどの本番環境のユースケースに適しています
vector_index_definition_binary_quantized
この構成では、バイナリ量子化(int1)が使用されます。
各ベクトル次元を 1 ビットに縮小
最大メモリ効率を提供します
メモリの制約が重要な、極端に大規模な配置に最適
これらのインデックスが作成されると、自動量化が透過的に行われ、Atlas ベクトル検索はインデックスの作成および検索操作中に、float32 から指定された量子化形式への変換を処理します。
vector_index_definition_float32_ann
インデックス構成は、cosine
類似性関数を使用して、1024
次元の完全な忠実度ベクトルをインデックス化します。1 # Scalar Quantization 2 vector_index_definition_scalar_quantized = { 3 "fields": [ 4 { 5 "type": "vector", 6 "path": "embedding", 7 "quantization": "scalar", 8 "numDimensions": 1024, 9 "similarity": "cosine", 10 } 11 ] 12 } 13 # Binary Quantization 14 vector_index_definition_binary_quantized = { 15 "fields": [ 16 { 17 "type": "vector", 18 "path": "embedding", 19 "quantization": "binary", 20 "numDimensions": 1024, 21 "similarity": "cosine", 22 } 23 ] 24 } 25 # Float32 Embeddings 26 vector_index_definition_float32_ann = { 27 "fields": [ 28 { 29 "type": "vector", 30 "path": "embedding", 31 "numDimensions": 1024, 32 "similarity": "cosine", 33 } 34 ] 35 } setup_vector_search_index
関数を使用して、スカラー、バイナリ、float32 インデックスを作成します。インデックスのコレクション名とインデックス名を設定します。
wiki_data_collection = db["wikipedia-22-12-en"] wiki_annotation_data_collection = db["wikipedia-22-12-en-annotation"] vector_search_scalar_quantized_index_name = "vector_index_scalar_quantized" vector_search_binary_quantized_index_name = "vector_index_binary_quantized" vector_search_float32_ann_index_name = "vector_index_float32_ann" Atlas Vector Search インデックスを作成します。
1 setup_vector_search_index( 2 wiki_data_collection, 3 vector_index_definition_scalar_quantized, 4 vector_search_scalar_quantized_index_name, 5 ) 6 setup_vector_search_index( 7 wiki_data_collection, 8 vector_index_definition_binary_quantized, 9 vector_search_binary_quantized_index_name, 10 ) 11 setup_vector_search_index( 12 wiki_data_collection, 13 vector_index_definition_float32_ann, 14 vector_search_float32_ann_index_name, 15 ) Creating index 'vector_index_scalar_quantized'... Polling to check if the index is ready. This may take a couple of minutes. Index 'vector_index_scalar_quantized' is ready for querying. Creating index 'vector_index_binary_quantized'... Polling to check if the index is ready. This may take a couple of minutes. Index 'vector_index_binary_quantized' is ready for querying. Creating index 'vector_index_float32_ann'... Polling to check if the index is ready. This may take a couple of minutes. Index 'vector_index_float32_ann' is ready for querying. vector_index_float32_ann' 注意
操作が完了するまでに数分かかる場合があります。インデックスをクエリで使用するには、インデックスが Ready 状態である必要があります。
Atlas クラスターにログインし、Atlas Search のインデックスを視覚的に検査して、インデックスの作成が成功したことを確認します。
Atlas ベクトル検索インデックスを使用して埋め込みを生成し、コレクションをクエリするための関数を定義します。
このコードでは、次の関数を定義します。
get_embedding
関数は、Vorage AI のvoyage-3-large
埋め込みモデルを使用して、指定されたテキストの 1024 次元埋め込みを生成します。custom_vector_search
関数は次の入力パラメータを受け取り、ベクトル検索操作の結果を返します。user_query
埋め込みを生成するクエリ テキスト string。
collection
検索するMongoDBコレクション。
embedding_path
埋め込みを含むコレクション内のフィールド。
vector_search_index_name
クエリで使用するインデックスの名前。
top_k
返される結果内の上位ドキュメントの数。
num_candidates
検討する候補の数。
use_full_precision
の場合は Ann
False
を実行し、 の場合は ENNTrue
検索を実行するフラグ。注意
Ann
use_full_precision
False
検索の場合、 値はデフォルトで に設定されます。use_full_precision
True
EXN 検索を実行するには、 の値を に設定します。具体的には、この関数は次のアクションを実行します。
クエリテキストの埋め込みを生成する
$vectorSearch
ステージを構築します検索のタイプを設定します
返すコレクション内のフィールドを指定します
パフォーマンス統計を収集した後にパイプラインを実行する
結果を返します
1 def get_embedding(text, task_prefix="document"): 2 """Fetch embedding for a given text using Voyage AI.""" 3 if not text.strip(): 4 print("Empty text provided for embedding.") 5 return [] 6 result = voyage_client.embed([text], model="voyage-3-large", input_type=task_prefix) 7 return result.embeddings[0] 8 9 def custom_vector_search( 10 user_query, 11 collection, 12 embedding_path, 13 vector_search_index_name="vector_index", 14 top_k=5, 15 num_candidates=25, 16 use_full_precision=False, 17 ): 18 19 # Generate embedding for the user query 20 query_embedding = get_embedding(user_query, task_prefix="query") 21 22 if query_embedding is None: 23 return "Invalid query or embedding generation failed." 24 25 # Define the vector search stage 26 vector_search_stage = { 27 "$vectorSearch": { 28 "index": vector_search_index_name, 29 "queryVector": query_embedding, 30 "path": embedding_path, 31 "limit": top_k, 32 } 33 } 34 35 # Add numCandidates only for approximate search 36 if not use_full_precision: 37 vector_search_stage["$vectorSearch"]["numCandidates"] = num_candidates 38 else: 39 # Set exact to true for exact search using full precision float32 vectors and running exact search 40 vector_search_stage["$vectorSearch"]["exact"] = True 41 42 project_stage = { 43 "$project": { 44 "_id": 0, 45 "title": 1, 46 "text": 1, 47 "wiki_id": 1, 48 "url": 1, 49 "score": { 50 "$meta": "vectorSearchScore" 51 }, 52 } 53 } 54 55 # Define the aggregate pipeline with the vector search stage and additional stages 56 pipeline = [vector_search_stage, project_stage] 57 58 # Execute the explain command 59 explain_result = collection.database.command( 60 "explain", 61 {"aggregate": collection.name, "pipeline": pipeline, "cursor": {}}, 62 verbosity="executionStats", 63 ) 64 65 # Extract the execution time 66 vector_search_explain = explain_result["stages"][0]["$vectorSearch"] 67 execution_time_ms = vector_search_explain["explain"]["query"]["stats"]["context"][ 68 "millisElapsed" 69 ] 70 71 # Execute the actual query 72 results = list(collection.aggregate(pipeline)) 73 74 return {"results": results, "execution_time_ms": execution_time_ms}
Atlas ベクトル検索クエリを実行して、検索パフォーマンスを評価します。
次のクエリは、さまざまな量化戦略にわたってベクトル検索を実行し、スカラー量化、バイナリ量子化、および完全精度(float32)ベクトルのパフォーマンス メトリクスを測定すると同時に、各精度レベルでレイテンシ測定値を取得し、分析比較用に結果形式を標準化します。クエリ文字列「最大出力の生成数を増やすにはどうすればよいですか」には、Voyage AIを使用して生成された埋め込みを使用します。
クエリは、精度レベル(スカラー、バイナリ、浮動小数点数32)、結果セットのサイズ(top_k
)、レイテンシ(ミリ秒単位)、検索されたドキュメントの内容など、主要なパフォーマンス インジケーターを results
変数に保存し、次の包括的なメトリクスを提供します。さまざまな量化戦略にわたって検索パフォーマンスを評価します。
1 vector_search_indices = [ 2 vector_search_float32_ann_index_name, 3 vector_search_scalar_quantized_index_name, 4 vector_search_binary_quantized_index_name, 5 ] 6 7 # Random query 8 user_query = "How do I increase my productivity for maximum output" 9 test_top_k = 5 10 test_num_candidates = 25 11 12 # Result is a list of dictionaries with the following headings: precision, top_k, latency_ms, results 13 results = [] 14 15 for vector_search_index in vector_search_indices: 16 # Conduct a vector search operation using scalar quantized 17 vector_search_results = custom_vector_search( 18 user_query, 19 wiki_data_collection, 20 embedding_path="embedding", 21 vector_search_index_name=vector_search_index, 22 top_k=test_top_k, 23 num_candidates=test_num_candidates, 24 use_full_precision=False, 25 ) 26 # Include the precision in the results 27 precision = vector_search_index.split("vector_index")[1] 28 precision = precision.replace("quantized", "").capitalize() 29 30 results.append( 31 { 32 "precision": precision, 33 "top_k": test_top_k, 34 "num_candidates": test_num_candidates, 35 "latency_ms": vector_search_results["execution_time_ms"], 36 "results": vector_search_results["results"][0], # Just taking the first result, modify this to include more results if needed 37 } 38 ) 39 40 # Conduct a vector search operation using full precision 41 precision = "Float32_ENN" 42 vector_search_results = custom_vector_search( 43 user_query, 44 wiki_data_collection, 45 embedding_path="embedding", 46 vector_search_index_name="vector_index_scalar_quantized", 47 top_k=test_top_k, 48 num_candidates=test_num_candidates, 49 use_full_precision=True, 50 ) 51 52 results.append( 53 { 54 "precision": precision, 55 "top_k": test_top_k, 56 "num_candidates": test_num_candidates, 57 "latency_ms": vector_search_results["execution_time_ms"], 58 "results": vector_search_results["results"][0], # Just taking the first result, modify this to include more results if needed 59 } 60 ) 61 62 # Convert the results to a pandas DataFrame with the headings: precision, top_k, latency_ms 63 results_df = pd.DataFrame(results) 64 results_df.columns = ["precision", "top_k", "num_candidates", "latency_ms", "results"] 65 66 # To display the results: 67 results_df.head()
precision top_k num_candidates latency_ms results 0 _float32_ann 5 25 1659.498601 {'title': 'Henry Ford', 'text': 'Ford had deci... 1 _scalar_ 5 25 951.537687 {'title': 'Gross domestic product', 'text': 'F... 2 _binary_ 5 25 344.585193 {'title': 'Great Depression', 'text': 'The fir... 3 Float32_ENN 5 25 0.231693 {'title': 'Great Depression', 'text': 'The fir...
結果のパフォーマンス メトリクスは、精度レベル間のレイテンシの違いを示しています。これは、量子化によってパフォーマンスの大幅な向上はあるものの、精度と検索速度の間には明確なトレードオフがあり、全精度の浮動小数点数32操作は量子化された対応と比較してかなり多くの計算時間が必要になることを示しています。
さまざまなtop-k
とnum_candidates
の値でレイテンシを測定します。
次のクエリは、さまざまな精度レベルと検索スケールにわたってベクトル検索のパフォーマンスを評価するシステム的なレイテンシ測定フレームワークを導入しています。パラメーターtop-k
numCandidates
は返される結果の数を決定するだけでなく、MongoDB の HNSWグラフ検索の パラメーターも設定します。
numCandidates
値は、DNS 検索中に Atlas ベクトル検索 が探索する HNSWグラフ内のノードの数に影響します。ここで、値が大きいほど、真の最近傍が見つかる可能性は高くなりますが、計算時間がより長く必要になります。
latency_ms
を人間が判読できる形式に形式する関数を定義します。1 from datetime import timedelta 2 3 def format_time(ms): 4 """Convert milliseconds to a human-readable format""" 5 delta = timedelta(milliseconds=ms) 6 7 # Extract minutes, seconds, and milliseconds with more precision 8 minutes = delta.seconds // 60 9 seconds = delta.seconds % 60 10 milliseconds = round(ms % 1000, 3) # Keep 3 decimal places for milliseconds 11 12 # Format based on duration 13 if minutes > 0: 14 return f"{minutes}m {seconds}.{milliseconds:03.0f}s" 15 elif seconds > 0: 16 return f"{seconds}.{milliseconds:03.0f}s" 17 else: 18 return f"{milliseconds:.3f}ms" ベクトル検索クエリーのレイテンシを測定するための 関数を定義します。
次の関数は、
user_query
、collection
、vector_search_index_name
、use_full_precision
値、top_k_values
値、num_candidates_values
値を入力として受け取り、ベクトル検索の結果を返します。ここで、次の点に注意してください。ベクトル検索操作ではより多くのドキュメントが使用され、検索に時間がかかるため、
top_k
とnum_candidates
の値が増加するとレイテンシが増加します。完全忠実度検索(
use_full_precision=True
)のレイテンシは、全精度の浮動小数点数を使用してデータセット全体を検索するため、近似検索( )よりも時間がかかるためです。use_full_precision=False
32量子化検索のレイテンシは、近似検索と量子化ベクトルを使用するため、完全フィルター検索よりも低くなります。
1 def measure_latency_with_varying_topk( 2 user_query, 3 collection, 4 vector_search_index_name="vector_index_scalar_quantized", 5 use_full_precision=False, 6 top_k_values=[5, 10, 100], 7 num_candidates_values=[25, 50, 100, 200, 500, 1000, 2000, 5000, 10000], 8 ): 9 results_data = [] 10 11 # Conduct vector search operation for each (top_k, num_candidates) combination 12 for top_k in top_k_values: 13 for num_candidates in num_candidates_values: 14 # Skip scenarios where num_candidates < top_k 15 if num_candidates < top_k: 16 continue 17 18 # Construct the precision name 19 precision_name = vector_search_index_name.split("vector_index")[1] 20 precision_name = precision_name.replace("quantized", "").capitalize() 21 22 # If use_full_precision is true, then the precision name is "_float32_" 23 if use_full_precision: 24 precision_name = "_float32_ENN" 25 26 # Perform the vector search 27 vector_search_results = custom_vector_search( 28 user_query=user_query, 29 collection=collection, 30 embedding_path="embedding", 31 vector_search_index_name=vector_search_index_name, 32 top_k=top_k, 33 num_candidates=num_candidates, 34 use_full_precision=use_full_precision, 35 ) 36 37 # Extract the execution time (latency) 38 latency_ms = vector_search_results["execution_time_ms"] 39 40 # Store results 41 results_data.append( 42 { 43 "precision": precision_name, 44 "top_k": top_k, 45 "num_candidates": num_candidates, 46 "latency_ms": latency_ms, 47 } 48 ) 49 50 return results_data Atlas ベクトル検索クエリを実行して、レイテンシを測定します。
レイテンシ評価操作は、すべての数量化戦略にわたる検索を実行し、複数の結果セット サイズをテストし、標準化されたパフォーマンス メトリクスをキャプチャし、比較分析のための結果を集計することで、包括的なパフォーマンス分析を実行し、さまざまな構成と検索負荷下でのベクトル検索の動作を詳細に評価できるようにします。
1 # Run the measurements 2 user_query = "How do I increase my productivity for maximum output" 3 top_k_values = [5, 10, 50, 100] 4 num_candidates_values = [25, 50, 100, 200, 500, 1000, 2000, 5000, 10000] 5 6 latency_results = [] 7 8 for vector_search_index in vector_search_indices: 9 latency_results.append( 10 measure_latency_with_varying_topk( 11 user_query, 12 wiki_data_collection, 13 vector_search_index_name=vector_search_index, 14 use_full_precision=False, 15 top_k_values=top_k_values, 16 num_candidates_values=num_candidates_values, 17 ) 18 ) 19 20 # Conduct vector search operation using full precision 21 latency_results.append( 22 measure_latency_with_varying_topk( 23 user_query, 24 wiki_data_collection, 25 vector_search_index_name="vector_index_scalar_quantized", 26 use_full_precision=True, 27 top_k_values=top_k_values, 28 num_candidates_values=num_candidates_values, 29 ) 30 ) 31 32 # Combine all results into a single DataFrame 33 all_latency_results = pd.concat([pd.DataFrame(latency_results)]) Top-K: 5, NumCandidates: 25, Latency: 1672.855906 ms, Precision: _float32_ann ... Top-K: 100, NumCandidates: 10000, Latency: 184.905389 ms, Precision: _float32_ann Top-K: 5, NumCandidates: 25, Latency: 828.45855 ms, Precision: _scalar_ ... Top-K: 100, NumCandidates: 10000, Latency: 214.199836 ms, Precision: _scalar_ Top-K: 5, NumCandidates: 25, Latency: 400.160243 ms, Precision: _binary_ ... Top-K: 100, NumCandidates: 10000, Latency: 360.908558 ms, Precision: _binary_ Top-K: 5, NumCandidates: 25, Latency: 0.239107 ms, Precision: _float32_ENN ... Top-K: 100, NumCandidates: 10000, Latency: 0.179203 ms, Precision: _float32_ENN レイテンシの測定値では、精度の型間の明確なパフォーマンス階層が示されています。ここでは、バイナリ量子化が最も最短の検索時間を示し、その後にスカラー量子化が続きます。完全な精度の浮動小数点数32 ANN操作は、大幅に高いレイテンシを示します。量化された検索と完全精度の検索のパフォーマンスの差は、Top-K 値が増加するにつれて大きくなります。浮動小数点数32 ENN 操作は最も遅くなりますが、最高の精度の結果を提供します。
さまざまなTop-k値に対して検索レイテンシをプロットします。
1 import matplotlib.pyplot as plt 2 3 # Map your precision field to the labels and colors you want in the legend 4 precision_label_map = { 5 "_scalar_": "scalar", 6 "_binary_": "binary", 7 "_float32_ann": "float32_ann", 8 "_float32_ENN": "float32_ENN", 9 } 10 11 precision_color_map = { 12 "_scalar_": "orange", 13 "_binary_": "red", 14 "_float32_ann": "blue", 15 "_float32_ENN": "purple", 16 } 17 18 # Flatten all measurements and find the unique top_k values 19 all_measurements = [m for precision_list in latency_results for m in precision_list] 20 unique_topk = sorted(set(m["top_k"] for m in all_measurements)) 21 22 # For each top_k, create a separate plot 23 for k in unique_topk: 24 plt.figure(figsize=(10, 6)) 25 26 # For each precision type, filter out measurements for the current top_k value 27 for measurements in latency_results: 28 # Filter measurements with top_k equal to the current k 29 filtered = [m for m in measurements if m["top_k"] == k] 30 if not filtered: 31 continue 32 33 # Extract x (num_candidates) and y (latency) values 34 x = [m["num_candidates"] for m in filtered] 35 y = [m["latency_ms"] for m in filtered] 36 37 # Determine the precision, label, and color from the first measurement in this filtered list 38 precision = filtered[0]["precision"] 39 label = precision_label_map.get(precision, precision) 40 color = precision_color_map.get(precision, "blue") 41 42 # Plot the line for this precision type 43 plt.plot(x, y, marker="o", color=color, label=label) 44 45 # Label axes and add title including the top_k value 46 plt.xlabel("Number of Candidates") 47 plt.ylabel("Latency (ms)") 48 plt.title(f"Search Latency vs Num Candidates for Top-K = {k}") 49 50 # Add a legend and grid, then show the plot 51 plt.legend() 52 plt.grid(True) 53 plt.show() このコードは、
top-k
(検索された結果の数)が増加するにつれて、ベクトル検索ドキュメント検索がさまざまな埋め込み精度タイプ、バイナリ、スカラー、浮動小数点数32でどのように実行レイテンシれるかを示す次のレイテンシ チャートを返します。
表現キャパシティーと保持を測定します。
次のクエリは、Atlas ベクトル検索 が基礎データセットから関連ドキュメントをどの程度効率的に検索するかを測定します。これは、フィールド 真実の関連ドキュメントの総数 (Found/ Total) に対する関連ドキュメントの総数に対する比率として計算されます。例、クエリにフィールド 真実の関連ドキュメントが 5 あり、Atlas ベクトル検索 がそれらの 4 を見つけた場合、再現率は 0.8 または 80% になります。
ベクトル検索操作の表現キャパシティーと保持を測定する関数を定義します。この関数は、次の処理を行います。
完全な精度の浮動小数点数32 ベクトルと ENN 検索を使用してベースライン検索を作成します。
量子化ベクトルと ANN 検索を使用して量子化検索を作成します。
ベースライン検索と比較した量化された検索の保持を計算します。
量子化検索では、保持を適切な範囲内に維持する必要があります。表現キャパシティーが低い場合、ベクトル検索操作ではクエリのセマンティック意味をキャプチャできず、結果が正確ではない可能性があります。これは、量化が効果的でなく、使用された初期埋め込みモデルが量子化プロセスに効果的でないことを示します。量化を認識する 埋め込みモデルを利用することをお勧めします。つまり、訓練プロセス中に、モデルは特別に最適化され、量子化後もセマンティック プロパティを維持する埋め込みを生成します。
1 def measure_representational_capacity_retention_against_float_enn( 2 ground_truth_collection, 3 collection, 4 quantized_index_name, # This is used for both the quantized search and (with use_full_precision=True) for the baseline. 5 top_k_values, # List/array of top-k values to test. 6 num_candidates_values, # List/array of num_candidates values to test. 7 num_queries_to_test=1, 8 ): 9 retention_results = {"per_query_retention": {}} 10 overall_retention = {} # overall_retention[top_k][num_candidates] = [list of retention values] 11 12 # Initialize overall retention structure 13 for top_k in top_k_values: 14 overall_retention[top_k] = {} 15 for num_candidates in num_candidates_values: 16 if num_candidates < top_k: 17 continue 18 overall_retention[top_k][num_candidates] = [] 19 20 # Extract and store the precision name from the quantized index name. 21 precision_name = quantized_index_name.split("vector_index")[1] 22 precision_name = precision_name.replace("quantized", "").capitalize() 23 retention_results["precision_name"] = precision_name 24 retention_results["top_k_values"] = top_k_values 25 retention_results["num_candidates_values"] = num_candidates_values 26 27 # Load ground truth annotations 28 ground_truth_annotations = list( 29 ground_truth_collection.find().limit(num_queries_to_test) 30 ) 31 print(f"Loaded {len(ground_truth_annotations)} ground truth annotations") 32 33 # Process each ground truth annotation 34 for annotation in ground_truth_annotations: 35 # Use the ground truth wiki_id from the annotation. 36 ground_truth_wiki_id = annotation["wiki_id"] 37 38 # Process only queries that are questions. 39 for query_type, queries in annotation["queries"].items(): 40 if query_type.lower() not in ["question", "questions"]: 41 continue 42 43 for query in queries: 44 # Prepare nested dict for this query 45 if query not in retention_results["per_query_retention"]: 46 retention_results["per_query_retention"][query] = {} 47 48 # For each valid combination of top_k and num_candidates 49 for top_k in top_k_values: 50 if top_k not in retention_results["per_query_retention"][query]: 51 retention_results["per_query_retention"][query][top_k] = {} 52 for num_candidates in num_candidates_values: 53 if num_candidates < top_k: 54 continue 55 56 # Baseline search: full precision using ENN (Float32) 57 baseline_result = custom_vector_search( 58 user_query=query, 59 collection=collection, 60 embedding_path="embedding", 61 vector_search_index_name=quantized_index_name, 62 top_k=top_k, 63 num_candidates=num_candidates, 64 use_full_precision=True, 65 ) 66 baseline_ids = { 67 res["wiki_id"] for res in baseline_result["results"] 68 } 69 70 # Quantized search: 71 quantized_result = custom_vector_search( 72 user_query=query, 73 collection=collection, 74 embedding_path="embedding", 75 vector_search_index_name=quantized_index_name, 76 top_k=top_k, 77 num_candidates=num_candidates, 78 use_full_precision=False, 79 ) 80 quantized_ids = { 81 res["wiki_id"] for res in quantized_result["results"] 82 } 83 84 # Compute retention for this combination 85 if baseline_ids: 86 retention = len( 87 baseline_ids.intersection(quantized_ids) 88 ) / len(baseline_ids) 89 else: 90 retention = 0 91 92 # Store the results per query 93 retention_results["per_query_retention"][query].setdefault( 94 top_k, {} 95 )[num_candidates] = { 96 "ground_truth_wiki_id": ground_truth_wiki_id, 97 "baseline_ids": sorted(baseline_ids), 98 "quantized_ids": sorted(quantized_ids), 99 "retention": retention, 100 } 101 overall_retention[top_k][num_candidates].append(retention) 102 103 print( 104 f"Query: '{query}' | top_k: {top_k}, num_candidates: {num_candidates}" 105 ) 106 print(f" Ground Truth wiki_id: {ground_truth_wiki_id}") 107 print(f" Baseline IDs (Float32): {sorted(baseline_ids)}") 108 print( 109 f" Quantized IDs: {precision_name}: {sorted(quantized_ids)}" 110 ) 111 print(f" Retention: {retention:.4f}\n") 112 113 # Compute overall average retention per combination 114 avg_overall_retention = {} 115 for top_k, cand_dict in overall_retention.items(): 116 avg_overall_retention[top_k] = {} 117 for num_candidates, retentions in cand_dict.items(): 118 if retentions: 119 avg = sum(retentions) / len(retentions) 120 else: 121 avg = 0 122 avg_overall_retention[top_k][num_candidates] = avg 123 print( 124 f"Overall Average Retention for top_k {top_k}, num_candidates {num_candidates}: {avg:.4f}" 125 ) 126 127 retention_results["average_retention"] = avg_overall_retention 128 return retention_results Atlas ベクトル検索インデックスのパフォーマンスを評価して比較します。
1 overall_recall_results = [] 2 top_k_values = [5, 10, 50, 100] 3 num_candidates_values = [25, 50, 100, 200, 500, 1000, 5000] 4 num_queries_to_test = 1 5 6 for vector_search_index in vector_search_indices: 7 overall_recall_results.append( 8 measure_representational_capacity_retention_against_float_enn( 9 ground_truth_collection=wiki_annotation_data_collection, 10 collection=wiki_data_collection, 11 quantized_index_name=vector_search_index, 12 top_k_values=top_k_values, 13 num_candidates_values=num_candidates_values, 14 num_queries_to_test=num_queries_to_test, 15 ) 16 ) Loaded 1 ground truth annotations Query: 'What happened in 2022?' | top_k: 5, num_candidates: 25 Ground Truth wiki_id: 69407798 Baseline IDs (Float32): [52251217, 60254944, 64483771, 69094871] Quantized IDs: _float32_ann: [60254944, 64483771, 69094871] Retention: 0.7500 ... Query: 'What happened in 2022?' | top_k: 5, num_candidates: 5000 Ground Truth wiki_id: 69407798 Baseline IDs (Float32): [52251217, 60254944, 64483771, 69094871] Quantized IDs: _float32_ann: [52251217, 60254944, 64483771, 69094871] Retention: 1.0000 Query: 'What happened in 2022?' | top_k: 10, num_candidates: 25 Ground Truth wiki_id: 69407798 Baseline IDs (Float32): [52251217, 60254944, 64483771, 69094871, 69265870] Quantized IDs: _float32_ann: [60254944, 64483771, 65225795, 69094871, 70149799] Retention: 1.0000 ... Query: 'What happened in 2022?' | top_k: 10, num_candidates: 5000 Ground Truth wiki_id: 69407798 Baseline IDs (Float32): [52251217, 60254944, 64483771, 69094871, 69265870] Quantized IDs: _float32_ann: [52251217, 60254944, 64483771, 69094871, 69265870] Retention: 1.0000 Query: 'What happened in 2022?' | top_k: 50, num_candidates: 50 Ground Truth wiki_id: 69407798 Baseline IDs (Float32): [25391, 832774, 8351234, 18426568, 29868391, 52241897, 52251217, 60254944, 63422045, 64483771, 65225795, 69094871, 69265859, 69265870, 70149799, 70157964] Quantized IDs: _float32_ann: [25391, 8351234, 29868391, 40365067, 52241897, 52251217, 60254944, 64483771, 65225795, 69094871, 69265859, 69265870, 70149799, 70157964] Retention: 0.8125 ... Query: 'What happened in 2022?' | top_k: 50, num_candidates: 5000 Ground Truth wiki_id: 69407798 Baseline IDs (Float32): [25391, 832774, 8351234, 18426568, 29868391, 52241897, 52251217, 60254944, 63422045, 64483771, 65225795, 69094871, 69265859, 69265870, 70149799, 70157964] Quantized IDs: _float32_ann: [25391, 832774, 8351234, 18426568, 29868391, 52241897, 52251217, 60254944, 63422045, 64483771, 65225795, 69094871, 69265859, 69265870, 70149799, 70157964] Retention: 1.0000 Query: 'What happened in 2022?' | top_k: 100, num_candidates: 100 Ground Truth wiki_id: 69407798 Baseline IDs (Float32): [16642, 22576, 25391, 547384, 737930, 751099, 832774, 8351234, 17742072, 18426568, 29868391, 40365067, 52241897, 52251217, 52851695, 53992315, 57798792, 60163783, 60254944, 62750956, 63422045, 64483771, 65225795, 65593860, 69094871, 69265859, 69265870, 70149799, 70157964] Quantized IDs: _float32_ann: [22576, 25391, 243401, 547384, 751099, 8351234, 17742072, 18426568, 29868391, 40365067, 47747350, 52241897, 52251217, 52851695, 53992315, 57798792, 60254944, 64483771, 65225795, 69094871, 69265859, 69265870, 70149799, 70157964] Retention: 0.7586 ... Query: 'What happened in 2022?' | top_k: 100, num_candidates: 5000 Ground Truth wiki_id: 69407798 Baseline IDs (Float32): [16642, 22576, 25391, 547384, 737930, 751099, 832774, 8351234, 17742072, 18426568, 29868391, 40365067, 52241897, 52251217, 52851695, 53992315, 57798792, 60163783, 60254944, 62750956, 63422045, 64483771, 65225795, 65593860, 69094871, 69265859, 69265870, 70149799, 70157964] Quantized IDs: _float32_ann: [16642, 22576, 25391, 547384, 737930, 751099, 832774, 8351234, 17742072, 18426568, 29868391, 40365067, 52241897, 52251217, 52851695, 53992315, 57798792, 60163783, 60254944, 62750956, 63422045, 64483771, 65225795, 65593860, 69094871, 69265859, 69265870, 70149799, 70157964] Retention: 1.0000 Overall Average Retention for top_k 5, num_candidates 25: 0.7500 ... 出力には、真実のデータセット内の各クエリの保持結果が表示されます。保持は 0 から 1 の間の 10 進数で表されます。1.0 ではフィールド ルールの ID が保持されることを意味し、0.25 ではフィールド ルール ID の 25% のみが保持されることを意味します。
さまざまな精度の型の保持機能をプロットします。
1 import matplotlib.pyplot as plt 2 3 # Define colors and labels for each precision type 4 precision_colors = {"_scalar_": "orange", "_binary_": "red", "_float32_": "green"} 5 6 if overall_recall_results: 7 # Determine unique top_k values from the first result's average_retention keys 8 unique_topk = sorted(list(overall_recall_results[0]["average_retention"].keys())) 9 10 for k in unique_topk: 11 plt.figure(figsize=(10, 6)) 12 # For each precision type, plot retention vs. number of candidates at this top_k 13 for result in overall_recall_results: 14 precision_name = result.get("precision_name", "unknown") 15 color = precision_colors.get(precision_name, "blue") 16 # Get candidate values from the average_retention dictionary for top_k k 17 candidate_values = sorted(result["average_retention"][k].keys()) 18 retention_values = [ 19 result["average_retention"][k][nc] for nc in candidate_values 20 ] 21 22 plt.plot( 23 candidate_values, 24 retention_values, 25 marker="o", 26 label=precision_name.strip("_"), 27 color=color, 28 ) 29 30 plt.xlabel("Number of Candidates") 31 plt.ylabel("Retention Score") 32 plt.title(f"Retention vs Number of Candidates for Top-K = {k}") 33 plt.legend() 34 plt.grid(True) 35 plt.show() 36 37 # Print detailed average retention results 38 print("\nDetailed Average Retention Results:") 39 for result in overall_recall_results: 40 precision_name = result.get("precision_name", "unknown") 41 print(f"\n{precision_name} Embedding:") 42 for k in sorted(result["average_retention"].keys()): 43 print(f"\nTop-K: {k}") 44 for nc in sorted(result["average_retention"][k].keys()): 45 ret = result["average_retention"][k][nc] 46 print(f" NumCandidates: {nc}, Retention: {ret:.4f}") このコードでは、次の保持チャートが返されます。
float32_ann
、scalar
、binary
埋め込みの場合、コードは次のような詳細な平均保持結果も返します。Detailed Average Retention Results: _float32_ann Embedding: Top-K: 5 NumCandidates: 25, Retention: 1.0000 NumCandidates: 50, Retention: 1.0000 NumCandidates: 100, Retention: 1.0000 NumCandidates: 200, Retention: 1.0000 NumCandidates: 500, Retention: 1.0000 NumCandidates: 1000, Retention: 1.0000 NumCandidates: 5000, Retention: 1.0000 Top-K: 10 NumCandidates: 25, Retention: 1.0000 NumCandidates: 50, Retention: 1.0000 NumCandidates: 100, Retention: 1.0000 NumCandidates: 200, Retention: 1.0000 NumCandidates: 500, Retention: 1.0000 NumCandidates: 1000, Retention: 1.0000 NumCandidates: 5000, Retention: 1.0000 Top-K: 50 NumCandidates: 50, Retention: 0.8125 NumCandidates: 100, Retention: 0.8750 NumCandidates: 200, Retention: 0.8750 NumCandidates: 500, Retention: 1.0000 NumCandidates: 1000, Retention: 1.0000 NumCandidates: 5000, Retention: 1.0000 Top-K: 100 NumCandidates: 100, Retention: 0.7586 NumCandidates: 200, Retention: 0.7241 NumCandidates: 500, Retention: 0.9655 NumCandidates: 1000, Retention: 0.9655 NumCandidates: 5000, Retention: 1.0000 _scalar_ Embedding: Top-K: 5 NumCandidates: 25, Retention: 0.2500 NumCandidates: 50, Retention: 0.5000 NumCandidates: 100, Retention: 0.7500 NumCandidates: 200, Retention: 1.0000 NumCandidates: 500, Retention: 1.0000 NumCandidates: 1000, Retention: 1.0000 NumCandidates: 5000, Retention: 1.0000 Top-K: 10 NumCandidates: 25, Retention: 0.4000 NumCandidates: 50, Retention: 0.6000 NumCandidates: 100, Retention: 0.8000 NumCandidates: 200, Retention: 1.0000 NumCandidates: 500, Retention: 1.0000 NumCandidates: 1000, Retention: 1.0000 NumCandidates: 5000, Retention: 1.0000 Top-K: 50 NumCandidates: 50, Retention: 0.7500 NumCandidates: 100, Retention: 0.8125 NumCandidates: 200, Retention: 0.8750 NumCandidates: 500, Retention: 0.9375 NumCandidates: 1000, Retention: 0.9375 NumCandidates: 5000, Retention: 1.0000 Top-K: 100 NumCandidates: 100, Retention: 0.8276 NumCandidates: 200, Retention: 0.8276 NumCandidates: 500, Retention: 0.8621 NumCandidates: 1000, Retention: 0.8966 NumCandidates: 5000, Retention: 0.9310 _binary_ Embedding: Top-K: 5 NumCandidates: 25, Retention: 0.2500 NumCandidates: 50, Retention: 0.2500 NumCandidates: 100, Retention: 0.7500 NumCandidates: 200, Retention: 0.7500 NumCandidates: 500, Retention: 1.0000 NumCandidates: 1000, Retention: 1.0000 NumCandidates: 5000, Retention: 1.0000 Top-K: 10 NumCandidates: 25, Retention: 0.2000 NumCandidates: 50, Retention: 0.2000 NumCandidates: 100, Retention: 0.8000 NumCandidates: 200, Retention: 0.8000 NumCandidates: 500, Retention: 1.0000 NumCandidates: 1000, Retention: 1.0000 NumCandidates: 5000, Retention: 1.0000 Top-K: 50 NumCandidates: 50, Retention: 0.2500 NumCandidates: 100, Retention: 0.5625 NumCandidates: 200, Retention: 0.6250 NumCandidates: 500, Retention: 0.7500 NumCandidates: 1000, Retention: 0.8125 NumCandidates: 5000, Retention: 1.0000 Top-K: 100 NumCandidates: 100, Retention: 0.4483 NumCandidates: 200, Retention: 0.5517 NumCandidates: 500, Retention: 0.7586 NumCandidates: 1000, Retention: 0.8621 NumCandidates: 5000, Retention: 1.0000 呼び出し結果は、3 つの埋め込みタイプ全体で異なるパフォーマンス パターンを示します。
スカラー量子化では確実な改善が見られ、K 値が高いほど強力な検索精度が得られます。バイナリ量化は、より低い値から開始されますが、Top-K 50 および 100 で改善されるため、計算効率と再現率のトレードオフが考えられます。浮動小数点数32 埋め込みは最も強力な初期パフォーマンスを示し、Top-K 50 および 100 でスカラー量子化と同じ最大再現率に達します。
これは、float32 がより低いTop-K値でより優れた再現率を提供すると同時に、スカラー量子化が高いTop-K値で同等のパフォーマンスを実現し、 計算効率の向上を提供できることを示しています。バイナリ量子化は、再現率の上限が低いにもかかわらず、メモリと計算の制約が最大の呼び出し精度の必要性を超えるシナリオでは価値があるかもしれません。