milvus-logo
LFAI
Home
  • Tutoriels

Recherche hybride avec Milvus

Open In Colab GitHub Repository

Dans ce tutoriel, nous allons montrer comment effectuer une recherche hybride avec Milvus et le modèle BGE-M3. Le modèle BGE-M3 peut convertir le texte en vecteurs denses et épars. Milvus prend en charge le stockage des deux types de vecteurs dans une collection, ce qui permet d'effectuer une recherche hybride qui améliore la pertinence des résultats.

Milvus prend en charge les méthodes de recherche denses, éparses et hybrides :

  • Recherche dense : Utilise le contexte sémantique pour comprendre le sens des requêtes.
  • Récupération éparse : Elle met l'accent sur la correspondance des mots-clés pour trouver des résultats basés sur des termes spécifiques, ce qui équivaut à une recherche en texte intégral.
  • Recherche hybride : Combine les approches denses et éparses, en capturant le contexte complet et les mots-clés spécifiques pour obtenir des résultats de recherche complets.

En intégrant ces méthodes, la recherche hybride Milvus équilibre les similitudes sémantiques et lexicales, améliorant ainsi la pertinence globale des résultats de la recherche. Ce bloc-notes décrit le processus de mise en place et d'utilisation de ces stratégies de recherche, en soulignant leur efficacité dans divers scénarios de recherche.

Dépendances et environnement

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

Télécharger l'ensemble de données

Pour démontrer la recherche, nous avons besoin d'un corpus de documents. Utilisons l'ensemble de données Quora Duplicate Questions et plaçons-le dans le répertoire local.

Source du jeu de données : Première version du jeu de données Quora : Question Pairs

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

Chargement et préparation des données

Nous allons charger l'ensemble de données et préparer un petit corpus pour la recherche.

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?

Utiliser le modèle BGE-M3 pour les embeddings

Le modèle BGE-M3 peut intégrer des textes sous forme de vecteurs denses et épars.

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]

Configuration de la collection et de l'index Milvus

Nous allons configurer la collection Milvus et créer des index pour les champs de vecteurs.

  • Définir l'uri comme un fichier local, par exemple "./milvus.db", est la méthode la plus pratique, car elle utilise automatiquement Milvus Lite pour stocker toutes les données dans ce fichier.
  • Si vous disposez de données à grande échelle, par exemple plus d'un million de vecteurs, vous pouvez configurer un serveur Milvus plus performant sur Docker ou Kubernetes. Dans cette configuration, veuillez utiliser l'uri du serveur, par exemple http://localhost:19530, comme uri.
  • Si vous souhaitez utiliser Zilliz Cloud, le service en nuage entièrement géré pour Milvus, ajustez l'uri et le token, qui correspondent au point final public et à la clé API dans Zilliz Cloud.
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()

Insérer des données dans la collection Milvus

Insérer des documents et leurs embeddings dans la 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

Saisissez votre requête de recherche

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

Nous allons d'abord préparer quelques fonctions utiles pour exécuter la recherche :

  • dense_search: recherche uniquement dans un champ vectoriel dense
  • sparse_search: recherche uniquement dans un champ vectoriel peu dense
  • hybrid_searchrecherche sur les champs denses et vectoriels avec un reranker pondéré
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]

Exécutons trois recherches différentes avec les fonctions définies :

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

Afficher les résultats de la recherche

Pour afficher les résultats des recherches denses, éparses et hybrides, nous avons besoin de quelques utilitaires pour formater les résultats.

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

Nous pouvons ensuite afficher les résultats de la recherche sous forme de texte avec des mises en évidence :

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

Résultats de la recherche dense :

Quelle est la meilleure façon de commencer à apprendre la robotique ?

Comment apprendre un langage informatique comme Java ?

Comment puis-je commencer à apprendre la sécurité de l'information ?

Qu'est-ce que la programmation Java ? Comment apprendre le langage de programmation Java ?

Comment apprendre la sécurité informatique ?

Quelle est la meilleure façon de commencer la robotique ? Quelle est la meilleure carte de développement sur laquelle je peux commencer à travailler ?

Comment apprendre à parler couramment l'anglais ?

Quelles sont les meilleures façons d'apprendre le français ?

Comment rendre la physique facile à apprendre ?

Comment se préparer à l'UPSC ?

Résultats de recherche épars :

Qu'est-ce que la programmation Java? Comment apprendre le langage de programmation Java ?

Quelle est la meilleure façon de commencer à apprendre la robotique?

Quelle est l'alternative à l' apprentissage automatique?

Comment créer un nouveau terminal et un nouvel interpréteur de commandes sous Linux en utilisant la programmation C?

Comment créer un nouveau shell dans un nouveau terminal en utilisant la programmation C (terminal Linux)?

Quelle est la meilleure entreprise à démarrer à Hyderabad?

Quel est le meilleur moyen de démarrer une entreprise à Hyderabad?

Quelle est la meilleure façon de commencer la robotique? Quelle est la meilleure carte de développement pour que je puisse commencer à travailler dessus?

Quelle est la meilleure façon de commencer à travailler en robotique? Quels sont les livres sur les algorithmes qui conviennent à un débutant complet?

Comment faire pour que la vie vous convienne et que la vie cesse de vous maltraiter mentalement et émotionnellement?

Résultats de la recherche hybride :

Quelle est la meilleure façon de commencer la robotique? Quelle est la meilleure carte de développement pour que je puisse commencer à travailler dessus?

Qu'est-ce que la programmation Java? Comment apprendre le langage de programmation Java ?

Quelle est la meilleure façon de commencer à apprendre la robotique?

Comment se préparer à l'UPSC?

Comment rendre la physique facile à apprendre?

Quelles sont les meilleures façons d 'apprendre le français?

Comment apprendre à parler couramment l'anglais?

Comment apprendre la sécurité informatique?

Comment puis-je commencer à apprendre la sécurité de l'information?

Comment apprendre un langage informatique comme java?

Quelle est l'alternative à l' apprentissage automatique?

Comment créer un nouveau terminal et un nouvel interpréteur de commandes sous Linux en utilisant la programmation C?

Comment créer un nouveau shell dans un nouveau terminal en utilisant la programmation C (terminal Linux)?

Quelle est la meilleure entreprise à démarrer à Hyderabad?

Quelle est la meilleure façon de démarrer une entreprise à Hyderabad?

Quelles sont les mathématiques dont un débutant a besoin pour comprendre les algorithmes de programmation informatique? Quels sont les livres sur les algorithmes qui conviennent à un débutant complet?

Comment faire en sorte que la vie vous convienne et qu'elle cesse de vous maltraiter mentalement et émotionnellement?

Déploiement rapide

Pour savoir comment démarrer une démonstration en ligne avec ce tutoriel, veuillez vous référer à l 'exemple d'application.

Traduit parDeepL

Try Managed Milvus for Free

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

Get Started
Feedback

Cette page a-t - elle été utile ?