Docs Menu
Docs Home
/
Atlas
/ /

投票AI埋め込みを使用した自動量化の実行方法

AIアプリケーションは、多くの場合、コンピューティング、データ、および金銭コストの点で小規模に開始できます。ユーザー コミットの増加によって本番アプリケーションの増やすが大きくなるにつれて、大量のデータの保存や取得に関連するコストなどの主要な要因は、最適化の重要な機会になります。これらの課題は、次の点に焦点を当てて対処できます。

  • 効率的なベクトル検索アルゴリズム

  • 自動量化プロセス

  • 最適化された埋め込み戦略

RAG(検索拡張生成)とエージェントベースのシステムはどちらも、セマンティック類似検索を実行するためにベクトルデータ (画像、ビデオ、テキストなどのデータ オブジェクトの数値表現)に依存しています。 RG またはエージェント駆動型ワークフローを使用するシステムは、高速応答時間を維持し、 取得レイテンシ を最小限に抑え、 インフラストラクチャ コストを制御するために、大規模で高次元のデータセットを効率的に処理する必要があります。

このチュートリアルでは、増やすな 高度なAIワークロードを設計、配置、管理するために必要な手法を取得し、 最適なパフォーマンスとコスト効率 を確保します。

具体的には、このチュートリアルでは、次の方法を学習します。

  • Vyage AI の voyage-3-large は、量子化も認識されている汎用の多言語埋め込みモデルであり、 を使用して埋め込みを生成し、 MongoDBデータベースに取り込みます。

  • 埋め込みを自動的に定量化してデータ型の精度を低くし、メモリ使用量と クエリレイテンシ の両方を最適化します。

  • 浮動小数点数32、int8、バイナリ埋め込みを比較するクエリを実行し、データ型の精度を効率と検索精度で比較します。

  • 量化された埋め込みの再現率(保持とも呼ばれる)を測定し、量子化された ANN 検索が全精度の ENN 検索と同じドキュメントをどの程度効率的に取得するかを評価します。

注意

  • バイナリ数量化はリソース消費の削減を必要とするシナリオに最適ですが、精度の低下に対応するために再スコアリングのパスが必要になる場合があります。

  • スカラー量化は実質的な中間値を提供し、パフォーマンスと精度のバランスが必要なほとんどのユースケースに適しています。

  • 浮動小数:32

Atlas の サンプル データ セット からの映画データを含むコレクションを使用します。

  • High-CPU S20 以上の検索階層を使用する 2 以上の検索ノードを持つ M20 以上の Atlas クラスター

  • VS Code Colas などのインタラクティブPythonノートを実行するための環境。

1
  1. .ipynb 拡張機能のファイルを保存して、インタラクティブPythonノートを作成します。

  2. ライブラリをインストールします。

    このチュートリアルでは、次のライブラリをインポートする必要があります。

    PyMongo

    voyageai

    データの埋め込みを生成するためにAI Pythonクライアントを投票します。

    pandas

    データをロードしてベクトル検索の準備をするためのデータ操作および分析ツール。

    データセット

    準備ができたデータセットへのアクセスを提供する Huge Page ライブラリ。

    matplotlib

    データを可視化するためのプロットと可視化ライブラリ。

    ライブラリをインストールするには、次のコマンドを実行します。

    pip install --quiet -U pymongo voyageai pandas datasets matplotlib
  3. 環境変数を安全に取得して設定します。

    次のset_env_securely ヘルパー関数は、環境変数を安全に取得して設定します。次のコードをコピーして貼り付けて実行し、プロンプトが表示されたら、Vyage AI APIキーや Atlas クラスター接続文字列などのシークレット値を設定します。

    1import getpass
    2import os
    3import voyageai
    4
    5# Function to securely get and set environment variables
    6def set_env_securely(var_name, prompt):
    7 value = getpass.getpass(prompt)
    8 os.environ[var_name] = value
    9
    10# Environment Variables
    11set_env_securely("VOYAGE_API_KEY", "Enter your Voyage API Key: ")
    12set_env_securely("MONGO_URI", "Enter your MongoDB URI: ")
    13MONGO_URI = os.environ.get("MONGO_URI")
    14if not MONGO_URI:
    15 raise ValueError("MONGO_URI not set in environment variables.")
    16
    17# Voyage Client
    18voyage_client = voyageai.Client()
