7 идей о том, как оптимизировать вашу поисковую систему с помощью обработки естественного языка без трансформеров

Автор: Даниэль Попек и Павел Мельничук.

Амбиции этой статьи

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

В предыдущей статье об Elasticsearch мы изложили основные факты о механизме поисковой системы и ее компонентах. На этот раз мы хотели бы больше сосредоточиться на классических концепциях НЛП для предварительной обработки текстов и индексации движка. Мы представляем вам 7 идей, которые вы можете найти или с которыми захотите поэкспериментировать, чтобы повысить эффективность поиска. Опираясь на наш опыт, мы также пытаемся оценить их удобство использования. Для целей этой статьи мы провели несколько экспериментов, чтобы лучше проиллюстрировать концепции и предоставить вам готовый к использованию код.

Примечание. В экспериментах мы использовали седьмую версию сервера Elasticsearch с открытым исходным кодом, на момент написания этой статьи уже выпущена более свежая версия 8. Все эксперименты проводились в докеризованной среде для простоты использования. Для удобства чтения некоторые таблицы с результатами свернуты. Полные результаты и подробности реализации см. в нашем репозитории NeuroSYS GitHub.

Настройка экспериментов

Чтобы лучше продемонстрировать идеи оптимизации, мы подготовили два набора данных информационного поиска.

  1. Первый основан на SQUAD 2.0 — тесте ответов на вопросы, который также можно использовать для проверки информационного поиска (IR) при правильной настройке. Мы извлекли 10 000 случайных документов и 1 000 связанных вопросов. Мы рассматриваем каждый абзац SQUAD как отдельный индексный документ. Стоит отметить, что Elasticsearch по умолчанию предназначен для гораздо большей коллекции (миллионы документов). Однако мы обнаружили, что ограниченная версия SQUAD быстрее вычисляется и хорошо обобщает.
  2. Второй бенчмарк, SWIFT UI, разработан нами специально и намного меньше. Мы использовали его, чтобы показать, как Elasticsearch ведет себя с меньшими индексами и как одни и те же типы запросов по-разному действуют на другие наборы данных. Набор данных для проверки основан на транскрипции лекций Стэнфордского университета по платформе SwiftUI, выпущенной под лицензией Creative Commons (CC BY-NC-SA 4.0). Лекции и лицензионные заметки можно найти здесь. Мы разделили первые семь лекций на 185 меньших, документов из 25 предложений с пятью перекрывающимися предложениями. Мы также подготовили 184 вопроса с несколькими вариантами ответов.

Абзацы SQUAD взяты из Википедии, поэтому текст лаконичен и хорошо написан и вряд ли будет содержать ошибки. Между тем, бенчмарк SWIFT UI состоит из текстов из записанных образцов речи — он более яркий, менее конкретный, но все же грамматически правильный. Кроме того, он богат технической, ориентированной на программную инженерию лексикой.

Для проверки задачи информационного поиска обычно используются MRR (средний обратный ранг) или MAP (средняя средняя точность). Мы также используем их ежедневно; однако для целей этой статьи, чтобы упростить интерпретацию результатов, мы выбрали те, которые намного проще — соотношение отвеченных вопросов в пределах top N попаданий:попаданий@10 , хиты@5, хиты@3, хиты@1.

Идея 1: Проверить влияние анализаторов на производительность IR

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

Чтобы подтвердить вышеизложенное, мы представляем результаты проверки применения различных анализаторов к ограниченным документам SQUAD. В зависимости от выполняемых операций эффективность поиска существенно различается.

Мы предоставляем результаты экспериментов, проведенных с использованием около 50 анализаторов на ограниченной группе SQUAD, отсортированных по попаданиям@10. Таблица свернута для удобства чтения; однако вы можете ознакомиться с полными результатами и кодом на нашем GitHub.

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

  • выборка обеспечивает значительные улучшения,
  • удаление стоп-слов не слишком улучшает производительность,
  • стандартный токенизатор обычно работает лучше всего, а пробел токенизатор работает хуже всего,
  • использование шинглов (словесные n-граммы) не дает значительного улучшения, в то время как char n-граммы могут даже снизить производительность,
  • одновременный поиск корней при сохранении исходных слов в одном и том же поле работает хуже, чем голый поиск корней,
  • Стэмминг Портера превосходит алгоритм Ловинса.

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

