Introdução à pesquisa híbrida semântica/texto integral com o Milvus 2.5
Neste artigo, vamos mostrar-lhe como começar a utilizar rapidamente a nova funcionalidade de pesquisa de texto integral e combiná-la com a pesquisa semântica convencional baseada em ligações vectoriais.
Requisitos
Em primeiro lugar, certifique-se de que instalou o Milvus 2.5:
pip install -U pymilvus[model]
e ter uma instância em execução do Milvus Standalone (por exemplo, na sua máquina local) usando as instruções de instalação nos documentos do Milvus.
Construir o esquema de dados e os índices de pesquisa
Importamos as classes e funções necessárias:
from pymilvus import MilvusClient, DataType, Function, FunctionType, model
Deve ter notado duas novas entradas para o Milvus 2.5, Function
e FunctionType
, que explicaremos em breve.
De seguida, abrimos a base de dados com o Milvus Standalone, ou seja, localmente, e criamos o esquema de dados. O esquema é composto por uma chave primária inteira, uma string de texto, um vetor denso de dimensão 384, e um vetor esparso (de dimensionalidade ilimitada). Note-se que o Milvus Lite não suporta atualmente a pesquisa de texto completo, apenas o Milvus Standalone e o 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}
Você deve ter notado o parâmetro enable_analyzer=True
. Este parâmetro diz ao Milvus 2.5 para ativar o analisador léxico neste campo e construir uma lista de tokens e frequências de tokens, que são necessárias para a pesquisa de texto integral. O campo sparse
conterá uma representação vetorial da documentação como um saco de palavras produzido a partir da análise text
.
Mas como é que ligamos os campos text
e sparse
e dizemos ao Milvus como é que sparse
deve ser calculado a partir de text
? É aqui que temos de invocar o objeto Function
e adicioná-lo ao esquema:
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': {}}]}
A abstração do objeto Function
é mais geral do que a aplicação da pesquisa de texto integral. No futuro, pode ser utilizado para outros casos em que um campo precisa de ser uma função de outro campo. No nosso caso, especificamos que sparse
é uma função de text
através da função FunctionType.BM25
. BM25
refere-se a uma métrica comum na recuperação de informação utilizada para calcular a semelhança de uma consulta com um documento (relativamente a uma coleção de documentos).
Utilizamos o modelo de incorporação predefinido no Milvus, que é o paraphrase-albert-small-v2:
embedding_fn = model.DefaultEmbeddingFunction()
O passo seguinte é adicionar os nossos índices de pesquisa. Temos um para o vetor denso e um outro para o vetor esparso. O tipo de índice é SPARSE_INVERTED_INDEX
com BM25
, uma vez que a pesquisa em texto integral requer um método de pesquisa diferente do utilizado para os vectores densos normais.
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"
)
Por fim, criamos a nossa coleção:
client.drop_collection('demo')
client.list_collections()
[]
client.create_collection(
collection_name='demo',
schema=schema,
index_params=index_params
)
client.list_collections()
['demo']
E com isso, temos uma base de dados vazia configurada para aceitar documentos de texto e efetuar pesquisas semânticas e de texto integral!
Inserção de dados e pesquisa de texto integral
A inserção de dados não é diferente das versões anteriores do 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}
Vamos primeiro ilustrar uma pesquisa de texto completo antes de passarmos à pesquisa híbrida:
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
)
O parâmetro de pesquisa drop_ratio_search
refere-se à proporção de documentos com pontuação mais baixa a eliminar durante o algoritmo de pesquisa.
Vamos ver os resultados:
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.'}}
Realização de pesquisa híbrida semântica e de texto integral
Vamos agora combinar o que aprendemos para executar uma pesquisa híbrida que combina pesquisas semânticas e de texto completo separadas com um 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.'}}
Como deve ter reparado, isto não é diferente de uma pesquisa híbrida com dois campos semânticos separados (disponível desde o Milvus 2.4). Os resultados são idênticos aos da pesquisa de texto integral neste exemplo simples, mas para bases de dados maiores e pesquisas específicas por palavra-chave, a pesquisa híbrida tem normalmente uma maior capacidade de recuperação.
Resumo
Está agora equipado com todos os conhecimentos necessários para efetuar pesquisas de texto integral e pesquisa híbrida semântica/texto integral com o Milvus 2.5. Consulte os artigos seguintes para obter mais informações sobre como funciona a pesquisa de texto integral e porque é complementar à pesquisa semântica:
- Requisitos
- Construir o esquema de dados e os índices de pesquisa
- Inserção de dados e pesquisa de texto integral
- Realização de pesquisa híbrida semântica e de texto integral
- Resumo
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