2

このステップでは、次のデータセットから最大 250000 のドキュメントを読み込みます。

  1. データをクラスターにロードするための関数を定義します。

    ノートにある次のコードをコピーして貼り付け、実行します。サンプルコードでは、次の関数を定義しています。

    • generate_bson_vector を使用して、データセットの埋め込みをBSONバイナリ ベクトルに変換し、ベクトルの効率的なストレージと処理を可能にします。

    • get_mongo_client Atlas クラスター接続文字列を取得します。

    • insert_dataframe_into_collection Atlas クラスターにデータを取り込むため。

    1import pandas as pd
    2from datasets import load_dataset
    3from bson.binary import Binary, BinaryVectorDtype
    4import pymongo
    5
    6# Connect to Cluster
    7def 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
    17def 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
    23def 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
    29def 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.")
  2. データをクラスターにロードします。

    ノート PC 内の次のコードをコピーして貼り付け、実行して、データセットを Atlas クラスターにロードします。このコードは、次のアクションを実行します。

    • データセットを取得します。

    • 埋め込みをBSON形式に変換します。

    • Atlas クラスターにコレクションを作成し、データを挿入します。

    1import pandas as pd
    2from bson.binary import Binary, BinaryVectorDtype
    3from pymongo.errors import CollectionInvalid
    4
    5wikipedia_data_df = load_and_prepare_data("MongoDB/wikipedia-22-12-en-voyage-embed", amount=250000)
    6wikipedia_annotation_data_df = load_and_prepare_data("MongoDB/wikipedia-22-12-en-annotation", amount=250000)
    7wikipedia_annotation_data_df.drop(columns=["_id"], inplace=True)
    8
    9# Convert embeddings to BSON format
    10wikipedia_data_df["embedding"] = wikipedia_data_df["embedding"].apply(
    11 lambda x: generate_bson_vector(x, BinaryVectorDtype.FLOAT32)
    12)
    13
    14# MongoDB Setup
    15mongo_client = get_mongo_client(MONGO_URI)
    16DB_NAME = "testing_datasets"
    17db = mongo_client[DB_NAME]
    18
    19collections = {
    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
    25for 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 クラスターに取り込むには、時間がかかる場合があります。

  3. Atlas クラスターにログインし、 Data Explorerでコレクションを視覚的に検査して、データセットが正常にロードされたことを確認します。

3

この手順では、embeddingフィールドに次の 3 つのインデックスを作成します。

スカラー量子インデックス

スカラー量化メソッド を使用して埋め込みを定量化します。

バイナリ量子化インデックス

バイナリ量子化メソッドを使用して埋め込みを定量化します。

Float32 ANN Index

埋め込みを定量化するには、float32 ANN メソッドを使用します。

  1. Atlas ベクトル検索インデックスを作成するための関数を定義します。

    ノートに以下をコピーして貼り付け、実行します。

    1import time
    2from pymongo.operations import SearchIndexModel
    3
    4def 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
  2. インデックスを定義します。

    次のインデックス構成では、異なる数量化戦略が実装されます。

    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
    2vector_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
    14vector_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
    26vector_index_definition_float32_ann = {
    27 "fields": [
    28 {
    29 "type": "vector",
    30 "path": "embedding",
    31 "numDimensions": 1024,
    32 "similarity": "cosine",
    33 }
    34 ]
    35}
  3. setup_vector_search_index 関数を使用して、スカラー、バイナリ、float32 インデックスを作成します。

    1. インデックスのコレクション名とインデックス名を設定します。

      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"
    2. Atlas Vector Search インデックスを作成します。

      1setup_vector_search_index(
      2 wiki_data_collection,
      3 vector_index_definition_scalar_quantized,
      4 vector_search_scalar_quantized_index_name,
      5)
      6setup_vector_search_index(
      7 wiki_data_collection,
      8 vector_index_definition_binary_quantized,
      9 vector_search_binary_quantized_index_name,
      10)
      11setup_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 状態である必要があります。

    3. Atlas クラスターにログインし、Atlas Search のインデックスを視覚的に検査して、インデックスの作成が成功したことを確認します。

4

このコードでは、次の関数を定義します。

  • 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 検索を実行するフラグ。

    注意

    Annuse_full_precision False検索の場合、 値はデフォルトで に設定されます。use_full_precision TrueEXN 検索を実行するには、 の値を に設定します。

    具体的には、この関数は次のアクションを実行します。

    • クエリテキストの埋め込みを生成する

    • $vectorSearchステージを構築します

    • 検索のタイプを設定します

    • 返すコレクション内のフィールドを指定します

    • パフォーマンス統計を収集した後にパイプラインを実行する

    • 結果を返します

1def 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
9def 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}
5

