Как мы сломали языковой барьер с помощью одной модели?

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

Предыстория

Я стажируюсь в ООО «ОЦРВ»[ссылка удалена мод.] в Сочи в лаборатории обработки естественного языка (NLP), где люди делают все для простого и удобного общения с пользователем.

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

Для этого был разработан мультиязычный модуль. Его достоинство, в том, что нам не нужно собирать данные на всех языках мира, а только на одном удобном для нас. Пользователь при этом просто обращается к ассистенту на своем родном языке, последний понимает на каком языке к нему обратились и генерирует ответ уже на этом языке. И все это благодаря пайплайну на основе языковой модели LaBSE (Language-agnostic BERT Sentence Embedding) и фреймворка RASA.

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

Путь

«У самурая нет цели, только путь»

Для эффективного использования нашего модуля, нам необходимо обеспечить возможность ведения диалога с пользователем. Именно здесь начинается наш путь к Rasa!

Rasa — это открытая сценарная платформа для создания чат-ботов и виртуальных помощников, в ней много инструментов для построения логики диалога. Для того чтобы приступить к работе с этим фреймворком мы устанавливаем его командой:

pip install rasa

Рекомендую использовать версию 3.1, так как она самая стабильная, и, скорее всего, не придется мучиться с версиями пакетов. Версию python рекомендую выбирать 3.8.0. Rasa, на данный момент, поддерживает такие python версии как 3.8, 3.9 и 3.10, но python 3.10 поддерживается только для версий 3.4.x и выше.

После того как мы скачали Rasa, нам нужно запустить инициализацию проекта.

rasa init

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

Выбор мультиязычной модели

Какие есть варианты и ограничения?

Так, как вариант написать свою собственную модель пока не рассматриваем, будем выбирать из того, что есть.
Сама Rasa «не пускает кого попало», а значит выбираем с умом!

Я ЕСТЬ RASA

Я ЕСТЬ RASA

В фреймворке есть так называемый протокол буферизации, который дает допуск только к нескольким моделям. Но его можно будет и обойти, если просто идти на прямую к моделям на Hugging Face. Модели поддерживаемые Rasa:

e086c6f71841fb5703b3363aa8683478.png

Из доступных для Rasa мультиязычных моделей под задачу подходят четыре. Давайте рассмотрим их по порядку.

1. rasa/LaBSE
Предназначена для создания векторных представлений предложений, которые полезны для задач перевода и семантического поиска в многоязычном контексте. Модель поддерживает 109 языков.

Особенности:

  • Эффективно используется для машинного перевода.

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

  • Мультиязычное понимание текста, семантическое сравнение, машинный перевод.

2. xlnet-base-cased
Это авторегрессионная модель, которая использует пермутационное предсказание, чтобы лучше уловить двунаправленные контексты в тексте, по сравнению с традиционными моделями, основанными на Transformer.

Особенности:

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

  • Расширенное понимание текста, вопросно-ответные системы, классификация текста.

3. distilbert-base-uncased
Это «облегченная» (дистиллированная) версия архитектуры BERT, разработанная с целью сокращения размера модели и повышения скорости работы при сохранении большей части ее производительности.

Особенности:

  • Примерно на 40% меньше параметров, чем у оригинального BERT, и на 60% быстрее.

  • Классификация текста, распознавание именованных сущностей, оптимизирована для быстрой работы на маломощных устройствах.

4. roberta-base
Разработана Facebook AI, является оптимизированной версией BERT, предназначенной для более эффективного обучения данных при увеличенных размерах батчей и длинных последовательностях.

Особенности:

  • Улучшена за счет более длительного обучения на большом наборе данных и с увеличенным размером батча.

  • Глубокое понимание текста, классификация текста, вопросно-ответные системы.

Если проводить сравнение этих моделей, то оно будет примерно такое:

Языковая поддержка: LaBSE выделяется своей мультиязычной поддержкой, в то время, как остальные модели обычно сфокусированы на одноязычных приложениях.
Производительность и скорость: DistilBERT предлагает наилучший баланс между производительностью и скоростью, что делает его идеальным выбором для применения в средах с ограниченными ресурсами.
Контекстное понимание: XLNet и RoBERTa предоставляют улучшенные возможности для понимания контекста благодаря своим уникальным архитектурным решениям, что делает их идеальными для сложных задач понимания текста и вопросно-ответных систем.

Исходя из результатов выбираем LaBSE.

Почему RASA и LaBSE?

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

Теперь перейдём от слов к действиям

Я использовала не только модель LaBSE, но и открытую библиотеку googletrans для определения языка на котором говорит/пишет пользователь, чтобы потом перевести ему ответ. В этой библиотеке реализован API Google Translate, который использует API Google Translate Ajax для вызова методов перевода и определение языка.
Версия googletrans==3.1.0a0

»Бесшовный» подход заключается в наличии базы ответов на одном языке, в контексте Rasa, это intents, но при этом введенное пользователем слово/предложение на любом языке распознается моделью как нужный нам интент за счет векторов.

