Учебник: семантический поиск с развернутой моделью
- Для наиболее простого выполнения семантического поиска в Elastic Stack, обратитесь к
semantic_textпошаговому учебнику. - Этот учебник был написан до появления точки входа вывода и
semantic_textтипа поля. Сегодня у нас есть более простые варианты для выполнения семантического поиска.
Это руководство покажет вам, как реализовать семантический поиск с моделями, развернутыми в Elasticsearch: от выбора модели НЛП до написания запросов.
Выберите модель НЛП
Elasticsearch предлагает использование широкого спектра моделей НЛП, включая как плотные, так и разреженные векторные модели. Ваш выбор языковой модели имеет решающее значение для успешной реализации семантического поиска.
Хотя можно использовать свою собственную модель встраивания текста, достижение хороших результатов поиска за счёт настройки модели является сложной задачей. Выбор подходящей модели из нашего списка моделей сторонних разработчиков — первый шаг. Обучение модели на собственных данных важно для обеспечения лучших результатов поиска, чем использование только BM25. Однако процесс обучения модели требует команды специалистов по данным и экспертов в области машинного обучения, что делает его дорогостоящим и длительным.
Для решения этой проблемы Elastic предоставляет предварительно обученную модель представления под названием Elastic Learned Sparse EncodeR (ELSER). ELSER, доступный на данный момент только для английского языка, представляет собой разреженную векторную модель вне области, которая не требует дообучения. Эта адаптивность делает её подходящей для различных задач НЛП «из коробки». Если у вас нет команды специалистов по машинному обучению, настоятельно рекомендуется использовать модель ELSER.
В случае разреженного векторного представления векторы в основном состоят из нулевых значений, при этом только небольшая подборка содержит ненулевые значения. Это представление обычно используется для текстовых данных. В случае ELSER каждый документ в индексе и сам текст запроса представлены многомерными разреженными векторами. Каждый ненулевой элемент вектора соответствует термину в словаре модели. Словарь ELSER содержит около 30000 терминов, поэтому разреженные векторы, созданные ELSER, содержат около 30000 значений, большинство из которых равны нулю. По сути, модель ELSER заменяет термины в исходном запросе другими терминами, которые были выучены для существования в документах, наилучшим образом соответствующих исходным терминам поиска в наборе обучающих данных, и весами, которые контролируют важность каждого.
Разверните модель
После того, как вы определились с моделью, которую хотите использовать для реализации семантического поиска, необходимо развернуть модель в Elasticsearch.
Для развертывания ELSER, см. Загрузка и развертывание ELSER.
Для развертывания модели встраивания текста сторонних разработчиков, см. Развертывание модели встраивания текста.
Сопоставьте поле для векторных представлений текста
Прежде чем начать использовать развернутую модель для генерации встраиваний на основе входного текста, необходимо сначала подготовить отображение вашего индекса. Отображение индекса зависит от типа модели.
ELSER генерирует пары "токен-вес" в качестве результата для входного текста и запроса. Тип поля Elasticsearch sparse_vector может хранить эти пары "токен-вес" в виде числовых векторных признаков. Индекс должен иметь поле типа sparse_vector для индексирования токенов, сгенерированных ELSER.
Для создания отображения для вашего индекса ELSER, обратитесь к разделу создания отображения индекса учебника. Пример демонстрирует, как создать отображение индекса для my-index, определяя поле my_embeddings.tokens (которое будет содержать выход ELSER) как поле sparse_vector.
resp = client.indices.create(
index="my-index",
mappings={
"properties": {
"my_tokens": {
"type": "sparse_vector"
},
"my_text_field": {
"type": "text"
}
}
},
)
print(resp) response = client.indices.create(
index: 'my-index',
body: {
mappings: {
properties: {
my_tokens: {
type: 'sparse_vector'
},
my_text_field: {
type: 'text'
}
}
}
}
)
puts response const response = await client.indices.create({
index: "my-index",
mappings: {
properties: {
my_tokens: {
type: "sparse_vector",
},
my_text_field: {
type: "text",
},
},
},
});
console.log(response); PUT my-index
{
"mappings": {
"properties": {
"my_tokens": {
"type": "sparse_vector"
},
"my_text_field": {
"type": "text"
}
}
}
} | Имя поля, которое будет содержать токены, сгенерированные ELSER. | |
| Поле, содержащее токены, должно быть полем типа | |
| Имя поля, из которого создать представление разреженного вектора. В этом примере имя поля — | |
| Тип поля — |
Совместимые с Elasticsearch модели НЛП генерируют плотные векторы в качестве вывода. Тип поля dense_vector подходит для хранения плотных векторов числовых значений. Индекс должен иметь поле типа dense_vector для индексирования встраиваний, которые генерирует поддерживаемая модель сторонних разработчиков, которую вы выбрали. Имейте в виду, что модель генерирует встраивания с определённым числом измерений. Поле dense_vector должно быть настроено с тем же количеством измерений, используя параметр dims. Справку о количестве измерений встраиваний смотрите в документации соответствующей модели.
Чтобы просмотреть отображение индекса для модели НЛП, обратитесь к фрагменту кода отображения в разделе добавления модели встраивания текста в конвейер индексирования учебника. Пример демонстрирует, как создать отображение индекса, определяя поле my_embeddings.predicted_value (которое будет содержать выход модели) как поле dense_vector.
resp = client.indices.create(
index="my-index",
mappings={
"properties": {
"my_embeddings.predicted_value": {
"type": "dense_vector",
"dims": 384
},
"my_text_field": {
"type": "text"
}
}
},
)
print(resp) response = client.indices.create(
index: 'my-index',
body: {
mappings: {
properties: {
'my_embeddings.predicted_value' => {
type: 'dense_vector',
dims: 384
},
my_text_field: {
type: 'text'
}
}
}
}
)
puts response const response = await client.indices.create({
index: "my-index",
mappings: {
properties: {
"my_embeddings.predicted_value": {
type: "dense_vector",
dims: 384,
},
my_text_field: {
type: "text",
},
},
},
});
console.log(response); PUT my-index
{
"mappings": {
"properties": {
"my_embeddings.predicted_value": {
"type": "dense_vector",
"dims": 384
},
"my_text_field": {
"type": "text"
}
}
}
} | Имя поля, которое будет содержать встраивания, сгенерированные моделью. | |
| Поле, содержащее встраивания, должно быть полем типа | |
| Модель генерирует встраивания с определённым числом измерений. Поле | |
| Имя поля, из которого создать представление плотного вектора. В этом примере имя поля — | |
| Тип поля — |
Сгенерировать векторные представления текста
После создания отображений для индекса вы можете сгенерировать векторные представления текста из входного текста. Это можно сделать, используя конвейер индексирования с процессором вывода inference. Конвейер индексирования обрабатывает входные данные и индексирует их в целевой индекс. Во время индексирования процессор вывода inference использует обученную модель для вывода по индексируемым данным. После создания конвейера индексирования с процессором вывода вы можете внести ваши данные в него, чтобы получить результат модели.
Вот как создается конвейер индексирования, использующий модель ELSER:
resp = client.ingest.put_pipeline(
id="my-text-embeddings-pipeline",
description="Text embedding pipeline",
processors=[
{
"inference": {
"model_id": ".elser_model_2",
"input_output": [
{
"input_field": "my_text_field",
"output_field": "my_tokens"
}
]
}
}
],
)
print(resp) response = client.ingest.put_pipeline(
id: 'my-text-embeddings-pipeline',
body: {
description: 'Text embedding pipeline',
processors: [
{
inference: {
model_id: '.elser_model_2',
input_output: [
{
input_field: 'my_text_field',
output_field: 'my_tokens'
}
]
}
}
]
}
)
puts response const response = await client.ingest.putPipeline({
id: "my-text-embeddings-pipeline",
description: "Text embedding pipeline",
processors: [
{
inference: {
model_id: ".elser_model_2",
input_output: [
{
input_field: "my_text_field",
output_field: "my_tokens",
},
],
},
},
],
});
console.log(response); PUT _ingest/pipeline/my-text-embeddings-pipeline
{
"description": "Text embedding pipeline",
"processors": [
{
"inference": {
"model_id": ".elser_model_2",
"input_output": [
{
"input_field": "my_text_field",
"output_field": "my_tokens"
}
]
}
}
]
} | Объект конфигурации, который определяет параметры |
Для индексирования данных через конвейер для генерации токенов с помощью ELSER, обратитесь к разделу Индексирование данных через конвейер вывода руководства. После успешного индексирования документов с помощью конвейера, ваш индекс будет содержать токены, сгенерированные ELSER. Токены — это усвоенные ассоциации, отражающие релевантность, они не являются синонимами. Чтобы узнать больше о том, что такое токены, обратитесь к этой странице.
Вот как создается конвейер индексирования, использующий модель встраивания текста:
resp = client.ingest.put_pipeline(
id="my-text-embeddings-pipeline",
description="Text embedding pipeline",
processors=[
{
"inference": {
"model_id": "sentence-transformers__msmarco-minilm-l-12-v3",
"target_field": "my_embeddings",
"field_map": {
"my_text_field": "text_field"
}
}
}
],
)
print(resp) response = client.ingest.put_pipeline(
id: 'my-text-embeddings-pipeline',
body: {
description: 'Text embedding pipeline',
processors: [
{
inference: {
model_id: 'sentence-transformers__msmarco-minilm-l-12-v3',
target_field: 'my_embeddings',
field_map: {
my_text_field: 'text_field'
}
}
}
]
}
)
puts response const response = await client.ingest.putPipeline({
id: "my-text-embeddings-pipeline",
description: "Text embedding pipeline",
processors: [
{
inference: {
model_id: "sentence-transformers__msmarco-minilm-l-12-v3",
target_field: "my_embeddings",
field_map: {
my_text_field: "text_field",
},
},
},
],
});
console.log(response); PUT _ingest/pipeline/my-text-embeddings-pipeline
{
"description": "Text embedding pipeline",
"processors": [
{
"inference": {
"model_id": "sentence-transformers__msmarco-minilm-l-12-v3",
"target_field": "my_embeddings",
"field_map": {
"my_text_field": "text_field"
}
}
}
]
} | Идентификатор модели встраивания текста, которую вы хотите использовать. | |
| Объект |
Для индексирования данных через конвейер для генерации встраиваний текста с выбранной моделью, обратитесь к разделу Добавление модели встраивания текста в конвейер индексирования вывода. Пример показывает, как создать конвейер с процессором вывода и повторно индексировать ваши данные через конвейер. После успешного индексирования документов с помощью конвейера, ваш индекс будет содержать текстовые встраивания, сгенерированные моделью.
Теперь пришло время выполнить поиск по смыслу!
Поиск по данным
В зависимости от типа развернутой модели вы можете запросить ранжированные признаки с помощью запроса разреженного вектора или плотных векторов с помощью поиска kNN.
Встраивания текста ELSER можно запросить с помощью запроса разреженного вектора. Запрос разреженного вектора позволяет запросить поле разреженного вектора, предоставив идентификатор вывода, связанный с моделью NLP, которую вы хотите использовать, и текст запроса:
resp = client.search(
index="my-index",
query={
"sparse_vector": {
"field": "my_tokens",
"inference_id": "my-elser-endpoint",
"query": "the query string"
}
},
)
print(resp) const response = await client.search({
index: "my-index",
query: {
sparse_vector: {
field: "my_tokens",
inference_id: "my-elser-endpoint",
query: "the query string",
},
},
});
console.log(response); GET my-index/_search
{
"query":{
"sparse_vector": {
"field": "my_tokens",
"inference_id": "my-elser-endpoint",
"query": "the query string"
}
}
} Встраивания текста, созданные моделями плотных векторов, можно запросить с помощью поиска kNN. В ключе knn укажите имя поля плотного вектора, а в ключе query_vector_builder укажите идентификатор модели и текст запроса.
resp = client.search(
index="my-index",
knn={
"field": "my_embeddings.predicted_value",
"k": 10,
"num_candidates": 100,
"query_vector_builder": {
"text_embedding": {
"model_id": "sentence-transformers__msmarco-minilm-l-12-v3",
"model_text": "the query string"
}
}
},
)
print(resp) response = client.search(
index: 'my-index',
body: {
knn: {
field: 'my_embeddings.predicted_value',
k: 10,
num_candidates: 100,
query_vector_builder: {
text_embedding: {
model_id: 'sentence-transformers__msmarco-minilm-l-12-v3',
model_text: 'the query string'
}
}
}
}
)
puts response const response = await client.search({
index: "my-index",
knn: {
field: "my_embeddings.predicted_value",
k: 10,
num_candidates: 100,
query_vector_builder: {
text_embedding: {
model_id: "sentence-transformers__msmarco-minilm-l-12-v3",
model_text: "the query string",
},
},
},
});
console.log(response); GET my-index/_search
{
"knn": {
"field": "my_embeddings.predicted_value",
"k": 10,
"num_candidates": 100,
"query_vector_builder": {
"text_embedding": {
"model_id": "sentence-transformers__msmarco-minilm-l-12-v3",
"model_text": "the query string"
}
}
}
} Выход за рамки поиска по смыслу с гибридным поиском
В некоторых ситуациях лексический поиск может работать лучше, чем поиск по смыслу. Например, при поиске отдельных слов или идентификаторов, таких как номера продуктов.
Комбинирование поиска по смыслу и лексического поиска в одном запросе гибридного поиска с использованием взаимного ранжирования слияния обеспечивает наилучшее сочетание обоих подходов. Более того, гибридный поиск с использованием взаимного ранжирования слияния показал лучшие результаты в целом.
Гибридный поиск между запросом по смыслу и лексическим запросом можно получить, используя получатель RRF в качестве части запроса поиска. Предоставьте запрос sparse_vector и полнотекстовый запрос в качестве стандартных получателей для получателя rrf. Получатель rrf использует взаимное ранжирование слияния для ранжирования лучших документов.
resp = client.search(
index="my-index",
retriever={
"rrf": {
"retrievers": [
{
"standard": {
"query": {
"match": {
"my_text_field": "the query string"
}
}
}
},
{
"standard": {
"query": {
"sparse_vector": {
"field": "my_tokens",
"inference_id": "my-elser-endpoint",
"query": "the query string"
}
}
}
}
]
}
},
)
print(resp) const response = await client.search({
index: "my-index",
retriever: {
rrf: {
retrievers: [
{
standard: {
query: {
match: {
my_text_field: "the query string",
},
},
},
},
{
standard: {
query: {
sparse_vector: {
field: "my_tokens",
inference_id: "my-elser-endpoint",
query: "the query string",
},
},
},
},
],
},
},
});
console.log(response); GET my-index/_search
{
"retriever": {
"rrf": {
"retrievers": [
{
"standard": {
"query": {
"match": {
"my_text_field": "the query string"
}
}
}
},
{
"standard": {
"query": {
"sparse_vector": {
"field": "my_tokens",
"inference_id": "my-elser-endpoint",
"query": "the query string"
}
}
}
}
]
}
}
} Гибридный поиск между запросом по смыслу и лексическим запросом можно получить, предоставив:
- получателя
rrfдля ранжирования лучших документов с использованием взаимного ранжирования слияния - получателя
standardв качестве дочернего получателя с ключевым словомqueryдля полнотекстового запроса - получателя
knnв качестве дочернего получателя с поиском kNN, который запрашивает поле плотного вектора
resp = client.search(
index="my-index",
retriever={
"rrf": {
"retrievers": [
{
"standard": {
"query": {
"match": {
"my_text_field": "the query string"
}
}
}
},
{
"knn": {
"field": "text_embedding.predicted_value",
"k": 10,
"num_candidates": 100,
"query_vector_builder": {
"text_embedding": {
"model_id": "sentence-transformers__msmarco-minilm-l-12-v3",
"model_text": "the query string"
}
}
}
}
]
}
},
)
print(resp) const response = await client.search({
index: "my-index",
retriever: {
rrf: {
retrievers: [
{
standard: {
query: {
match: {
my_text_field: "the query string",
},
},
},
},
{
knn: {
field: "text_embedding.predicted_value",
k: 10,
num_candidates: 100,
query_vector_builder: {
text_embedding: {
model_id: "sentence-transformers__msmarco-minilm-l-12-v3",
model_text: "the query string",
},
},
},
},
],
},
},
});
console.log(response); GET my-index/_search
{
"retriever": {
"rrf": {
"retrievers": [
{
"standard": {
"query": {
"match": {
"my_text_field": "the query string"
}
}
}
},
{
"knn": {
"field": "text_embedding.predicted_value",
"k": 10,
"num_candidates": 100,
"query_vector_builder": {
"text_embedding": {
"model_id": "sentence-transformers__msmarco-minilm-l-12-v3",
"model_text": "the query string"
}
}
}
}
]
}
}
}
© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/semantic-search-deployed-nlp-model.html