Извлечение сущностей с помощью новой функции таблицы поиска в Rasa NLU

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

"What's the weather going to be like in Berlin tomorrow"

и хотел бы извлечь объекты

{ "location": "Berlin", "time": "tomorrow" }

чтобы вы знали, какую информацию вернуть пользователю.

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

Чтобы помочь с этим, мы добавили новую функцию в версию 0.13.3 Rasa NLU, которая позволяет вам добавлять таблицы поиска к вашим обучающим данным. Эти справочные таблицы предназначены для хранения всех известных значений, которые, как вы ожидаете, будут принимать ваши объекты. Например, если вы хотели извлечь сущности сотрудники, они могут содержать имена всех сотрудников вашей компании. Как мы увидим, включение таблиц поиска может значительно улучшить извлечение сущностей и уменьшить количество обучающих примеров, которые вам понадобятся, чтобы получить отличную модель!

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

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

Демонстрация таблицы поиска

Мы рассмотрим, как справочные таблицы повышают производительность трех разных наборов данных. Один маленький (‹100 примеров), один средний (~ 1000 примеров) и один большой (~ 10 000 примеров). Как мы увидим, при использовании этой функции следует помнить о нескольких вещах:

  1. Вам следует подумать, подходит ли объект для поиска в таблицах. Лучше всего использовать поисковые объекты с четко определенной и узкой областью действия. Например, «имена сотрудников» были бы гораздо лучшим вариантом, чем «объекты».
  2. В связи с этим вы всегда должны курировать таблицы поиска, чтобы убедиться, что в них нет элементов, соответствующих нежелательным токенам. Есть много возможностей, чтобы один неверный элемент в таблице испортил тренировку. Например, если одним из элементов является слово, которое может встречаться в других контекстах ваших данных.
  3. Вам следует попытаться сделать таблицы поиска короткими (если возможно), потому что время обучения и оценки зависит от размера таблицы поиска. Как показывает опыт, мы обнаружили, что в таблицах поиска с более чем одним миллионом элементов для завершения обучения и оценки может потребоваться от нескольких минут до часа. Кроме того, использование коротких таблиц поиска может уменьшить проблемы, связанные с первыми двумя точками.

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

А теперь перейдем к демонстрации. Если вы хотите продолжить, код для этих примеров предоставлен здесь.

Краткий пример: ресторан

В этой демонстрации мы покажем, как работают таблицы поиска, обучив бота по поиску ресторанов. Здесь мы сосредоточимся на извлечении food объектов из текста. В data/food/food_train.md мы включили обучающий набор из 36 примеров с намерением restaurant_search. Например:

Could you help me find an [empanada](food) place?

Мы будем обучать эту модель, используя следующую конфигурацию:

Файл: configs / config.yaml

language: "en" 
pipeline: 
 - name: "nlp_spacy"
 - name: "tokenizer_spacy"
 - name: "intent_entity_featurizer_regex"
 - name: "ner_crf"
   features: [ 
               ["low", "title", "upper"],
               ["bias", "low", "prefix5", "prefix2", "suffix5",
                "suffix3", "suffix2", "upper", "title", "digit", 
                "pattern"], 
               ["low", "title", "upper"] 
             ]

Поскольку таблицы используют регулярные выражения для сопоставления, нам понадобятся intent_entity_featurizer_regex и pattern функция в ner_crf.

Затем мы протестируем нашу модель на тестовом наборе food_data/data/food_test.md. Этот тестовый набор содержит несколько пищевых объектов, которые не были замечены моделью, поэтому компоненту ner_crf должно быть сложно извлечь их без какой-либо дополнительной информации.

В демонстрации кода мы можем сделать этот шаг, запустив сценарий run_lookup.py с аргументом food:

python run_lookup.py food

Вот показатели оценки для объекта food:

Метрика Значение точности 1,00 отзыв 0,26 Оценка f1 0,42

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

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

Чтобы указать таблицу поиска в Rasa NLU, мы можем указать список значений или внешний файл, состоящий из фраз, разделенных новой строкой. Мы включили файл data/food/food.txt, содержащий несколько названий продуктов

mapo tofu chana masala sushi pizza ...

и можете загрузить его, добавив следующие строки в файл данных обучения

## lookup:food
   data/food/food.txt

В json это выглядело бы так:

"lookup_tables": [
    {
        "name": "food",
        "elements": "data/food/food.txt"
    }
]

Мы включили файл data/food/food_train_lookup.md, который в точности совпадает с исходными данными обучения, но со вставленной таблицей поиска.