Пример
Если пользователь напишет «hello», то модель распознает его как greet, на русском «привет».
Эти слова будут иметь одинаковые векторы, несмотря на разный язык на входе.

e6ea428591b036d565355a5cf8853ed3.png

Слово преобразуется в вектор, и собственно тут мы и «идем» к модели LaBSE. Ей не важно на каком языке мы к ней обращаемся.

Для того, чтобы запустить модель в работу, нужно в файле config.yml в pipeline прописать следующее:

pipeline:
  - name: "LanguageModelFeaturizer"
    model_name: bert
    model_weights: rasa/LaBSE
    cache_dir: null
    use_cache: null

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

Далее нам понадобится задать тренировочные данные в nlu.yml
Важное уточнение: вам обязательно понадобится минимум 2 интента и 2 примера их значений.

c0c13b14aa35d7e3cdf0d24b18e9161c.png

На интенты составим ответы для пользователя, заведем для этого отдельный файл responses.yml. Он был создан для более корректного выполнения логики перевода. Можно не создавать отдельный файл и прописывать ответы в domain.yml.

Реализация ассистента-переводчика

Для начала импортируем необходимые библиотеки. Импорт из SDK для разработки чат-ботов на базе Rasa загружает классы Action и Tracker.

  • Action будем использовать для создания пользовательских действий, которые чат-бот может выполнить в ответ на запросы пользователя.

  • Tracker — для отслеживания состояния диалога в Rasa, позволяя получить доступ к предыдущим сообщениям пользователя, их намерениям, сущностям и другим данным из диалога.

  • CollectingDispatcher нужен для отправки сообщений обратно
    пользователю. Он собирает сообщения и данные, которые нужно передать и
    управляет их отправкой.

from typing import Any, Text, Dict, List
from rasa_sdk import Action, Tracker
from rasa_sdk.executor import CollectingDispatcher
from googletrans import Translator

Создадим класс CustomTranslator для перевода текста.

class CustomTranslator:

Инициализируем экземпляр переводчика Translator из библиотеки googletrans. Поле detected_lang используется для хранения обнаруженного двухбуквенного кода языка текста.

    def __init__(self):
        self.translator = Translator()
        self.detected_lang = None

Метод detect_language нужен для определения двухбуквенного кода языка заданного текста. Метод использует функцию detect из экземпляра Translator для определения языка текста и сохраняет его в переменной detected_lang. Затем возвращает обнаруженный язык.

    def detect_language(self, text: str) -> str:
        self.detected_lang = self.translator.detect(text).lang
        return self.detected_lang

Метод translate_text для перевода текста. Если язык не был обнаружен, метод выводит сообщение об ошибке и возвращает исходный текст. Если язык обнаружен, метод переводит текст на обнаруженный язык с помощью функции translate из экземпляра Translator, указывая целевой язык (dest) равным detected_lang. Метод вернет переведенный текст.

    def translate_text(self, reply: str) -> str:
        if not self.detected_lang:
            print("Language not detected.")
            return reply

        result = self.translator.translate(reply, dest=self.detected_lang)
        return result.text

Далее создаем класс ActionGetResponse, который будет наследоваться от базового класса Action, который предлагает нам Rasa.

class ActionGetResponse(Action):

Создаем метод name. В контексте Rasa SDK, он используется для определения уникального идентификатора действия (action) в чат-боте. В данном случае метод name возвращает строку «action_get_translated_response», которая является уникальным названием действия.

    def name(self) -> Text:
        return "action_get_translated_response"

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

Следующий метод — run (запускается сразу после определения интента, указанного в stories.yml). В нем реализуем логику перевода ответов из файла responses на интенты. Для перевода конкретного ответа строится ключ для поиска подходящего ответа в domain['responses'] на основе определенного намерения. Если ответ найден, он извлекается. Иначе вызывает ошибку.

    def run(self, dispatcher: CollectingDispatcher,
            tracker: Tracker,
            domain: Dict[Text, Any]) -> List[Dict[Text, Any]]:

        user_message = tracker.latest_message.get('text')
      	## tracker извлекается последнее сообщение пользователя.

        translator = CustomTranslator()

        translator.detect_language(user_message)

        intent_name = tracker.latest_message['intent']['name']
        ## Извлекается намерение (intent) из последнего сообщения пользователя.

        response_key = f"utter_{intent_name}"
        if response_key in domain['responses']:
            reply = domain['responses'][response_key][0]['text']

            translated_text = translator.translate_text(reply)
            dispatcher.utter_message(text=translated_text)
        else:
            dispatcher.utter_message(text='No response found for this intent.')

        return []

Вместо заключения

Я бы хотела, чтобы этот проект не просто оставался на уровне концепции, но и находил свое применение в реальных условиях, принося пользу. Мне кажется, что такая идея вполне могла бы быть реализована в продуктах ООО «ОЦРВ».

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

Согласно данным РБК: «В январе — декабре 2023 года иностранцы посетили Россию с туристическими целями 670,7 тыс. раз.» Таким образом, модуль может оказаться полезным для дальнейшего развития проектов в рамках ООО «ОЦРВ»)

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

Полезные ссылки

© Habrahabr.ru