Гибридный поиск с помощью Milvus
В этом уроке мы покажем, как проводить гибридный поиск с помощью Milvus и модели BGE-M3. Модель BGE-M3 может преобразовывать текст в плотные и разреженные векторы. Milvus поддерживает хранение обоих типов векторов в одной коллекции, что позволяет осуществлять гибридный поиск, повышающий релевантность результатов.
Milvus поддерживает плотный, разреженный и гибридный методы поиска:
- Плотный поиск: Использует семантический контекст для понимания смысла запросов.
- Разрозненный поиск: Упор делается на сопоставление текста для поиска результатов по определенным терминам, что эквивалентно полнотекстовому поиску.
- Гибридный поиск: Комбинирует плотный и разреженный подходы, захватывая полный контекст и конкретные ключевые слова для получения исчерпывающих результатов поиска.
Интегрируя эти методы, гибридный поиск Milvus уравновешивает семантическое и лексическое сходство, улучшая общую релевантность результатов поиска. В этом блокноте мы рассмотрим процесс настройки и использования этих стратегий поиска, подчеркнем их эффективность в различных сценариях поиска.
Зависимости и окружение
$ pip install --upgrade pymilvus "pymilvus[model]"
Загрузить набор данных
Для демонстрации поиска нам нужен корпус документов. Давайте воспользуемся набором данных Quora Duplicate Questions и поместим его в локальную директорию.
Источник набора данных: First Quora Dataset Release: Question Pairs
# Run this cell to download the dataset
$ wget http://qim.fs.quoracdn.net/quora_duplicate_questions.tsv
Загрузка и подготовка данных
Мы загрузим набор данных и подготовим небольшой корпус для поиска.
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?
Использование модели BGE-M3 для вкраплений
Модель BGE-M3 может встраивать тексты в виде плотных и разреженных векторов.
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]
Настройка коллекции и индекса Milvus
Мы настроим коллекцию Milvus и создадим индексы для векторных полей.
- Задание uri локального файла, например "./milvus.db", является наиболее удобным методом, поскольку он автоматически использует Milvus Lite для хранения всех данных в этом файле.
- Если у вас большой объем данных, скажем, более миллиона векторов, вы можете настроить более производительный сервер Milvus на Docker или Kubernetes. В этом случае в качестве uri используйте ури сервера, например http://localhost:19530.
- Если вы хотите использовать Zilliz Cloud, полностью управляемый облачный сервис для Milvus, настройте uri и token, которые соответствуют публичной конечной точке и ключу API в 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()
Вставка данных в коллекцию Milvus
Вставьте документы и их вкрапления в коллекцию.
# 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
Введите поисковый запрос
# 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?
Запуск поиска
Сначала мы подготовим несколько полезных функций для запуска поиска:
dense_search
: только поиск по плотному векторному полюsparse_search
: поиск только по разреженному векторному полюhybrid_search
: поиск по плотному и векторному полю с взвешенным реранкером
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]
Запустим три разных поиска с заданными функциями:
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,
)
Отображение результатов поиска
Чтобы отобразить результаты плотного, разреженного и гибридного поиска, нам понадобятся утилиты для форматирования результатов.
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
Затем мы можем отобразить результаты поиска в виде текста с выделением:
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))
Результаты плотного поиска:
Как лучше всего начать изучать робототехнику?
Как выучить такой компьютерный язык, как java?
Как начать изучать информационную безопасность?
Что такое программирование на Java? Как выучить язык программирования Java?
Как выучить компьютерную безопасность?
Как лучше всего начать заниматься робототехникой? Какая самая лучшая плата для разработки, чтобы я мог начать работать на ней?
Как научиться свободно говорить по-английски?
Каковы лучшие способы выучить французский язык?
Как сделать физику легкой для изучения?
Как подготовиться к UPSC?
Результаты разреженного поиска:
Что такое программирование на Java? Как выучить язык программирования Java?
С чего лучше всего начать изучение робототехники?
Что является альтернативой машинному обучению?
Как создать новый терминал и новую оболочку в Linux с помощью программирования на C?
Как создать новую оболочку в новом терминале с помощью программирования на C (терминал Linux)?
Какой бизнес лучше начать в Хайдарабаде?
Какой бизнес лучше начать в Хайдарабаде?
Как лучше всего начать заниматься робототехникой? Какая самая лучшая плата для разработки, чтобы я мог начать работать на ней?
Какая математика нужна новичку для понимания алгоритмов компьютерного программирования? Какие книги по алгоритмам подходят для новичков?
Как сделать так, чтобы жизнь устраивала вас и не позволяла издеваться над вами психически и эмоционально?
Результаты поиска по запросу Гибрид:
Как лучше всего начать заниматься робототехникой? Какая плата лучше для разработки, чтобы я мог начать работать на ней?
Что такое программирование на Java? Как выучить язык программирования Java?
Как лучше всего начать изучать робототехнику?
Как подготовиться к UPSC?
Как сделать физику легкой для изучения?
Каковы лучшие способы изучения французского языка?
Как научиться свободно говорить по-английски?
Как научиться компьютерной безопасности?
Как начать изучать информационную безопасность?
Как выучить такой компьютерный язык, как java?
Что является альтернативой машинному обучению?
Как создать новый терминал и новую оболочку в Linux с помощью программирования на C?
Как создать новую оболочку в новом терминале с помощью программирования на C (терминал Linux)?
Какой бизнес лучше начать в Хайдарабаде?
Какой бизнес лучше начать в Хайдарабаде?
Какая математика нужна новичку для понимания алгоритмов компьютерного программирования? Какие книги по алгоритмам подходят для начинающих?
Как сделать так, чтобы жизнь вас устраивала, и не дать жизни издеваться над вами психически и эмоционально?
Быстрое развертывание
Чтобы узнать, как запустить онлайн-демонстрацию с помощью этого учебника, пожалуйста, обратитесь к примеру приложения.