Программирование
Как сделать нечеткое сопоставление строк в Rasa
Учебник о том, как интегрировать библиотеку fuzzywuzzy в конвейер Rasa NLU
Введение
В этой статье я расскажу, как создать пользовательский компонент в rasa, чтобы сделать извлечение сущностей более устойчивым к опечаткам. В частности, мы будем использовать библиотеку fuzzywuzzy, чтобы выполнить нечеткое сопоставление строк для автокоррекции объекта на основе его оценки сходства.
Код для воспроизведения бота, описанного в этой статье, можно найти здесь.
Постановка задачи
Предположим, что бот должен извлекать объекты, представляющие страну, из высказывания и нормализовать их до некоторой канонической формы. Это можно сделать с помощью функции синонимы расы:
Таким образом, фраза типа «Я из Соединенных Штатов» будет обрабатываться конвейером NLU как:
Однако, если пользователь сделал опечатку, например. «Я из Соединенных Штатов Америки», результаты такие:
Мы видим, что этому классификатору DIET удается распознать united states of amercia
как объект страны, но, поскольку в предоставленном списке синонимов нет точного соответствия, он не получил сопоставление с usa
, в отличие от высказывания на рисунке 2. Это действительно раздражает, потому что это обычная опечатка и невозможность привести ее к какой-либо канонической форме будут мешать последующим задачам, которые полагаются на извлечение правильного объекта страны.
Одно из возможных решений — включить в список синонимов все возможные опечатки (или хотя бы распространенные). Это не идеально, потому что список будет не только излишне длинным и сложным в обслуживании, но и нам придется предвидеть возможные ошибки, которые могут совершить наши пользователи!
Лучшим решением является создание пользовательского компонента, который просматривает извлеченные сущности и нечетко сопоставляет их со списком известных сущностей. Если сходство между извлеченным объектом и элементом в этом списке превышает определенный порог, мы делаем вывод, что, вероятно, в извлеченном объекте есть опечатка, и автоматически исправляем его на этот элемент. В следующем разделе будет описано, как построить это решение.
Решение: создание пользовательского компонента
Шаг 1. Определитесь с полным именем класса пользовательского компонента.
Мы реализуем наш пользовательский компонент в классе с именем EntityTypoFixer
в файле с именем my_custom_components.py
, который сохраняется в папке с именем addons
относительно корневого каталога проекта.
Поэтому, если вы запустите python в корневом каталоге проекта, полное имя класса компонента будет addons.my_custom_components.EntityTypoFixer
.
Шаг 2: Реализуйте параметры для пользовательского компонента
Было бы удобно, чтобы пороговая оценка была параметром пользовательского компонента, чтобы мы могли сравнивать эффекты разных порогов с rasa test
. Мы назовем этот параметр score_cutoff
и присвоим ему значение по умолчанию 80.
Первое, что нам нужно сделать, это определить свойство класса с именем defaults
, которое будет хранить словарь значений параметров по умолчанию:
Далее мы определяем, как использовать параметр score_cutoff
по умолчанию в методе __init__
EntityTypoFixer
:
Шаг 3: Определите, что происходит во время обучения
Когда конвейер NLU обучен, мы хотели бы собрать все сущности, перечисленные во всех синонимах, в список, чтобы мы могли искать их во время вывода:
Шаг 4: Определите, что происходит во время логического вывода
Получив сообщение во время вывода, мы извлечем все сущности в список и пройдемся по каждому элементу, чтобы нечетко сопоставить их с сущностями в self.entities
:
Фактическое нечеткое сопоставление выполняется при вызове self.fix_entity_typo
и определяется следующим образом:
На рисунке 8 показано, что если оценка наилучшего совпадения в self.entities
с извлеченным объектом превышает score_cutoff
и не равна 100, то значение этого извлеченного объекта обновляется значением наилучшего совпадения, а также добавляется этот компонент в список processors
, поэтому что мы знаем, что этот компонент использовался при обработке высказывания.
Шаг 5: Определите, как сохранить и загрузить пользовательский компонент
Поскольку для правильной работы этого компонента требуется список сущностей, извлеченных во время обучения, давайте определим функции для сохранения и загрузки этой информации:
Шаг 6: Добавьте пользовательский компонент в config.yml
Наконец, мы добавляем этот компонент в раздел pipeline
раздела config.yml
. Поскольку мы хотим, чтобы этот компонент запускался после извлечения сущностей и до того, как они будут сопоставлены с синонимами, его следует поместить между компонентами DIETClassifier
и EntitySynonymMapper
:
Обратите внимание, что мы использовали полное имя класса компонента, которое мы выяснили на шаге 1.
Результаты
После переобучения модели с новым config.yml
результатом высказывания «Я из Соединенных Штатов Америки» будет:
По сравнению с рисунком 3 мы видим, что значение извлеченного объекта теперь равно usa
, чего мы и хотели. Мы также видим, что EntityTypoFixer
находится в списке processors
, что указывает на то, что он был вызван как часть процесса извлечения.
Результат обработки «Я из Соединенных Штатов» неизменен, так как в этом высказывании нет опечатки:
Вывод
В этой статье показано, как выполнить нечеткое сопоставление извлеченных сущностей в rasa с помощью библиотеки fuzzywuzzy. Шаги, описанные в этой статье, можно применять для создания других пользовательских компонентов, выполняющих произвольные задачи предварительной или последующей обработки.
Я надеюсь, что вы нашли это полезным.