Чтобы прояснить ситуацию, мы создали эту справочную таблицу таким образом, чтобы каждый из ее элементов соответствовал каждому из пищевых объектов как в обучающем, так и в тестовом наборе. Однако при использовании этой функции в своем приложении вам нужно будет приложить некоторые усилия для создания исчерпывающей таблицы поиска, охватывающей большинство значений, которые могут вас заинтересовать. Это будет проще или сложнее в зависимости от характера объекта, который вы хотите извлечь. Например, сущности «страна» - это простой выбор для таблицы поиска, поскольку она может просто содержать список названий каждой страны. Однако для более расплывчатой ​​сущности, такой как «объект», домен может быть слишком большим для таблицы поиска, чтобы охватить все возможные значения.

После включения таблицы поиска и обучения новой модели мы получаем следующие результаты:

Показатель Точность значения 0,82 отзыв 0,55 Оценка f1 0,66

Это свидетельствует о значительном улучшении узнаваемости пищевых продуктов. Обратите особое внимание на то, что показатель отзыва улучшился с 0,26 до 0,55! Похоже, что справочная таблица помогла модели выбрать объекты в тестовом наборе, которые не были замечены в обучающем наборе. Однако, поскольку обучающая выборка все еще очень мала, вам, вероятно, понадобится еще несколько сотен примеров, чтобы на практике повысить этот показатель выше 80%.

Здесь мы суммируем food метрики извлечения сущностей, включая базовый показатель, который представляет собой просто ner_crf компонент с удаленными функциями low, prefix и suffix.

Таблица подстановки хорошо показала себя на простом тестовом примере, но теперь давайте попробуем тот же подход на реальном примере, но с немного большей сложностью. Здесь мы обучим модель с несколькими намерениями и сущностями и воспользуемся более чем 1000 обучающих примеров. Наша цель будет заключаться в улучшении извлечения сущностей company сущностей (названий компаний) для бота, который обучается отвечать на часто задаваемые вопросы на веб-сайте компании. Например:

I work for the [New York Times](company)

Мы включили обучающие примеры в company_data/data/company_data.json. Давайте сначала запустим модель без таблиц поиска и посмотрим, что мы получим. Мы можем сделать это с помощью того же сценария run_lookup.py, запустив

python run_lookup.py company

Это дает нам следующие результаты

Показатель Точность значения 0,83 отзыв 0,11 Оценка f1 0,20

Мы видим, что наш отзыв company равен 0,11, что довольно плохо. Как и в предыдущем примере, мы добавим справочную таблицу, чтобы улучшить этот результат. Мы включили набор данных из 36 тысяч названий стартапов в company_data/data/startups.csv. Затем мы добавили следующие строки к нашим обучающим данным, чтобы загрузить эту таблицу поиска.

{
    "rasa_nlu_data": {
        "lookup_tables": [ 
                {
                    "name": "company",
                    "elements": "data/company/startups.csv"
                }
        ],
        "common_examples": [
             { 
                 "text": "I wanna talk to your sales people.",
                 "intent": "contact_sales",
                 "entities": []
             },
         ... 
        ] 
    } 
}

Когда мы добавляем этот набор данных, мы видим улучшение отзыва с 0,11 до 0,22. Однако мы все еще можем добиться большего. После проверки совпадений мы обнаружили, что было несколько стартапов с названиями, которые также являются обычными английскими словами. Некоторые примеры - это компании, называемые THE или cloud. Они сопоставлялись с неправильными токенами в данных обучения, что ухудшало производительность.

Чтобы решить эту проблему, мы очистили таблицу поиска, отфильтровав эти проблемные элементы. Для этого мы написали сценарий фильтрации таблицы поиска filter_lookup.py, который можно запустить как

python filter_lookup.py <lookup_in> <cross_list> <lookup_out>

Сценарий берет таблицу поиска <lookup_in>, удаляет элементы, содержащиеся в перекрестном списке <cross_list>, и выводит другую отфильтрованную таблицу поиска <lookup_out>.

Для фильтрации обычных английских слов мы использовали перекрестный список, состоящий из допустимых слов Scrabble, который включен в data/company/english_scrabble.txt. Затем можно отфильтровать справочную таблицу стартапов, запустив

python filter_lookup.py data/company/startups.csv data/company/english_scrabble.txt data/company/startups_filtered.csv

который генерирует новый список data/company/startups_filtered.csv, исключающий большинство проблемных имен запуска.

Теперь, запуск тестов с этой новой таблицей поиска дает

Показатель Точность значения 0,96 отзыв 0,51 Оценка f1 0,67

Это дает компании оценку F1 0,51, поэтому мы видим, что удаление этих элементов немного помогло!

Это приводит нас к очень важному предупреждению об использовании таблиц поиска:

Будьте осторожны, очищая и фильтруя данные таблицы поиска !!

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

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

Take me to [123 Washington Street](address) please

У нас есть файл обучающих данных с 11 894 примерами, 3633 из которых имеют адресные объекты. Поскольку эти данные находятся под NDA, мы только что включили результаты оценки ниже.