次のクエリは、さまざまな量化戦略にわたってベクトル検索を実行し、スカラー量化、バイナリ量子化、および完全精度(float32)ベクトルのパフォーマンス メトリクスを測定すると同時に、各精度レベルでレイテンシ測定値を取得し、分析比較用に結果形式を標準化します。クエリ文字列「最大出力の生成数を増やすにはどうすればよいですか」には、Voyage AIを使用して生成された埋め込みを使用します。

クエリは、精度レベル(スカラー、バイナリ、浮動小数点数32)、結果セットのサイズ(top_k)、レイテンシ(ミリ秒単位)、検索されたドキュメントの内容など、主要なパフォーマンス インジケーターを results 変数に保存し、次の包括的なメトリクスを提供します。さまざまな量化戦略にわたって検索パフォーマンスを評価します。

1vector_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
8user_query = "How do I increase my productivity for maximum output"
9test_top_k = 5
10test_num_candidates = 25
11
12# Result is a list of dictionaries with the following headings: precision, top_k, latency_ms, results
13results = []
14
15for 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
41precision = "Float32_ENN"
42vector_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
52results.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
63results_df = pd.DataFrame(results)
64results_df.columns = ["precision", "top_k", "num_candidates", "latency_ms", "results"]
65
66# To display the results:
67results_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操作は量子化された対応と比較してかなり多くの計算時間が必要になることを示しています。

6

次のクエリは、さまざまな精度レベルと検索スケールにわたってベクトル検索のパフォーマンスを評価するシステム的なレイテンシ測定フレームワークを導入しています。パラメーターtop-k numCandidatesは返される結果の数を決定するだけでなく、MongoDB の HNSWグラフ検索の パラメーターも設定します。

numCandidates値は、DNS 検索中に Atlas ベクトル検索 が探索する HNSWグラフ内のノードの数に影響します。ここで、値が大きいほど、真の最近傍が見つかる可能性は高くなりますが、計算時間がより長く必要になります。

  1. latency_ms を人間が判読できる形式に形式する関数を定義します。

    1from datetime import timedelta
    2
    3def 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"
  2. ベクトル検索クエリーのレイテンシを測定するための 関数を定義します。

    次の関数は、user_querycollectionvector_search_index_nameuse_full_precision 値、top_k_values 値、num_candidates_values 値を入力として受け取り、ベクトル検索の結果を返します。ここで、次の点に注意してください。

    • ベクトル検索操作ではより多くのドキュメントが使用され、検索に時間がかかるため、top_knum_candidates の値が増加するとレイテンシが増加します。

    • 完全忠実度検索(use_full_precision=True )のレイテンシは、全精度の浮動小数点数を使用してデータセット全体を検索するため、近似検索( )よりも時間がかかるためです。use_full_precision=False32

    • 量子化検索のレイテンシは、近似検索と量子化ベクトルを使用するため、完全フィルター検索よりも低くなります。

    1def 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
  3. Atlas ベクトル検索クエリを実行して、レイテンシを測定します。

    レイテンシ評価操作は、すべての数量化戦略にわたる検索を実行し、複数の結果セット サイズをテストし、標準化されたパフォーマンス メトリクスをキャプチャし、比較分析のための結果を集計することで、包括的なパフォーマンス分析を実行し、さまざまな構成と検索負荷下でのベクトル検索の動作を詳細に評価できるようにします。

    1# Run the measurements
    2user_query = "How do I increase my productivity for maximum output"
    3top_k_values = [5, 10, 50, 100]
    4num_candidates_values = [25, 50, 100, 200, 500, 1000, 2000, 5000, 10000]
    5
    6latency_results = []
    7
    8for 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
    21latency_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
    33all_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 操作は最も遅くなりますが、最高の精度の結果を提供します。

  4. さまざまなTop-k値に対して検索レイテンシをプロットします。

    1import matplotlib.pyplot as plt
    2
    3# Map your precision field to the labels and colors you want in the legend
    4precision_label_map = {
    5 "_scalar_": "scalar",
    6 "_binary_": "binary",
    7 "_float32_ann": "float32_ann",
    8 "_float32_ENN": "float32_ENN",
    9}
    10
    11precision_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
    19all_measurements = [m for precision_list in latency_results for m in precision_list]
    20unique_topk = sorted(set(m["top_k"] for m in all_measurements))
    21
    22# For each top_k, create a separate plot
    23for 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でどのように実行レイテンシれるかを示す次のレイテンシ チャートを返します。