Идея 2: копнуть глубже в механизм подсчета очков

Как мы знаем, Elasticsearch использует индексы Lucene для сегментирования, что работает в пользу эффективности времени, но также может вызвать у вас головную боль, если вы об этом не знаете. Одним из сюрпризов является то, что Elasticsearch вычисляет баллы отдельно для каждого сегмента. Это может повлиять на производительность поиска, если используется слишком много сегментов. Как следствие, результаты могут оказаться недетерминированными между индексациями.

Обратная частота документов (IDF) является неотъемлемой частью BM25 и рассчитывается для каждого термина, при этом документы помещаются в отдельные корзины. Следовательно, чем больше осколков у нас есть, тем больше будет отличаться поисковая оценка по конкретным запросам.

Тем не менее, можно заставить Elasticsearch вычислять оценку BM25 для всех осколков вместе, рассматривая их как единый большой индекс. Однако это сильно влияет на время поиска. Если вам важно не время поиска, а согласованность/воспроизводимость, рассмотрите возможность использования Распределенного частотного поиска. Он суммирует все факторы BM25, независимо от количества сегментов.

Мы представили точность задачи информационного поиска в таблице ниже.

Примечание. Мы хотели сосредоточиться на точности результатов, а не на том, как быстро нам удалось их получить.

Хорошо видно, что точность колеблется при изменении количества осколков. Также можно отметить, что количество сегментов не влияет на оценку при использовании DFS.

Однако при достаточно большом наборе данных влияние осколков будет меньше. Чем больше документов в индексе, тем больше частей IDF BM25 нормализуются в осколках.

В приведенной выше таблице видно, что влияние сегментов (относительная разница между оценками DFS и не DFS) тем ниже, чем больше документов индексируется. Следовательно, проблема менее болезненна при работе с более обширными коллекциями текстов. Однако в таком случае более вероятно, что нам потребуется больше осколков из-за производительности по времени. Когда дело доходит до меньших индексов, мы рекомендуем установить количество осколков на значение по умолчанию, равное единице, и не слишком беспокоиться о влиянии осколков.

Идея 3: проверить влияние различных функций оценки

BM25 — хорошо зарекомендовавший себя алгоритм подсчета очков, который отлично работает во многих случаях. Однако, если вы хотите попробовать другие алгоритмы и посмотреть, насколько хорошо они работают в вашей языковой области, Elasticsearch позволяет вам выбрать одну из нескольких реализованных функций или определить свои собственные, если это необходимо.

Несмотря на то, что мы не рекомендуем начинать оптимизацию с изменения алгоритма оценки, возможность остается открытой. Мы хотели бы представить результаты на SQUAD 10k с использованием следующих функций:

  • Окапи BM25 (по умолчанию),
  • DFR (расхождение от случайности),
  • DFI (отклонение от независимости),
  • ИБ (информационный),
  • Л. М. Дирихле,
  • Л. М. Елинек Мерсер,
  • пользовательский TFIDF, реализованный как скриптовая функция подобия.

Как вы можете видеть в случае ограниченного SQUAD, BM25 оказался самой эффективной функцией подсчета очков. Однако когда дело доходит до пользовательского интерфейса SWIFT, несколько лучшие результаты можно получить, используя альтернативные оценки сходства, в зависимости от интересующей нас метрики.

Идея 4: настроить параметры Okapi BM25

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

Значения по умолчанию для параметров:

  • b — коэффициент нормализации частоты терминов в зависимости от длины документа,
  • k1 — коэффициент нелинейной нормализации частоты терминов

Обычно они лучше всего работают в нескольких тестах, что мы также подтвердили в наших тестах на SQUAD.

Имейте в виду, что, несмотря на то, что значения по умолчанию считаются наиболее универсальными, это не означает, что вы должны игнорировать другие параметры. Например, в случае с набором данных пользовательского интерфейса SWIFT другие значения оказались лучше на 2 % в 10 самых популярных обращениях.

