Introducción a la búsqueda híbrida semántica/de texto completo con Milvus 2.5
En este artículo, le mostraremos cómo poner en marcha rápidamente la nueva función de búsqueda de texto completo y combinarla con la búsqueda semántica convencional basada en incrustaciones vectoriales.
Requisitos
En primer lugar, asegúrese de haber instalado Milvus 2.5:
pip install -U pymilvus[model]
y tener una instancia en ejecución de Milvus Standalone (por ejemplo, en su máquina local) siguiendo las instrucciones de instalación de la documentación de Milvus.
Construir el esquema de datos y los índices de búsqueda
Importamos las clases y funciones necesarias:
from pymilvus import MilvusClient, DataType, Function, FunctionType, model
Puede que haya notado dos nuevas entradas para Milvus 2.5, Function
y FunctionType
, que explicaremos en breve.
A continuación abrimos la base de datos con Milvus Standalone, es decir, localmente, y creamos el esquema de datos. El esquema comprende una clave primaria entera, una cadena de texto, un vector denso de dimensión 384, y un vector disperso (de dimensionalidad ilimitada). Observe que Milvus Lite no soporta actualmente la búsqueda de texto completo, sólo Milvus Standalone y 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}
Puede que haya notado el parámetro enable_analyzer=True
. Esto indica a Milvus 2.5 que active el analizador léxico en este campo y construya una lista de tokens y frecuencias de tokens, que son necesarios para la búsqueda de texto completo. El campo sparse
contendrá una representación vectorial de la documentación como una bolsa de palabras producida a partir del análisis text
.
Pero, ¿cómo conectamos los campos text
y sparse
e indicamos a Milvus cómo debe calcularse sparse
a partir de text
? Aquí es donde tenemos que invocar el objeto Function
y añadirlo al 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': {}}]}
La abstracción del objeto Function
es más general que la de aplicar la búsqueda de texto completo. En el futuro, podrá utilizarse para otros casos en los que un campo deba ser función de otro. En nuestro caso, especificamos que sparse
es una función de text
a través de la función FunctionType.BM25
. BM25
se refiere a una métrica común en la recuperación de información utilizada para calcular la similitud de una consulta con un documento (en relación con una colección de documentos).
Utilizamos el modelo de incrustación predeterminado en Milvus, que es paraphrase-albert-small-v2:
embedding_fn = model.DefaultEmbeddingFunction()
El siguiente paso es añadir nuestros índices de búsqueda. Tenemos uno para el vector denso y otro para el vector disperso. El tipo de índice es SPARSE_INVERTED_INDEX
con BM25
ya que la búsqueda de texto completo requiere un método de búsqueda diferente al de los vectores densos estándar.
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 último, creamos nuestra colección:
client.drop_collection('demo')
client.list_collections()
[]
client.create_collection(
collection_name='demo',
schema=schema,
index_params=index_params
)
client.list_collections()
['demo']
Y ya tenemos una base de datos vacía preparada para aceptar documentos de texto y realizar búsquedas semánticas y de texto completo.
Insertar datos y realizar búsquedas de texto completo
La inserción de datos no difiere de las versiones anteriores de 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}
Ilustremos primero una búsqueda de texto completo antes de pasar a la búsqueda 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
)
El parámetro de búsqueda drop_ratio_search
se refiere a la proporción de documentos con puntuación más baja que deben descartarse durante el algoritmo de búsqueda.
Veamos los 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.'}}
Búsqueda híbrida semántica y de texto completo
Combinemos ahora lo que hemos aprendido para realizar una búsqueda híbrida que combine búsquedas semánticas y de texto completo por separado 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.'}}
Como habrá notado, esto no difiere de una búsqueda híbrida con dos campos semánticos separados (disponible desde Milvus 2.4). Los resultados son idénticos a los de la búsqueda de texto completo en este sencillo ejemplo, pero para bases de datos más grandes y búsquedas específicas por palabra clave, la búsqueda híbrida suele tener una mayor recuperación.
Resumen
Ahora ya dispone de todos los conocimientos necesarios para realizar búsquedas de texto completo e híbridas semánticas/de texto completo con Milvus 2.5. Consulte los siguientes artículos para obtener más información sobre cómo funciona la búsqueda de texto completo y por qué es complementaria a la búsqueda semántica:
- Requisitos
- Construir el esquema de datos y los índices de búsqueda
- Insertar datos y realizar búsquedas de texto completo
- Búsqueda híbrida semántica y de texto completo
- Resumen
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