Spec-Zone.ru › Elasticsearch 8
›Elasticsearch Руководство [8.17] ›Поиск данных ›Семантический поиск

Учебное пособие: семантический поиск с ELSER

Elastic Learned Sparse EncodeR — или ELSER — это модель NLP, обученная Elastic, которая позволяет выполнять семантический поиск, используя разреженное векторное представление. Вместо буквального соответствия поисковым запросам, семантический поиск извлекает результаты, основанные на намерениях и контекстуальном значении поискового запроса.

Инструкции в этом учебнике показывают, как использовать ELSER для выполнения семантического поиска по вашим данным.

Для самого простого способа выполнения семантического поиска в Elastic Stack, обратитесь к semantic_text пошаговому руководству.

При семантическом поиске с помощью ELSER учитываются только первые 512 извлеченных токенов на поле. Дополнительную информацию см. на этой странице.

Требования

Для выполнения семантического поиска с помощью ELSER у вас должна быть развернута модель NLP в вашем кластере. Обратитесь к документации ELSER, чтобы узнать, как загрузить и развернуть модель.

Минимальный размер выделенного узла ML для развертывания и использования модели ELSER составляет 4 ГБ в Elasticsearch Service, если автомасштабирование развертывания выключено. Рекомендуется включить автомасштабирование, поскольку оно позволяет вашему развертыванию динамически регулировать ресурсы в зависимости от спроса. Более высокую производительность можно достичь, используя больше выделений или больше потоков на выделение, что требует больших узлов ML. Автомасштабирование предоставляет более крупные узлы при необходимости. Если автомасштабирование выключено, вы должны сами предоставить узлы соответствующего размера.

Создайте сопоставление индекса

Сначала необходимо создать сопоставление целевого индекса — индекса, который содержит токены, созданные моделью на основе вашего текста. Целевой индекс должен иметь поле с sparse_vector или rank_features типом поля для индексирования вывода ELSER.

Вывод ELSER должен быть обработан в поле с типом поля sparse_vector или rank_features. В противном случае Elasticsearch интерпретирует пары «токен-вес» как большое количество полей в документе. Если вы получаете ошибку, похожую на эту: "Limit of total fields [1000] has been exceeded while adding new fields", значит поле вывода ELSER не правильно сопоставлено и имеет тип поля, отличный от sparse_vector или rank_features.

