🚀 免費嘗試 Zilliz Cloud,完全托管的 Milvus,體驗速度提升 10 倍!立即嘗試

milvus-logo
LFAI
主頁
  • 教學
  • Home
  • Docs
  • 教學

  • 使用 Milvus 進行混合搜尋

使用 Milvus 進行混合搜尋

Open In Colab GitHub Repository

在本教程中,我們將示範如何使用MilvusBGE-M3 模型進行混合搜尋。BGE-M3 模型可以將文字轉換為密集向量和稀疏向量。Milvus 支援在一個集合中同時儲存這兩種向量,允許混合搜尋以增強結果的相關性。

Milvus 支援密集、稀疏和混合檢索方法:

  • 密集檢索:利用語意上下文來瞭解查詢背後的意義。
  • 稀疏檢索:強調文字匹配,根據特定詞彙尋找結果,相當於全文檢索。
  • 混合式檢索:結合 Dense 與 Sparse 兩種方法,擷取完整的上下文與特定關鍵字,以獲得全面的搜尋結果。

透過整合這些方法,Milvus Hybrid Search 可平衡語義與詞彙的相似性,改善搜尋結果的整體相關性。本手冊將介紹設定和使用這些檢索策略的過程,並強調它們在各種搜尋情況下的有效性。

依賴與環境

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

下載資料集

為了示範搜尋,我們需要一個文件語料庫。讓我們使用 Quora 重複問題資料集,並將其放置在本機目錄中。

資料集的來源:第一次 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" 是最方便的方法,因為它會自動利用Milvus Lite將所有資料儲存在這個檔案中。
  • 如果您有大規模的資料,例如超過一百萬個向量,您可以在Docker 或 Kubernetes 上架設效能更高的 Milvus 伺服器。在此設定中,請使用伺服器的 uri,例如:http://localhost:19530。
  • 如果您想使用Zilliz Cloud,Milvus 的完全管理雲端服務,請調整 uri 和 token,對應 Zilliz Cloud 的Public Endpoint 和 API key
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]

讓我們用定義的函式執行三種不同的搜尋:

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 中建立新終端和新 shell

如何使用 C 程式語言在新終端(Linux 終端)建立新 shell

在海德拉巴 開設哪家公司比較好?

在海得拉巴 開設哪家公司比較好?

開辦機器人的最佳方式是什麼哪種開發板最適合我 開始工作

一個完全的新手需要哪些 數學來理解電腦 程式設計的演算法哪些有關演算法的書籍適合完全的初學者

如何讓生活適合自己,讓生活不再虐待自己的精神和情緒

混合搜尋結果:

開始學習機器人的最佳方式是什麼哪種開發板最好,我可以 開始工作

什麼是 Java 程式設計?如何學習 Java 程式語言?

開始學習機器人的最佳方式是什麼

如何準備 UPSC

如何讓物理 變得容易學習

學習法語最佳方法是什麼

如何 才能學會說流利的英語

如何學習電腦安全

如何開始 學習資訊安全

如何學習 java 等電腦語言

機器 學習替代方法是什麼?

如何在 Linux 中使用 C 程式建立新的 Terminal 和新的 shell

如何使用 C 程式語言在新的終端(Linux 終端)建立新的 shell

在海德拉巴 開設哪家公司比較好?

在海得拉巴 開設哪家公司比較好?

一個完全的新手需要哪些數學知識 才能理解電腦 程式設計的演算法有哪些有關演算法的書籍適合初學者

如何讓生活適合您,並避免生活在精神上和情緒上虐待

快速部署

若要瞭解如何利用本教學開始線上示範,請參考範例應用程式

免費嘗試托管的 Milvus

Zilliz Cloud 無縫接入,由 Milvus 提供動力,速度提升 10 倍。

開始使用
反饋

這個頁面有幫助嗎?