Показатель Точность значения 0,95 Отзыв 0,93 Оценка f1 0,94

Мы видим, что Rasa NLU действительно неплохо извлекает адреса! Но мы постараемся сделать еще лучше, включив две таблицы поиска, которые мы построили с использованием открытых адресов:

  1. список всех названий городов в США
  2. список всех названий улиц в США.

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

После повторного запуска обучения и оценки с этими новыми таблицами поиска мы получаем

Показатель Точность значения 0,95 отзыв 0,94 Оценка f1 0,94

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

Выводы

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

  1. Делайте их узкими. Лучше всего использовать поисковые объекты с четко определенной областью действия. Например, «имена сотрудников» были бы подходящим вариантом для данного приложения, но, как мы обнаружили, «названия компаний» и «названия улиц» на самом деле являются рискованными вариантами, потому что они очень часто пересекаются с обычными токенами, не являющимися объектами.
  2. Держите их в чистоте. Нужно быть очень осторожным с данными, используемыми в таблицах поиска, особенно больших. Есть много возможностей, чтобы всего один странный элемент в таблице испортил тренировку. Некоторые из них можно очистить (например, как я удалил бессмысленные слова), но некоторые просто являются неотъемлемой частью данных. Например, было много названий улиц, которые не обязательно были словами, но все же совпадали с неадресными жетонами, такими как имена людей. Следовательно, может потребоваться хорошая очистка данных, если вы включите таблицу поиска, взятую из большого набора данных.
  3. Делайте их краткими. Гигантские справочные таблицы также могут увеличить время обучения. Как показывает практика, если длина составляет ›1 м, тренировка займет как минимум от нескольких минут до часа. Ниже приведен график зависимости времени обучения и оценки от количества элементов поиска. Это было обучено на демонстрационном обучающем наборе company.

Помимо точных совпадений

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

Нечеткое соответствие

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

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

Наши первоначальные эксперименты с нечетким соответствием показали, что оно может улучшить отзывчивость и надежность. Однако этот подход намного медленнее, чем обычные таблицы поиска. Поэтому лучше попробовать, когда у вас короткие таблицы поиска (‹1000 элементов).

Символьные диаграммы

При извлечении сущностей в некоторых случаях особенности внутри слова могут быть более важными, чем полные фразы. Одна из наиболее простых функций подслова - это «n-граммы символов», которые просто относятся к последовательностям символов, которые могут отображаться в ваших текстовых данных. Например, утверждение:

"Ban bananas!"

Имеет следующий набор символьных нграмм длиной 3

("ban", "an_", "n_b", "_ba", "ana", "nan", "nas", "as!")

Обратите внимание, что в этой фразе дважды встречаются ban и ana.

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

("llc", "corp", "gmbh", "com", "tech", "plex")

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

Основной процесс выглядит следующим образом:

  1. Сначала мы создаем помеченный набор данных с: a) значениями, которые, как мы ожидаем, примут наши объекты. б) другие ценности, не являющиеся сущностями. мы использовали для этого скрэббл-слова в сочетании с общепринятыми названиями.
  2. Затем мы преобразуем каждый пример, чтобы выразить его в терминах количества каждого символа n-грамма в примере. Например: Ban bananas! -> { 'ban':2, 'an_':1, 'n_b':1, '_ba':1, 'ana':2, 'nan':1, 'nas':1, 'as!':1 }
  3. Из этой формы мы используем рандомизированную логистическую регрессию для извлечения диаграмм, которые имеют наибольшую предсказательную силу при классификации данных.
  4. Теперь мы сортируем эти диаграммы по положительному или отрицательному влиянию на предсказание сущности. Для этого мы просто обучаем модель регулярной логистической регрессии на исходном наборе данных и фильтруем по знаку конечных коэффициентов.
  5. Наконец, положительные и отрицательные диаграммы влияния могут быть помещены в отдельные таблицы поиска, вставлены в обучающие данные и использованы в нашей задаче NLU.

Заявление об ограничении ответственности: в текущем выпуске Rasa NLU таблицы поиска соответствуют только в том случае, если вокруг элементов есть границы слов. Строка кода, составляющая это регулярное выражение, скопирована здесь

regex_string = '(?i)(\\b' + '\\b|\\b'.join(elements_sanitized) + '\\b)'

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

Вы можете поиграть со способом сопоставления таблиц поиска, отредактировав метод _generate_lookup_regex в rasa_nlu/featurizers/regex_featurizer.py вашего форка Rasa NLU.

Заключение

Мы надеемся, что вы получите пользу от этой новой функции в Rasa NLU. Если у вас есть какие-либо демонстрации или другие идеи, пожалуйста, поделитесь с нами!

Ресурсы

Первоначально опубликовано на blog.rasa.com.