Время прочтения: 6 мин.

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

Решение NLP задач с Elasticsearch

Большинство задач NLP начинаются со стандартной предварительной обработки:

  1. Сбор данных
  2. Извлечение необработанного текста
  3. Разделение предложения
  4. Токенизация
  5. Нормализация (стемминг, лемматизация)
  6. Удаление стоп-слов
  7. Частеречная разметка (POS tagging)

Некоторые задачи NLP, такие как синтаксический анализ, требуют глубокого лингвистического распознавания. Сразу стоит отметить, что для такого рода задач Elasticsearch не обеспечивает идеальную архитектуру и формат данных. То есть для задач, выходящих за рамки уровня токенов, необходимо написать или использовать настраиваемые плагины для доступа к полному тексту. Но для таких задач, как классификация, кластеризация, извлечение ключевых слов, измерение сходства и т.д. подойдет Elasticsearch.

Сбор данных и извлечение необработанного текста можно реализовать с помощью плагина Ingest Attachment Processor в Elasticsearch. Извлечение необработанного текста для этих плагинов основано на Apаche Tika, который работает с наиболее распространенными форматами данных (Word, PDF, HTML и другие).

Токенизация, нормализация и удаление стоп-слов осуществляются с помощью языковых анализаторов, вшитых внутри Elasticsearch. Пример:

{
    "properties":{
       "content":{
          "type":"text",
          "analyzer":"russian"
       }
    }
  }

Если тип сопоставления для данного поля — «текст» и анализатор настроен на один из языков, изначально поддерживаемых Elasticsearch, токенизация, нормализация и удаление стоп-слов будут выполняться автоматически во время индексации. Таким образом, для перехода из любого документа, поддерживаемого Apache Tika, в виде представления «мешка слов» (bag-of-words) не требуется никакого специального кода или других инструментов.

Анализаторы языка также можно вызывать через REST API, когда работает Elasticsearch:

curl -XGET "http://localhost:9200/_analyze?analyzer=english" -d'
  {
   "text" : "This is a test."
  }'
  {
    "tokens":[
       {
          "token":"test",
          "start_offset":10,
          "end_offset":14,
          "type":"<ALPHANUM>",
          "position":3
       }
    ]
  }

Модель для классификации

Большинство алгоритмов машинного обучения требуют представления данных в виде модели векторного пространства. Такое пространство обычно представляет собой что-то вроде 10 000 наиболее важных слов конкретного набора данных.

Важность слова определим с TF-IDF. Это формула была изобретена в 70-х годах прошлого века. TF-IDF — статистическая мера, используемая для оценки важности слова в контексте документа. Если слово в документе имеет высокий показатель TF-IDF, это означает, что это очень характерное ключевое слово и отличает данный документ от всех других документов с помощью этого слова.

Ключевые слова с наивысшими оценками TF-IDF в подмножестве документов могут представлять тему. Для классификации текста довольно часто встречается пространство признаков с N словами с наивысшими общими баллами TF-IDF. Каждый документ преобразуется в признаковое описание объекта, а затем со всеми обучающими экземплярами для каждого класса/категории создается модель. После этого новые документы можно классифицировать по этой модели. Поэтому документ необходимо преобразовать в признаковое описание объекта, и оттуда вычисляются все сходства. Документу будет присвоена категория с наивысшим баллом.

Классификация текста с Elasticsearch

Классификация текстов – это задача, традиционно решаемая с помощью машинного обучения с учителем. Входными данными для обучения модели является набор помеченных документов. Минимальным представлением этого будет документ JSON с двумя полями: «содержание» и «категория». Традиционно классификацию текста можно решить с помощью таких инструментов, как SciKit Learn, Weka, NLTK, Apache Mahout и другие.

Все вышеперечисленное можно решить гораздо проще с помощью Elasticsearch (или Lucene).

Нужно выполнить 4 шага:

  1. Настроить сопоставление («контент»: «текст», «категория»: «ключевое слово»)
  2. Проиндексировать документы
  3. Выполнить запрос, похожий на этот (запрос MLT)
  4. Написать небольшой скрипт, который объединяет совпадения этого запроса по количеству очков (by score).
PUT sample
  POST sample/document/_mapping
  {
    "properties":{
       "content":{
          "type":"text",
          "analyzer":"english"
       },
       "category":{
          "type":"text",
          "analyzer":"english",
          "fields":{
             "raw":{
                "type":"keyword"
             }
          }
       }
    }
  }
  POST sample/document/1
  {
    "category":"Apple (Fruit)",
    "content":"Granny Smith, Royal Gala, Golden Delicious and Pink … …oven at a low heat."
  }
  POST sample/document/2
  {
    "category":"Apple (Company)",
    "content":"Apple is an American multinational … …Store and Mac App Store, Apple Music, and iCloud."
  }

MLT-запрос – очень важный запрос для интеллектуального анализа текста. Он может обрабатывать произвольный текст, извлекать первые N ключевых слов и запускать логический запрос соответствия с этими ключевыми словами. Этот запрос часто используется для сбора похожих документов.

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

category GET sample/document/_search
  {
    "query":{
       "more_like_this":{
          "fields":[
             "content",
             "category"
          ],
          "like":"The apple tree (Malus pumila, commonly and erroneously called Malus domestica) is a deciduous tree in the rose family best … …uropean Christian traditions.",
          "min_term_freq":1,
          "max_query_terms":20
       }
    }
  }

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

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

from operator import itemgetter
  def get_best_category(response):
     categories = {}
     for hit in response['hits']['hits']:
         score = hit['_score']
         for category in hit['_source']['category']: 
             if category not in categories:
                 categories[category] = score
             else:
                 categories[category] += score
     if len(categories) > 0:
         sortedCategories = sorted(categories.items(), key=itemgetter(1), reverse=True)
         category = sortedCategories[0][0]
     return category

Примеры применения

Классификация текста – очень распространенный пример использования NLP в реальном мире. Как пример – данные, товары электронной коммерции. Многие люди открывают онлайн магазины с партнерскими ссылками. Данные предоставляются несколькими магазинами и часто сопровождаются тегом категории. Но у каждого магазина есть свой тег категории. Таким образом, системы категорий необходимо унифицировать, и, следовательно, все данные необходимо переклассифицировать в соответствии с новым деревом категорий. Или возьмем приложение Business Intelligence, в котором веб-сайты компаний необходимо классифицировать по их сектору (парикмахерская или пекарня и т. д.)

Оценка эффективности

Оценка производилась с помощью стандартного набора данных классификации текстов: набора данных 20 групп новостей. Наивысшая точность (92% правильных меток) была достигнута при высоком пороге оценки качества, который включал только 12% документов. При маркировке всех документов 72% прогнозов оказались верными.

Лучшими алгоритмами классификации текста в наборе данных являются SVM и Наивный байесовский алгоритм. У них более высокая средняя точность для всего набора данных.

Так почему вы должны использовать Elasticsearch для классификации, если есть лучшие алгоритмы?

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

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