milvus-logo
LFAI
フロントページへ
  • チュートリアル

Milvusを使ったハイブリッド検索

Open In Colab GitHub Repository

このチュートリアルでは、Milvusと BGE-M3モデルを使ったハイブリッド検索の方法を説明します。BGE-M3モデルはテキストを密なベクトルと疎なベクトルに変換することができます。Milvusは1つのコレクションに両方のタイプのベクトルを格納することをサポートし、結果の関連性を高めるハイブリッド検索を可能にします。

Milvusは密検索、疎検索、ハイブリッド検索をサポートしています:

  • 密検索:クエリの背後にある意味を理解するためにセマンティックコンテキストを利用します。
  • スパース検索:全文検索に相当する、特定の用語に基づいた結果を見つけるためのテキストマッチングに重点を置く。
  • ハイブリッド検索:DenseとSparseの両アプローチを組み合わせ、包括的な検索結果のために完全な文脈と特定のキーワードを捕捉する。

Milvusハイブリッド検索は、これらの手法を統合することで、意味的な類似性と語彙的な類似性のバランスをとり、検索結果の全体的な関連性を向上させます。このノートブックでは、これらの検索ストラテジーのセットアップと使用方法を説明し、様々な検索シナリオにおける有効性を強調します。

依存関係と環境

$ pip install --upgrade pymilvus "pymilvus[model]"

データセットのダウンロード

検索を実証するには、文書のコーパスが必要だ。Quora Duplicate Questionsデータセットを使い、ローカルディレクトリに置いてみよう。

データセットのソース最初のQuoraデータセットリリース:質問ペア

# Run this cell to download the dataset
$ wget http://qim.fs.quoracdn.net/quora_duplicate_questions.tsv

データのロードと準備

データセットをロードし、検索用の小さなコーパスを準備する。

import pandas as pd

file_path = "quora_duplicate_questions.tsv"
df = pd.read_csv(file_path, sep="\t")
questions = set()
for _, row in df.iterrows():
    obj = row.to_dict()
    questions.add(obj["question1"][:512])
    questions.add(obj["question2"][:512])
    if len(questions) > 500:  # Skip this if you want to use the full dataset
        break

docs = list(questions)

# example question
print(docs[0])
What is the strongest Kevlar cord?

埋め込みにBGE-M3モデルを使う

BGE-M3モデルはテキストを密なベクトルと疎なベクトルとして埋め込むことができる。

from milvus_model.hybrid import BGEM3EmbeddingFunction

ef = BGEM3EmbeddingFunction(use_fp16=False, device="cpu")
dense_dim = ef.dim["dense"]

# Generate embeddings using BGE-M3 model
docs_embeddings = ef(docs)
Fetching 30 files: 100%|██████████| 30/30 [00:00<00:00, 302473.85it/s]
Inference Embeddings: 100%|██████████| 32/32 [01:59<00:00,  3.74s/it]

Milvusコレクションとインデックスのセットアップ

Milvusコレクションをセットアップし、ベクトルフィールドのインデックスを作成する。

  • uriをローカルファイル、例えば"./milvus.db "に設定するのが最も便利である。
  • 100万ベクトルを超えるような大規模なデータをお持ちの場合は、DockerやKubernetes上でよりパフォーマンスの高いMilvusサーバを構築することができます。このセットアップでは、uriとしてサーバのuri、例えば.http://localhost:19530 を使用してください。
  • MilvusのフルマネージドクラウドサービスであるZilliz Cloudを利用する場合は、Zilliz CloudのPublic EndpointとAPI Keyに対応するuriとtokenを調整してください。
from pymilvus import (
    connections,
    utility,
    FieldSchema,
    CollectionSchema,
    DataType,
    Collection,
)

# Connect to Milvus given URI
connections.connect(uri="./milvus.db")

# Specify the data schema for the new Collection
fields = [
    # Use auto generated id as primary key
    FieldSchema(
        name="pk", dtype=DataType.VARCHAR, is_primary=True, auto_id=True, max_length=100
    ),
    # Store the original text to retrieve based on semantically distance
    FieldSchema(name="text", dtype=DataType.VARCHAR, max_length=512),
    # Milvus now supports both sparse and dense vectors,
    # we can store each in a separate field to conduct hybrid search on both vectors
    FieldSchema(name="sparse_vector", dtype=DataType.SPARSE_FLOAT_VECTOR),
    FieldSchema(name="dense_vector", dtype=DataType.FLOAT_VECTOR, dim=dense_dim),
]
schema = CollectionSchema(fields)