В данном случае параметры по умолчанию снова оказались лучшими для SQUAD, тогда как SWIFT UI выиграл бы больше от других.

Идея 5: добавьте дополнительные данные в свой индекс с помощью настраиваемых фильтров

Как уже упоминалось, в НЛП есть масса опций, которыми можно обогатить текст. Мы хотели бы показать вам, что происходит, когда мы решаем добавить синонимы или другие производные слова, такие как фонемы.

Синонимы

Задаваясь вопросом, как сделать наши документы более подробными или более удобными для запросов, мы можем попытаться расширить доступные формулировки, используемые для описаний документов. Однако делать это нужно с большой осторожностью. Слепое добавление слов в документы может привести к потере их смысла, особенно если речь идет о более длинных текстах.

Автоматически — синонимы WordNet

Есть возможность автоматически расширять наш инвертированный индекс дополнительными словами, используя синонимы из синсетов WordNet. Elasticsearch имеет встроенный фильтр синонимов, который упрощает интеграцию.

Ниже мы представили результаты поиска по наборам данных пользовательского интерфейса SQUAD и SWIFT с использованием и без использования всех доступных синонимов.

Как видно, использование автоматического добавления синонимов вслепую резко снизило производительность. С тысячами дополнительных слов представления документов становятся переполненными; таким образом, они теряют свой первоначальный смысл. Эти избыточные синонимы могут не только не улучшить описательность документов, но и нанести вред уже имеющим смысл текстам.

Количество терминов в наборе данных пользовательского интерфейса SWIFT увеличилось более чем в три раза при использовании синонимов. Это приносит очень негативные последствия для алгоритма BM25. Помните, что алгоритм наказывает длинные тексты, поэтому документы, которые раньше были короткими и информативными, теперь могут занимать значительно меньше места на странице результатов поиска.

Значимые синонимы

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

  1. Во-первых, с помощью spaCy мы извлекли 50 различных именованных объектов из домена языка программирования Swift, используемого в наборе данных пользовательского интерфейса SWIFT.
  2. Во-вторых, мы нашли для них синонимы вручную. Поскольку наша симуляция не требует использования реальных, существующих слов, мы просто использовали случайные слова в качестве заменителей бизнес-сущностей.
  3. Наконец, мы заменили вхождения именованных сущностей в вопросы эквивалентами выбранных слов из предыдущего шага и добавили список синонимов в индекс с помощью synonym_analyzer.

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

Производительность поиска повышается при использовании синонимов, добавляемых вручную. Несмотря на то, что эксперимент был проведен на небольшой выборке, мы надеемся, что он хорошо иллюстрирует концепцию — вы можете извлечь пользу из добавления эквивалентов некоторых значимых слов, если у вас есть надлежащее знание предметной области. Этот процесс занимает много времени и вряд ли может быть автоматизирован; однако мы считаем, что это часто стоит затраченного времени и усилий.

Влияние фонем

Следует отметить, что при работе с транскрипциями ASR (автоматическое распознавание речи) многие слова могут распознаваться неправильно. Они часто подвержены многочисленным ошибкам в транскрипции, поскольку некоторые фразы и слова звучат одинаково. Также может случиться так, что не носители языка могут неправильно произносить слова. Например:

Для использования фонетического токенизатора в узле Elasticsearch должен быть установлен специальный плагин.

Предложение «Том Хэнкс — хороший актер, потому что он любит играть» представлено как:

  • ['TM', 'HNKS', 'IS', 'A', 'KT', 'AKTR', 'AS', 'H', 'LFS', 'PLYN'], когда используя фонетический токенизатор Metaphone,

и

  • ['TM', 'том', 'HNKS', 'хэнкс', 'ИС', 'есть', 'А', 'а', 'КТ', 'хорошо', 'АКТР', 'актер ', 'AS', 'as', 'H', 'he', 'LFS', 'loves', 'PLYN', 'playing'], при одновременном использовании фонем и исходных слов.