resp = client.indices.create(
    index="my-index",
    mappings={
        "properties": {
            "content_embedding": {
                "type": "sparse_vector"
            },
            "content": {
                "type": "text"
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'my-index',
  body: {
    mappings: {
      properties: {
        content_embedding: {
          type: 'sparse_vector'
        },
        content: {
          type: 'text'
        }
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "my-index",
  mappings: {
    properties: {
      content_embedding: {
        type: "sparse_vector",
      },
      content: {
        type: "text",
      },
    },
  },
});
console.log(response);
PUT my-index
{
  "mappings": {
    "properties": {
      "content_embedding": { 
        "type": "sparse_vector" 
      },
      "content": { 
        "type": "text" 
      }
    }
  }
}

Имя поля для хранения сгенерированных токенов. Оно должно быть указано в конфигурации конвейера вывода на следующем шаге.

Поле, содержащее токены, является полем типа sparse_vector.

Имя поля, из которого создается разреженное векторное представление. В этом примере имя поля — content. Оно должно быть указано в конфигурации конвейера вывода на следующем шаге.

Тип поля — текст в этом примере.

Чтобы узнать, как оптимизировать занимаемое место, обратитесь к разделу Сохранение дискового пространства путём исключения токенов ELSER из исходного документа.

Создайте конвейер загрузки с процессором вывода

Создайте конвейер загрузки с процессором вывода, чтобы использовать ELSER для вывода на данные, которые загружаются в конвейер.

resp = client.ingest.put_pipeline(
    id="elser-v2-test",
    processors=[
        {
            "inference": {
                "model_id": ".elser_model_2",
                "input_output": [
                    {
                        "input_field": "content",
                        "output_field": "content_embedding"
                    }
                ]
            }
        }
    ],
)
print(resp)
response = client.ingest.put_pipeline(
  id: 'elser-v2-test',
  body: {
    processors: [
      {
        inference: {
          model_id: '.elser_model_2',
          input_output: [
            {
              input_field: 'content',
              output_field: 'content_embedding'
            }
          ]
        }
      }
    ]
  }
)
puts response
const response = await client.ingest.putPipeline({
  id: "elser-v2-test",
  processors: [
    {
      inference: {
        model_id: ".elser_model_2",
        input_output: [
          {
            input_field: "content",
            output_field: "content_embedding",
          },
        ],
      },
    },
  ],
});
console.log(response);
PUT _ingest/pipeline/elser-v2-test
{
  "processors": [
    {
      "inference": {
        "model_id": ".elser_model_2",
        "input_output": [ 
          {
            "input_field": "content",
            "output_field": "content_embedding"
          }
        ]
      }
    }
  ]
}

Объект конфигурации, который определяет input_field для процесса вывода и output_field, который будет содержать результаты вывода.

Загрузите данные

На этом шаге вы загружаете данные, которые позже используете в конвейере загрузки вывода для извлечения токенов из них.

Используйте набор данных msmarco-passagetest2019-top1000, который является подмножеством набора данных MS MARCO Passage Ranking. Он состоит из 200 запросов, каждый из которых сопровождается списком соответствующих текстовых фрагментов. Все уникальные фрагменты вместе с их идентификаторами были извлечены из этого набора данных и объединены в tsv-файл.

Набор данных msmarco-passagetest2019-top1000 не использовался для обучения модели. Мы используем этот демонстрационный набор данных в этом учебнике, потому что он легко доступен для демонстрационных целей. Вы можете использовать другой набор данных для тестирования рабочего процесса и ознакомления с ним.

Загрузите файл и загрузите его в свой кластер, используя Загрузчик файлов в интерфейсе. После анализа ваших данных нажмите Заменить настройки. В разделе Изменить имена полей назначьте id первому столбцу и content второму. Нажмите Применить, затем Импортировать. Назовите индекс test-data и нажмите Импортировать. После завершения загрузки вы увидите индекс под названием test-data с 182 469 документами.

Загрузите данные через конвейер загрузки вывода

Создайте токены из текста, повторно индексируя данные через конвейер вывода, использующий ELSER в качестве модели вывода.

resp = client.reindex(
    wait_for_completion=False,
    source={
        "index": "test-data",
        "size": 50
    },
    dest={
        "index": "my-index",
        "pipeline": "elser-v2-test"
    },
)
print(resp)
response = client.reindex(
  wait_for_completion: false,
  body: {
    source: {
      index: 'test-data',
      size: 50
    },
    dest: {
      index: 'my-index',
      pipeline: 'elser-v2-test'
    }
  }
)
puts response
const response = await client.reindex({
  wait_for_completion: "false",
  source: {
    index: "test-data",
    size: 50,
  },
  dest: {
    index: "my-index",
    pipeline: "elser-v2-test",
  },
});
console.log(response);
POST _reindex?wait_for_completion=false
{
  "source": {
    "index": "test-data",
    "size": 50 
  },
  "dest": {
    "index": "my-index",
    "pipeline": "elser-v2-test"
  }
}

Размер пакета по умолчанию для повторной индексации составляет 1000. Уменьшение size до меньшего числа ускоряет обновление процесса повторной индексации, что позволяет вам следить за прогрессом и вовремя обнаруживать ошибки.

Вызов возвращает идентификатор задачи для отслеживания прогресса:

resp = client.tasks.get(
    task_id="<task_id>",
)
print(resp)
const response = await client.tasks.get({
  task_id: "<task_id>",
});
console.log(response);
GET _tasks/<task_id>

Вы также можете открыть интерфейс «Обученные модели», выбрать вкладку «Конвейеры» в ELSER, чтобы отслеживать прогресс.

Переиндексация больших наборов данных может занять много времени. Вы можете протестировать этот рабочий процесс, используя только подмножество набора данных. Сделайте это, отменив процесс повторной индексации и сгенерировав вложения только для подмножества, которое было переиндексировано. Следующий API-запрос отменит задачу повторной индексации:

resp = client.tasks.cancel(
    task_id="<task_id>",
)
print(resp)
const response = await client.tasks.cancel({
  task_id: "<task_id>",
});
console.log(response);
POST _tasks/<task_id>/_cancel

Семантический поиск с помощью запроса sparse_vector

Для выполнения семантического поиска используйте sparse_vector запрос и укажите текст запроса и идентификатор вывода, связанный с вашей моделью ELSER. В примере ниже используется текст запроса "Как избежать мышечных болей после бега?", поле content_embedding содержит сгенерированный вывод ELSER:

resp = client.search(
    index="my-index",
    query={
        "sparse_vector": {
            "field": "content_embedding",
            "inference_id": "my-elser-endpoint",
            "query": "How to avoid muscle soreness after running?"
        }
    },
)
print(resp)
const response = await client.search({
  index: "my-index",
  query: {
    sparse_vector: {
      field: "content_embedding",
      inference_id: "my-elser-endpoint",
      query: "How to avoid muscle soreness after running?",
    },
  },
});
console.log(response);
GET my-index/_search
{
   "query":{
      "sparse_vector":{
         "field": "content_embedding",
         "inference_id": "my-elser-endpoint",
         "query": "How to avoid muscle soreness after running?"
      }
   }
}

В результате вы получите 10 документов, наиболее близких по смыслу к вашему запросу из индекса my-index, отсортированные по их релевантности. Результат также содержит извлеченные токены для каждого из релевантных результатов поиска с их весами. Токены — это обученные ассоциации, отражающие релевантность, это не синонимы. Чтобы узнать больше о том, что такое токены, обратитесь к этой странице. Можно исключить токены из источника, для получения дополнительной информации см. этот раздел.

"hits": {
  "total": {
    "value": 10000,
    "relation": "gte"
  },
  "max_score": 26.199875,
  "hits": [
    {
      "_index": "my-index",
      "_id": "FPr9HYsBag9jXmT8lEpI",
      "_score": 26.199875,
      "_source": {
        "content_embedding": {
          "muscular": 0.2821541,
          "bleeding": 0.37929374,
          "foods": 1.1718726,
          "delayed": 1.2112266,
          "cure": 0.6848574,
          "during": 0.5886185,
          "fighting": 0.35022718,
          "rid": 0.2752442,
          "soon": 0.2967024,
          "leg": 0.37649947,
          "preparation": 0.32974035,
          "advance": 0.09652356,
          (...)
        },
        "id": 1713868,
        "model_id": ".elser_model_2",
        "content": "For example, if you go for a run, you will mostly use the muscles in your lower body. Give yourself 2 days to rest those muscles so they have a chance to heal before you exercise them again. Not giving your muscles enough time to rest can cause muscle damage, rather than muscle development."
      }
    },
    (...)
  ]
}

Комбинирование семантического поиска с другими запросами

Вы можете комбинировать sparse_vector с другими запросами в составном запросе. Например, используйте условие фильтра в булевом или полнотекстовом запросе с тем же (или другим) текстом запроса, что и sparse_vector запрос. Это позволит вам объединить результаты поиска по обоим запросам.

Результаты поиска по запросу sparse_vector, как правило, имеют более высокий рейтинг, чем результаты других запросов Elasticsearch. Эти оценки можно отрегулировать, увеличив или уменьшив релевантность результатов каждого запроса, используя параметр boost. Точность по запросу sparse_vector может быть высокой, когда есть длинный хвост менее релевантных результатов. Используйте параметр min_score для отсечения этих менее релевантных документов.

resp = client.search(
    index="my-index",
    query={
        "bool": {
            "should": [
                {
                    "sparse_vector": {
                        "field": "content_embedding",
                        "inference_id": "my-elser-endpoint",
                        "query": "How to avoid muscle soreness after running?",
                        "boost": 1
                    }
                },
                {
                    "query_string": {
                        "query": "toxins",
                        "boost": 4
                    }
                }
            ]
        }
    },
    min_score=10,
)
print(resp)
const response = await client.search({
  index: "my-index",
  query: {
    bool: {
      should: [
        {
          sparse_vector: {
            field: "content_embedding",
            inference_id: "my-elser-endpoint",
            query: "How to avoid muscle soreness after running?",
            boost: 1,
          },
        },
        {
          query_string: {
            query: "toxins",
            boost: 4,
          },
        },
      ],
    },
  },
  min_score: 10,
});
console.log(response);
GET my-index/_search
{
  "query": {
    "bool": { 
      "should": [
        {
          "sparse_vector": {
            "field": "content_embedding",
            "inference_id": "my-elser-endpoint",
            "query": "How to avoid muscle soreness after running?",
            "boost": 1 
          }
        },
        {
          "query_string": {
            "query": "toxins",
            "boost": 4 
          }
        }
      ]
    }
  },
  "min_score": 10 
}

И запрос sparse_vector, и запрос query_string находятся в условии should составного запроса bool.

Значение boost равно 1 для запроса sparse_vector, что является значением по умолчанию. Это означает, что релевантность результатов этого запроса не повышена.

Значение boost равно 4 для запроса query_string. Релевантность результатов этого запроса повышена, что приводит к их более высокому ранжированию в результатах поиска.

Отображаются только результаты с оценкой, равной или большей, чем 10.

Оптимизация производительности

Экономия места на диске за счёт исключения токенов ELSER из исходного текста документа

Токены, сгенерированные ELSER, должны быть индексированы для использования в запросе sparse_vector. Однако нет необходимости сохранять эти термины в исходном тексте документа. Вы можете сэкономить место на диске, используя отображение исключения источника, чтобы удалить термины ELSER из исходного текста документа.

Переиндексация использует исходный текст документа для заполнения целевого индекса. После исключения терминов ELSER из источника их нельзя восстановить при переиндексации. Исключение токенов из источника — это оптимизация экономии места, которую следует применять только в том случае, если вы уверены, что переиндексация не потребуется в будущем! Важно тщательно взвесить этот компромисс и убедиться, что исключение терминов ELSER из источника соответствует вашим конкретным требованиям и вариантам использования. Тщательно просмотрите разделы отключения поля _source и включения/исключения полей из _source, чтобы узнать больше о возможных последствиях исключения токенов из _source.

Отображение, исключающее content_embedding из поля _source, можно создать с помощью следующего API-запроса:

resp = client.indices.create(
    index="my-index",
    mappings={
        "_source": {
            "excludes": [
                "content_embedding"
            ]
        },
        "properties": {
            "content_embedding": {
                "type": "sparse_vector"
            },
            "content": {
                "type": "text"
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'my-index',
  body: {
    mappings: {
      _source: {
        excludes: [
          'content_embedding'
        ]
      },
      properties: {
        content_embedding: {
          type: 'sparse_vector'
        },
        content: {
          type: 'text'
        }
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "my-index",
  mappings: {
    _source: {
      excludes: ["content_embedding"],
    },
    properties: {
      content_embedding: {
        type: "sparse_vector",
      },
      content: {
        type: "text",
      },
    },
  },
});
console.log(response);
PUT my-index
{
  "mappings": {
    "_source": {
      "excludes": [
        "content_embedding"
      ]
    },
    "properties": {
      "content_embedding": {
        "type": "sparse_vector"
      },
      "content": {
        "type": "text"
      }
    }
  }
}

В зависимости от ваших данных, запрос sparse_vector может быть быстрее с track_total_hits: false.

Дополнительная информация

  • Как загрузить и развернуть ELSER
  • Ограничения ELSER
  • Улучшение релевантности поиска в Elastic Stack: Представление Elastic Learned Sparse Encoder, нашей новой модели поиска

Интерактивный пример

  • Репозиторий elasticsearch-labs содержит интерактивный пример запуска поиска с семантикой ELSER с использованием Elasticsearch Python клиента.

© 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-elser.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API