# Create collection (drop the old one if exists)
col_name = "hybrid_demo"
if utility.has_collection(col_name):
    Collection(col_name).drop()
col = Collection(col_name, schema, consistency_level="Strong")

# To make vector search efficient, we need to create indices for the vector fields
sparse_index = {"index_type": "SPARSE_INVERTED_INDEX", "metric_type": "IP"}
col.create_index("sparse_vector", sparse_index)
dense_index = {"index_type": "AUTOINDEX", "metric_type": "IP"}
col.create_index("dense_vector", dense_index)
col.load()

Milvusコレクションへのデータ挿入

ドキュメントとその埋め込みデータをコレクションに挿入します。

# For efficiency, we insert 50 records in each small batch
for i in range(0, len(docs), 50):
    batched_entities = [
        docs[i : i + 50],
        docs_embeddings["sparse"][i : i + 50],
        docs_embeddings["dense"][i : i + 50],
    ]
    col.insert(batched_entities)
print("Number of entities inserted:", col.num_entities)
Number of entities inserted: 502

検索クエリの入力

# Enter your search query
query = input("Enter your search query: ")
print(query)

# Generate embeddings for the query
query_embeddings = ef([query])
# print(query_embeddings)
How to start learning programming?

まず、検索に役立つ関数を用意します:

  • dense_search密なベクトルフィールドの検索のみ
  • sparse_search疎なベクトル場のみを検索
  • hybrid_search密なベクトル場と疎なベクトル場の両方を重み付きリランカーで検索する。
from pymilvus import (
    AnnSearchRequest,
    WeightedRanker,
)


def dense_search(col, query_dense_embedding, limit=10):
    search_params = {"metric_type": "IP", "params": {}}
    res = col.search(
        [query_dense_embedding],
        anns_field="dense_vector",
        limit=limit,
        output_fields=["text"],
        param=search_params,
    )[0]
    return [hit.get("text") for hit in res]


def sparse_search(col, query_sparse_embedding, limit=10):
    search_params = {
        "metric_type": "IP",
        "params": {},
    }
    res = col.search(
        [query_sparse_embedding],
        anns_field="sparse_vector",
        limit=limit,
        output_fields=["text"],
        param=search_params,
    )[0]
    return [hit.get("text") for hit in res]


def hybrid_search(
    col,
    query_dense_embedding,
    query_sparse_embedding,
    sparse_weight=1.0,
    dense_weight=1.0,
    limit=10,
):
    dense_search_params = {"metric_type": "IP", "params": {}}
    dense_req = AnnSearchRequest(
        [query_dense_embedding], "dense_vector", dense_search_params, limit=limit
    )
    sparse_search_params = {"metric_type": "IP", "params": {}}
    sparse_req = AnnSearchRequest(
        [query_sparse_embedding], "sparse_vector", sparse_search_params, limit=limit
    )
    rerank = WeightedRanker(sparse_weight, dense_weight)
    res = col.hybrid_search(
        [sparse_req, dense_req], rerank=rerank, limit=limit, output_fields=["text"]
    )[0]
    return [hit.get("text") for hit in res]

定義された関数を使って3種類の検索を実行してみよう:

dense_results = dense_search(col, query_embeddings["dense"][0])
sparse_results = sparse_search(col, query_embeddings["sparse"]._getrow(0))
hybrid_results = hybrid_search(
    col,
    query_embeddings["dense"][0],
    query_embeddings["sparse"]._getrow(0),
    sparse_weight=0.7,
    dense_weight=1.0,
)

検索結果の表示

密検索、疎検索、ハイブリッド検索の結果を表示するには、結果をフォーマットするユーティリティが必要だ。

def doc_text_formatting(ef, query, docs):
    tokenizer = ef.model.tokenizer
    query_tokens_ids = tokenizer.encode(query, return_offsets_mapping=True)
    query_tokens = tokenizer.convert_ids_to_tokens(query_tokens_ids)
    formatted_texts = []

    for doc in docs:
        ldx = 0
        landmarks = []
        encoding = tokenizer.encode_plus(doc, return_offsets_mapping=True)
        tokens = tokenizer.convert_ids_to_tokens(encoding["input_ids"])[1:-1]
        offsets = encoding["offset_mapping"][1:-1]
        for token, (start, end) in zip(tokens, offsets):
            if token in query_tokens:
                if len(landmarks) != 0 and start == landmarks[-1]:
                    landmarks[-1] = end
                else:
                    landmarks.append(start)
                    landmarks.append(end)
        close = False
        formatted_text = ""
        for i, c in enumerate(doc):
            if ldx == len(landmarks):
                pass
            elif i == landmarks[ldx]:
                if close:
                    formatted_text += "</span>"
                else:
                    formatted_text += "<span style='color:red'>"
                close = not close
                ldx = ldx + 1
            formatted_text += c
        if close is True:
            formatted_text += "</span>"
        formatted_texts.append(formatted_text)
    return formatted_texts

