Ricerca ibrida con Milvus
In questa esercitazione dimostreremo come condurre una ricerca ibrida con Milvus e il modello BGE-M3. Il modello BGE-M3 può convertire il testo in vettori densi e radi. Milvus supporta la memorizzazione di entrambi i tipi di vettori in un'unica raccolta, consentendo una ricerca ibrida che migliora la rilevanza dei risultati.
Milvus supporta metodi di recupero densi, radi e ibridi:
- Recupero denso: Utilizza il contesto semantico per comprendere il significato delle query.
- Recupero sparso: Enfatizza la corrispondenza delle parole chiave per trovare risultati basati su termini specifici, equivalente alla ricerca full-text.
- Recupero ibrido: Combina gli approcci Dense e Sparse, catturando il contesto completo e le parole chiave specifiche per ottenere risultati di ricerca completi.
Integrando questi metodi, la ricerca ibrida di Milvus bilancia le somiglianze semantiche e lessicali, migliorando la rilevanza complessiva dei risultati della ricerca. Questo quaderno illustra il processo di impostazione e utilizzo di queste strategie di reperimento, evidenziandone l'efficacia in vari scenari di ricerca.
Dipendenze e ambiente
$ pip install --upgrade pymilvus "pymilvus[model]"
Scaricare il set di dati
Per dimostrare la ricerca, abbiamo bisogno di un corpus di documenti. Utilizziamo il dataset Quora Duplicate Questions e collochiamolo nella directory locale.
Fonte del dataset: First Quora Dataset Release: Coppie di domande
# Run this cell to download the dataset
$ wget http://qim.fs.quoracdn.net/quora_duplicate_questions.tsv
Caricare e preparare i dati
Carichiamo il dataset e prepariamo un piccolo corpus per la ricerca.
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?
Utilizzare il modello BGE-M3 per le incorporazioni
Il modello BGE-M3 può incorporare i testi come vettori densi e radi.
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]
Impostazione della raccolta e dell'indice Milvus
Si imposterà la raccolta Milvus e si creeranno gli indici per i campi vettoriali.
- Impostare l'uri come file locale, ad esempio "./milvus.db", è il metodo più conveniente, poiché utilizza automaticamente Milvus Lite per memorizzare tutti i dati in questo file.
- Se si dispone di una grande quantità di dati, ad esempio più di un milione di vettori, è possibile configurare un server Milvus più performante su Docker o Kubernetes. In questa configurazione, utilizzare l'uri del server, ad esempio http://localhost:19530, come uri.
- Se si desidera utilizzare Zilliz Cloud, il servizio cloud completamente gestito per Milvus, regolare l'uri e il token, che corrispondono all'endpoint pubblico e alla chiave API di 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()
Inserire i dati nella raccolta Milvus
Inserire i documenti e i loro embedding nella collezione.
# 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
Inserire la query di ricerca
# 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?
Eseguire la ricerca
Per prima cosa prepareremo alcune funzioni utili per eseguire la ricerca:
dense_search
: ricerca solo nel campo vettoriale densosparse_search
: ricerca solo nel campo vettoriale radohybrid_search
: ricerca in entrambi i campi vettoriali e densi con un reranker ponderato
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]
Eseguiamo tre diverse ricerche con le funzioni definite:
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,
)
Visualizzare i risultati della ricerca
Per visualizzare i risultati delle ricerche Dense, Sparse e Ibride, abbiamo bisogno di alcune utility per formattare i risultati.
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
Quindi possiamo visualizzare i risultati della ricerca in testo con evidenziazioni:
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))
Risultati della ricerca densa:
Qual è il modo migliore per iniziare a imparare la robotica?
Come si impara un linguaggio informatico come java?
Come posso iniziare a imparare la sicurezza informatica?
Che cos'è la programmazione Java? Come imparare il linguaggio di programmazione Java?
Come posso imparare la sicurezza informatica?
Qual è il modo migliore per iniziare la robotica? Qual è la migliore scheda di sviluppo su cui posso iniziare a lavorare?
Come posso imparare a parlare inglese in modo fluente?
Quali sono i modi migliori per imparare il francese?
Come si può rendere la fisica facile da imparare?
Come ci si prepara per l'UPSC?
Risultati della ricerca sparsa:
Cos'è la programmazione Java? Come imparare il linguaggio di programmazione Java?
Qual è il modo migliore per iniziare a imparare la robotica?
Qual è l'alternativa all' apprendimento automatico?
Come si crea un nuovo terminale e una nuova shell in Linux utilizzando la programmazione C?
Come si crea una nuova shell in un nuovo terminale utilizzando la programmazione C (terminale Linux)?
Quale attività è meglio avviare a Hyderabad?
Quale attività è meglio avviare a Hyderabad?
Qual è il modo migliore per iniziare la robotica? Qual è la migliore scheda di sviluppo che posso iniziare a lavorare su di essa?
Di quale matematica ha bisogno un principiante per capire gli algoritmi di programmazione? Quali libri sugli algoritmi sono adatti per un principiante completo?
Come fare in modo che la vita si adatti a te e impedisca alla vita di abusare di te mentalmente ed emotivamente?
Risultati della ricerca ibrida:
Qual è il modo migliore per iniziare la robotica? Qual è la migliore scheda di sviluppo su cui posso iniziare a lavorare?
Che cos'è la programmazione Java? Come imparare il linguaggio di programmazione Java?
Qual è il modo migliore per iniziare a imparare la robotica?
Come ci si prepara per l'UPSC?
Come si può rendere la fisica facile da imparare?
Quali sono i modi migliori per imparare il francese?
Come posso imparare a parlare fluentemente l'inglese?
Come posso imparare la sicurezza informatica?
Come posso iniziare a imparare la sicurezza informatica?
Come si impara un linguaggio informatico come java?
Qual è l'alternativa all' apprendimento automatico?
Come si crea un nuovo terminale e una nuova shell in Linux utilizzando la programmazione C?
Come si crea una nuova shell in un nuovo terminale utilizzando la programmazione C (terminale Linux)?
Quale attività è meglio avviare a Hyderabad?
Quale attività è meglio avviare a Hyderabad?
Di quale matematica ha bisogno un principiante per capire gli algoritmi della programmazione informatica? Quali libri sugli algoritmi sono adatti a un principiante assoluto?
Come fare in modo che la vita si adatti a voi e impedisca alla vita di abusare di voi mentalmente ed emotivamente?
Distribuzione rapida
Per sapere come avviare una demo online con questo tutorial, fate riferimento all 'applicazione di esempio.