7

次のクエリは、Atlas ベクトル検索 が基礎データセットから関連ドキュメントをどの程度効率的に検索するかを測定します。これは、フィールド 真実の関連ドキュメントの総数 (Found/ Total) に対する関連ドキュメントの総数に対する比率として計算されます。例、クエリにフィールド 真実の関連ドキュメントが 5 あり、Atlas ベクトル検索 がそれらの 4 を見つけた場合、再現率は 0.8 または 80% になります。

  1. ベクトル検索操作の表現キャパシティーと保持を測定する関数を定義します。この関数は、次の処理を行います。

    1. 完全な精度の浮動小数点数32 ベクトルと ENN 検索を使用してベースライン検索を作成します。

    2. 量子化ベクトルと ANN 検索を使用して量子化検索を作成します。

    3. ベースライン検索と比較した量化された検索の保持を計算します。

    量子化検索では、保持を適切な範囲内に維持する必要があります。表現キャパシティーが低い場合、ベクトル検索操作ではクエリのセマンティック意味をキャプチャできず、結果が正確ではない可能性があります。これは、量化が効果的でなく、使用された初期埋め込みモデルが量子化プロセスに効果的でないことを示します。量化を認識する 埋め込みモデルを利用することをお勧めします。つまり、訓練プロセス中に、モデルは特別に最適化され、量子化後もセマンティック プロパティを維持する埋め込みを生成します。

    1def 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):
    9retention_results = {"per_query_retention": {}}
    10overall_retention = {} # overall_retention[top_k][num_candidates] = [list of retention values]
    11
    12# Initialize overall retention structure
    13for 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.
    21precision_name = quantized_index_name.split("vector_index")[1]
    22precision_name = precision_name.replace("quantized", "").capitalize()
    23retention_results["precision_name"] = precision_name
    24retention_results["top_k_values"] = top_k_values
    25retention_results["num_candidates_values"] = num_candidates_values
    26
    27# Load ground truth annotations
    28ground_truth_annotations = list(
    29 ground_truth_collection.find().limit(num_queries_to_test)
    30)
    31print(f"Loaded {len(ground_truth_annotations)} ground truth annotations")
    32
    33# Process each ground truth annotation
    34for 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
    114avg_overall_retention = {}
    115for 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
    127retention_results["average_retention"] = avg_overall_retention
    128return retention_results
  2. Atlas ベクトル検索インデックスのパフォーマンスを評価して比較します。

    1overall_recall_results = []
    2top_k_values = [5, 10, 50, 100]
    3num_candidates_values = [25, 50, 100, 200, 500, 1000, 5000]
    4num_queries_to_test = 1
    5
    6for 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% のみが保持されることを意味します。

  3. さまざまな精度の型の保持機能をプロットします。

    1import matplotlib.pyplot as plt
    2
    3# Define colors and labels for each precision type
    4precision_colors = {"_scalar_": "orange", "_binary_": "red", "_float32_": "green"}
    5
    6if 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_annscalarbinary 埋め込みの場合、コードは次のような詳細な平均保持結果も返します。

    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値で同等のパフォーマンスを実現し、 計算効率の向上を提供できることを示しています。バイナリ量子化は、再現率の上限が低いにもかかわらず、メモリと計算の制約が最大の呼び出し精度の必要性を超えるシナリオでは価値があるかもしれません。

戻る

ローカル RAG