Мы пришли к выводу, что использование фонем вместо исходного текста в случае высококачественных наборов данных без ASR, таких как SQUAD, не дает значительных улучшений. Однако индексирование фонем и исходный текст в отдельных полях и поиск по обоим полям немного повысили производительность. В случае SWIFT UI качество транскрипции на удивление хорошее, хотя текст исходит из ASR. Следовательно, фонетический токенизатор и здесь неприменим.

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

Идея 6: добавьте дополнительные поля в свой индекс

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

В этом небольшом эксперименте мы хотели доказать, имеет ли смысл вышеуказанная идея в Elasticsearch и как этого добиться. Мы проверили это:

  • извлечение именованных сущностей с использованием моделей глубокого обучения на основе Transformer из Huggingface 🤗,
  • получение ключевых слов по модели KeyBERT,
  • добавление лемм из SpaCy

Таким образом, мы нарушили здесь правило использования только классических, ненейронных методов. Однако в этом сценарии можно использовать и другие алгоритмы генерации ключевых слов, такие как Textrank и Named Entities от SpaCy.

Примечание. Именованные объекты, а также ключевые слова — это отрывки, уже существующие в тексте, но выделенные в отдельные поля. Напротив, леммы представляют собой дополнительно обработанные слова; они предоставляют больше информации, чем доступно в исходном тексте.

Проводя эксперименты, мы обнаружили, что в данном случае ключевые слова и NER не улучшают эффективность IR. Наоборот, лемматизация слов, похоже, дала значительный прирост.

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

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

Короче говоря, кажется, что поля содержат некоторую дополнительную информацию (например, текст название)или содержат дополнительно обработанные осмысленные фразы (например, слова леммы) может повысить точность поиска.

Идея 7: оптимизировать запрос

И последнее, но не менее важное: существует множество вариантов создания запросов. Мы можем не только изменить тип запроса, но и усилить отдельные поля в индексе. Помимо использования анализатора, мы настоятельно рекомендуем поэкспериментировать с этим шагом, так как он обычно улучшает результаты.

Мы провели небольшой эксперимент, в ходе которого протестировали следующие типы запросов Elastic multi-match: best_fields, most_fields, cross_fields, на полях:

  1. текст — исходный текст,
  2. заголовок — заголовок документа, только если он указан,
  3. ключевые слова — взяты из KeyBERT,
  4. NERs — сделано с помощью Huggingface 🤗 Transformers,
  5. леммы — извлечены с помощью SpaCy,

Кроме того, мы увеличили значение каждого поля с 1,0 по умолчанию до 2,0 с шагом 0,25.

Как было доказано выше, результаты по набору данных SQUAD, несмотря на его ограниченность, показывают, что запросы типа cross_field дали наилучшие результаты. Следует также отметить, что увеличение поля title было хорошим выбором, так как в большинстве случаев оно уже содержало важные и описательные данные обо всем документе. Мы также заметили, что увеличение только полей keywords или NER приводит к худшим результатам.

Однако, как это часто бывает, нет ничего лучше одного четкого и универсального выбора. Экспериментируя с пользовательским интерфейсом SWIFT, мы пришли к выводу, что поле title в этом случае менее важно, так как оно часто отсутствует или содержит тарабарщину. Кроме того, когда дело доходит до типа запроса, хотя cross_fields обычно отображается вверху, существует множество запросов best_fields с очень похожей производительностью. В обоих случаях запросы most_fields обычно размещаются где-то посередине.

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

Заключение

По сравнению с моделями поиска информации с глубоким обучением, полнотекстовый поиск по-прежнему работает довольно хорошо во многих случаях использования. Elasticsearch — отличный и популярный инструмент, поэтому у вас может возникнуть соблазн начать использовать его прямо сейчас. Тем не менее, мы рекомендуем вам по крайней мере немного прочитать заранее, а затем попытаться оптимизировать эффективность поиска. Так вы избежите попадания в яму неправильного использования и попыток выбраться из нее.

Мы настоятельно рекомендуем начать с анализаторов и оптимизации запросов. Используя готовые механизмы НЛП в Elastic, вы можете значительно улучшить результаты поиска. Только после этого переходите к более сложным или экспериментальным идеям, таким как функции оценки, синонимы или дополнительные поля.

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