そうすれば、検索結果をテキストでハイライト表示することができる:

from IPython.display import Markdown, display

# Dense search results
display(Markdown("**Dense Search Results:**"))
formatted_results = doc_text_formatting(ef, query, dense_results)
for result in dense_results:
    display(Markdown(result))

# Sparse search results
display(Markdown("\n**Sparse Search Results:**"))
formatted_results = doc_text_formatting(ef, query, sparse_results)
for result in formatted_results:
    display(Markdown(result))

# Hybrid search results
display(Markdown("\n**Hybrid Search Results:**"))
formatted_results = doc_text_formatting(ef, query, hybrid_results)
for result in formatted_results:
    display(Markdown(result))

密な検索結果

ロボット工学の学習を始めるのに最も良い方法は?

javaのようなコンピュータ言語を学ぶにはどうしたらいいですか?

情報セキュリティの学習はどのように始めればいいですか?

Javaプログラミングとは何ですか?Javaプログラミング言語を学ぶには?

コンピュータ・セキュリティはどのように学べばいいですか?

ロボット工学を始めるのに最適な方法は何ですか?ロボット製作を始めるのに最適な開発ボードはどれですか?

英語を流暢に話せるようになるには?

フランス語を学ぶにはどのような方法がありますか?

物理を簡単に学ぶにはどうしたらいいですか?

UPSCの準備はどのようにすればいいですか?

疎な検索結果

Java プログラミングとは ですか?Javaプログラミング言語を 学ぶには

ロボット工学を 学び始めるのに最適な方法は

機械 学習に代わるものは何 ですか?

C プログラミングを使ってLinuxで新しいターミナルと新しいシェルを作成するには

C プログラミングを使用して、新しいターミナルで新しいシェルを作成する方法を教えてください(Linuxターミナル)

ハイデラバードで 起業するのに適したビジネスはどれですか?

ハイデラバードで 起業するのに適したビジネスはどれですか?

ロボット工学を 始めるのに最適な方法は何ですか?私が作業を 開始できる最適な開発ボードはどれですか?

全くの初心者がコンピュータ・ プログラミングのアルゴリズムを理解するにはどのような数学が必要ですか全くの初心者に適したアルゴリズムに関する本は何ですか?

人生を自分に合ったものにし、精神的・感情的に虐待れないようにするには

ハイブリッドの検索結果

ロボット工学を 始めるのに最適な方法は開発ボードはどれがいいですか

Java プログラミングとは ですか?Javaプログラミング言語を 学ぶには

ロボット工学を 学ぶ最良の方法は何ですか

どのようにUPSCの準備をするのですか?

物理を 簡単に学ぶには

フランス語を学ぶ最善の 方法はですか?

どうすれば英語を流暢に話せる ようになりますか?

コンピュータ・セキュリティを学ぶにはどうしたらいいですか?

情報 セキュリティを学ぶにはどうしたらいいですか?

Javaのようなコンピュータ言語はどのように学べばいいですか?

機械 学習に代わるものは何 ですか?

LinuxでC言語を使って新しいターミナルとシェルを作成するにはどうすれば いいですか?

C プログラミングを使用して、新しいターミナルに新しいシェルを作成する方法を教えてください

ハイデラバードで 起業するのに適したビジネスはどれですか?

ハイデラバードで 起業するのに適したビジネスはどれですか?

全くの初心者がコンピュータ プログラミングのアルゴリズムを理解する ために必要な数学は何 ですか?全くの初心者に適したアルゴリズムに関する本は何ですか?

人生を自分に合ったものにし、精神的・感情的に虐待れないようにするにはどうしたらいいですか?

クイックデプロイ

このチュートリアルでオンライン・デモを始める方法については、アプリケーションの例を参照してください。

翻訳DeepL

Try Managed Milvus for Free

Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.

Get Started
フィードバック

このページは役に立ちましたか ?