Come iniziare con la ricerca semantica ibrida / full text con Milvus 2.5
In questo articolo vi mostreremo come utilizzare rapidamente la nuova funzione di ricerca full-text e come combinarla con la ricerca semantica convenzionale basata su embeddings vettoriali.
Requisiti
Per prima cosa, assicuratevi di aver installato Milvus 2.5:
pip install -U pymilvus[model]
e avere un'istanza funzionante di Milvus Standalone (ad esempio, sul computer locale) utilizzando le istruzioni di installazione contenute nei documenti di Milvus.
Creare lo schema dei dati e gli indici di ricerca
Importiamo le classi e le funzioni necessarie:
from pymilvus import MilvusClient, DataType, Function, FunctionType, model
Avrete notato due nuove voci per Milvus 2.5, Function
e FunctionType
, che spiegheremo tra poco.
Quindi apriamo il database con Milvus Standalone, cioè in locale, e creiamo lo schema dei dati. Lo schema comprende una chiave primaria intera, una stringa di testo, un vettore denso di dimensione 384 e un vettore sparso (di dimensione illimitata). Si noti che Milvus Lite non supporta attualmente la ricerca full-text, ma solo Milvus Standalone e Milvus Distributed.
client = MilvusClient(uri="http://localhost:19530")
schema = client.create_schema()
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True, auto_id=True)
schema.add_field(field_name="text", datatype=DataType.VARCHAR, max_length=1000, enable_analyzer=True)
schema.add_field(field_name="dense", datatype=DataType.FLOAT_VECTOR, dim=768),
schema.add_field(field_name="sparse", datatype=DataType.SPARSE_FLOAT_VECTOR)
{'auto_id': False, 'description': '', 'fields': [{'name': 'id', 'description': '', 'type': <DataType.INT64: 5>, 'is_primary': True, 'auto_id': True}, {'name': 'text', 'description': '', 'type': <DataType.VARCHAR: 21>, 'params': {'max_length': 1000, 'enable_analyzer': True}}, {'name': 'dense', 'description': '', 'type': <DataType.FLOAT_VECTOR: 101>, 'params': {'dim': 768}}, {'name': 'sparse', 'description': '', 'type': <DataType.SPARSE_FLOAT_VECTOR: 104>}], 'enable_dynamic_field': False}
Avrete notato il parametro enable_analyzer=True
. Questo parametro indica a Milvus 2.5 di abilitare il parser lessicale su questo campo e di creare un elenco di token e di frequenze di token, necessari per la ricerca full-text. Il campo sparse
conterrà una rappresentazione vettoriale della documentazione come bag-of-words prodotta dal parsing text
.
Ma come possiamo collegare i campi text
e sparse
e dire a Milvus come sparse
deve essere calcolato da text
? È qui che dobbiamo richiamare l'oggetto Function
e aggiungerlo allo schema:
bm25_function = Function(
name="text_bm25_emb", # Function name
input_field_names=["text"], # Name of the VARCHAR field containing raw text data
output_field_names=["sparse"], # Name of the SPARSE_FLOAT_VECTOR field reserved to store generated embeddings
function_type=FunctionType.BM25,
)
schema.add_function(bm25_function)
{'auto_id': False, 'description': '', 'fields': [{'name': 'id', 'description': '', 'type': <DataType.INT64: 5>, 'is_primary': True, 'auto_id': True}, {'name': 'text', 'description': '', 'type': <DataType.VARCHAR: 21>, 'params': {'max_length': 1000, 'enable_analyzer': True}}, {'name': 'dense', 'description': '', 'type': <DataType.FLOAT_VECTOR: 101>, 'params': {'dim': 768}}, {'name': 'sparse', 'description': '', 'type': <DataType.SPARSE_FLOAT_VECTOR: 104>, 'is_function_output': True}], 'enable_dynamic_field': False, 'functions': [{'name': 'text_bm25_emb', 'description': '', 'type': <FunctionType.BM25: 1>, 'input_field_names': ['text'], 'output_field_names': ['sparse'], 'params': {}}]}
L'astrazione dell'oggetto Function
è più generale rispetto all'applicazione della ricerca full-text. In futuro, potrà essere utilizzato per altri casi in cui un campo deve essere una funzione di un altro campo. Nel nostro caso, specifichiamo che sparse
è una funzione di text
tramite la funzione FunctionType.BM25
. BM25
si riferisce a una metrica comune nel reperimento di informazioni, utilizzata per calcolare la somiglianza di una query con un documento (rispetto a un insieme di documenti).
Utilizziamo il modello di incorporamento predefinito di Milvus, che è parafrasi-alberto-piccolo-v2:
embedding_fn = model.DefaultEmbeddingFunction()
Il passo successivo è aggiungere i nostri indici di ricerca. Ne abbiamo uno per il vettore denso e uno separato per il vettore rado. Il tipo di indice è SPARSE_INVERTED_INDEX
con BM25
, poiché la ricerca full-text richiede un metodo di ricerca diverso da quello dei vettori densi standard.
index_params = client.prepare_index_params()
index_params.add_index(
field_name="dense",
index_type="AUTOINDEX",
metric_type="COSINE"
)
index_params.add_index(
field_name="sparse",
index_type="SPARSE_INVERTED_INDEX",
metric_type="BM25"
)
Infine, creiamo la nostra collezione:
client.drop_collection('demo')
client.list_collections()
[]
client.create_collection(
collection_name='demo',
schema=schema,
index_params=index_params
)
client.list_collections()
['demo']
E con ciò, abbiamo un database vuoto impostato per accettare documenti di testo ed eseguire ricerche semantiche e full-text!
Inserimento dei dati ed esecuzione della ricerca full-text
L'inserimento dei dati non è diverso dalle versioni precedenti di Milvus:
docs = [
'information retrieval is a field of study.',
'information retrieval focuses on finding relevant information in large datasets.',
'data mining and information retrieval overlap in research.'
]
embeddings = embedding_fn(docs)
client.insert('demo', [
{'text': doc, 'dense': vec} for doc, vec in zip(docs, embeddings)
])
{'insert_count': 3, 'ids': [454387371651630485, 454387371651630486, 454387371651630487], 'cost': 0}
Illustriamo innanzitutto una ricerca full-text prima di passare alla ricerca ibrida:
search_params = {
'params': {'drop_ratio_search': 0.2},
}
results = client.search(
collection_name='demo',
data=['whats the focus of information retrieval?'],
output_fields=['text'],
anns_field='sparse',
limit=3,
search_params=search_params
)
Il parametro di ricerca drop_ratio_search
si riferisce alla percentuale di documenti con punteggio inferiore da eliminare durante l'algoritmo di ricerca.
Vediamo i risultati:
for hit in results[0]:
print(hit)
{'id': 454387371651630485, 'distance': 1.3352930545806885, 'entity': {'text': 'information retrieval is a field of study.'}}
{'id': 454387371651630486, 'distance': 0.29726022481918335, 'entity': {'text': 'information retrieval focuses on finding relevant information in large datasets.'}}
{'id': 454387371651630487, 'distance': 0.2715056240558624, 'entity': {'text': 'data mining and information retrieval overlap in research.'}}
Esecuzione di una ricerca ibrida semantica e full-text
Combiniamo ora quanto appreso per eseguire una ricerca ibrida che combina ricerche semantiche e full-text separate con un reranker:
from pymilvus import AnnSearchRequest, RRFRanker
query = 'whats the focus of information retrieval?'
query_dense_vector = embedding_fn([query])
search_param_1 = {
"data": query_dense_vector,
"anns_field": "dense",
"param": {
"metric_type": "COSINE",
},
"limit": 3
}
request_1 = AnnSearchRequest(**search_param_1)
search_param_2 = {
"data": [query],
"anns_field": "sparse",
"param": {
"metric_type": "BM25",
"params": {"drop_ratio_build": 0.0}
},
"limit": 3
}
request_2 = AnnSearchRequest(**search_param_2)
reqs = [request_1, request_2]
ranker = RRFRanker()
res = client.hybrid_search(
collection_name="demo",
output_fields=['text'],
reqs=reqs,
ranker=ranker,
limit=3
)
for hit in res[0]:
print(hit)
{'id': 454387371651630485, 'distance': 0.032786883413791656, 'entity': {'text': 'information retrieval is a field of study.'}}
{'id': 454387371651630486, 'distance': 0.032258063554763794, 'entity': {'text': 'information retrieval focuses on finding relevant information in large datasets.'}}
{'id': 454387371651630487, 'distance': 0.0317460335791111, 'entity': {'text': 'data mining and information retrieval overlap in research.'}}
Come avrete notato, non è diversa da una ricerca ibrida con due campi semantici separati (disponibile da Milvus 2.4). I risultati sono identici a quelli della ricerca full-text in questo semplice esempio, ma per database più grandi e ricerche specifiche per parole chiave la ricerca ibrida ha in genere un richiamo più elevato.
Riassunto
Ora avete tutte le conoscenze necessarie per eseguire ricerche full-text e ibride semantiche/full-text con Milvus 2.5. Per ulteriori informazioni su come funziona la ricerca full-text e perché è complementare alla ricerca semantica, consultare i seguenti articoli:
- Requisiti
- Creare lo schema dei dati e gli indici di ricerca
- Inserimento dei dati ed esecuzione della ricerca full-text
- Esecuzione di una ricerca ibrida semantica e full-text
- Riassunto
On This Page
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word