본문 바로가기
WIKI 기술 지식 베이스

Milvus 하이브리드 검색 (Hybrid Search with Milvus)

원문 보기 위키 갱신

이 튜토리얼의 최종 효과를 직접 경험하고 싶다면 https://demos.milvus.io/hybrid-search/ 로 바로 이동할 수 있어요.

이 튜토리얼에서는 Milvus와 BGE-M3 모델로 하이브리드 검색을 수행하는 방법을 시연해요. BGE-M3 모델은 텍스트를 밀집(dense) 및 스파스(sparse) 벡터로 변환할 수 있어요. Milvus는 두 유형의 벡터를 한 컬렉션에 저장하는 것을 지원해 결과 관련성을 높이는 하이브리드 검색을 가능하게 해요.

Milvus는 Dense, Sparse, Hybrid 검색 방법을 지원해요.

  • Dense Retrieval: 시맨틱 컨텍스트를 활용해 쿼리 뒤에 있는 의미를 이해해요.
  • Sparse Retrieval: 키워드 매칭을 강조해 특정 용어를 기반으로 결과를 찾아요. 전문 검색과 동등해요.
  • Hybrid Retrieval: Dense와 Sparse 접근 방식을 모두 결합해 전체 컨텍스트와 특정 키워드를 포착해 포괄적인 검색 결과를 제공해요.

이 방법들을 통합함으로써 Milvus 하이브리드 검색은 시맨틱 및 어휘 유사성을 균형 있게 맞춰 검색 결과의 전반적인 관련성을 향상시켜요. 이 노트북은 이러한 검색 전략을 설정하고 사용하는 과정을 안내하며 다양한 검색 시나리오에서의 효과를 강조해요.

출처: Milvus 문서

본문

의존성과 환경 (Dependencies and Environment)

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

데이터셋 다운로드 (Download Dataset)

검색을 시연하려면 문서 말뭉치(corpus)가 필요해요. Quora Duplicate Questions 데이터셋을 사용해 로컬 디렉토리에 배치해요.

데이터셋 출처: First Quora Dataset Release: Question Pairs

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

데이터 로드 및 준비 (Load and Prepare Data)

데이터셋을 로드하고 검색용으로 작은 말뭉치를 준비해요.

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 모델 사용 (Use BGE-M3 Model for Embeddings)

BGE-M3 모델은 텍스트를 밀집 및 스파스 벡터로 임베딩할 수 있어요.

from pymilvus.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 컬렉션과 인덱스 설정 (Setup Milvus Collection and Index)

Milvus 컬렉션을 설정하고 벡터 필드용 인덱스를 만들어요.

  • uri를 로컬 파일(예: ./milvus.db)로 설정하는 것이 가장 편리한 방법이에요. Milvus Lite를 자동으로 사용해 모든 데이터를 이 파일에 저장하기 때문이에요.
  • 백만 개 이상의 벡터처럼 대규모 데이터가 있다면 Docker 또는 Kubernetes에 더 성능이 좋은 Milvus 서버를 설정할 수 있어요. 이 경우 서버 uri(예: http://localhost:19530)를 uri로 사용하세요.
  • 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="Bounded")

# 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 컬렉션에 데이터 삽입 (Insert Data into Milvus Collection)

문서와 임베딩을 컬렉션에 삽입해요.

# 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)

# 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: 가중 재순위화기(weighted reranker)로 밀집·벡터 필드 모두에서 검색해요.
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]

정의된 함수들로 세 가지 다른 검색을 실행해 보아요.

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

검색 결과 표시 (Display Search Results)

Dense, Sparse, Hybrid 검색의 결과를 표시하려면 결과 형식을 지정할 몇 가지 유틸리티가 필요해요.

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))

Dense Search Results:

What's the best way to start learning robotics?

How do I learn a computer language like java?

How can I get started to learn information security?

What is Java programming? How To Learn Java Programming Language ?

How can I learn computer security?

What is the best way to start robotics? Which is the best development board that I can start working on it?

How can I learn to speak English fluently?

What are the best ways to learn French?

How can you make physics easy to learn?

How do we prepare for UPSC?

Sparse Search Results:

What is Java programming? How To Learn Java Programming Language ?

What's the best way to start learning robotics?

What is the alternative to machine learning?

How do I create a new Terminal and new shell in Linux using C programming?

How do I create a new shell in a new terminal using C programming (Linux terminal)?

Which business is better to start in Hyderabad?

Which business is good start up in Hyderabad?

What is the best way to start robotics? Which is the best development board that I can start working on it?

What math does a complete newbie need to understand algorithms for computer programming? What books on algorithms are suitable for a complete beginner?

How do you make life suit you and stop life from abusing you mentally and emotionally?

Hybrid Search Results:

What is the best way to start robotics? Which is the best development board that I can start working on it?

What is Java programming? How To Learn Java Programming Language ?

What's the best way to start learning robotics?

How do we prepare for UPSC?

How can you make physics easy to learn?

What are the best ways to learn French?

How can I learn to speak English fluently?

How can I learn computer security?

How can I get started to learn information security?

How do I learn a computer language like java?

What is the alternative to machine learning?

How do I create a new Terminal and new shell in Linux using C programming?

How do I create a new shell in a new terminal using C programming (Linux terminal)?

Which business is better to start in Hyderabad?

Which business is good start up in Hyderabad?

What math does a complete newbie need to understand algorithms for computer programming? What books on algorithms are suitable for a complete beginner?

How do you make life suit you and stop life from abusing you mentally and emotionally?

빠른 배포 (Quick Deploy)

이 튜토리얼로 온라인 데모를 시작하는 방법을 배우려면 the example application을 참고하세요.

더 알아보기 (Learn more)