Русский
Русский
English
Статистика
Реклама

Nlu

Голос в мобильном приложении учимся вызывать экраны и заполнять формы без рук

30.09.2020 16:04:54 | Автор: admin

Как быстро и бесшовно встроить голосовой интерфейс в ваше мобильное приложение? И как научить ассистента всему, что оно умеет? В прошлый раз мы взяли опенсорсное лайфстайл-приложение Habitica и показали, как добавить в него помощника и запилить базовый голосовой сценарий из коробки (уточнение прогноза погоды и времени). А теперь перейдем к более продвинутому этапу научимся вызывать голосом определенные экраны, делать сложные запросы с NLU и form-filling с помощью голоса внутри приложения.

(Читать первую часть туториала)

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

Логика голосового интерфейса

Начнем с самого простого логики на стороне приложения. Мы хотим по голосовой команде открывать, например, настройки или окно изменения характеристик. Открываем AndroidManifest и находим соответствующие активити. Находим PrefsActivity, который отвечает за настройки, FixCharacterValuesActivity, который отвечает за изменение характеристик персонажа, и до кучи находим активити, по которой открывается профиль и информация о приложении, FullProfileActivity и AboutActivity.

Согласно документации, нам нужно вносить клиентскую логику в класс, наследуемый от CustomSkill. Во-первых, укажем, что нам нужно реагировать только на ответ от бота, содержащий в response.action changeView. В response.intent мы будем передавать непосредственно команду, куда именно переходить и в зависимости от этого вызывать активити. Ну и не забудем перед этим найти контекст приложения:

class ChangeViewSkill(private val context: Context): CustomSkill<AimyboxRequest, AimyboxResponse> {    override fun canHandle(response: AimyboxResponse) = response.action == "changeView"    override suspend fun onResponse(            response: AimyboxResponse,            aimybox: Aimybox,            defaultHandler: suspend (Response) -> Unit    ) {        val intent = when (response.intent) {            "settings" -> Intent(context, PrefsActivity::class.java)            "characteristics" -> Intent(context, FixCharacterValuesActivity::class.java)//            "profile" -> Intent(context, FullProfileActivity::class.java)//            "about" -> Intent(context, AboutActivity::class.java)            else -> Intent(context, MainActivity::class.java)        }        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)        aimybox.standby()        context.startActivity(intent)    }}

Этот скилл добавляется к ассистенту следующим образом:

 val dialogApi = AimyboxDialogApi(                "YOUR KEY HERE", unitId,            customSkills = linkedSetOf(ChangeView()))

Навык и интенты

Навык мы будем писать наJAICF(это опенсорсный и совершенно бесплатный фреймворк для разработки голосовых приложений от Just AI на Kotlin).

Форкаем себе https://github.com/just-ai/jaicf-jaicp-caila-template.

К сожалению, на момент написания статьи на платформе JAICP(Just AI Conversational Platform) еще не было интеграции c Aimybox (SDK для построения диалоговых интерфейсов), иначе подключение было бы намного более простым просто через добавление одной строчки в один из двух файлов подключений в папке connections. А пока делаем новый файл подключения, который мы будем запускать для тестов. Создаем файл AimyboxConnection.

package com.justai.jaicf.template.connectionsimport com.justai.jaicf.channel.http.httpBotRoutingimport com.justai.jaicf.channel.aimybox.AimyboxChannelimport io.ktor.routing.routingimport io.ktor.server.engine.embeddedServerimport io.ktor.server.netty.Nettyimport com.justai.jaicf.template.templateBotfun main() {    embeddedServer(Netty, System.getenv("PORT")?.toInt() ?: 8080) {        routing {            httpBotRouting("/" to AimyboxChannel(templateBot))        }    }.start(wait = true)}

Для того, чтобы пользоваться NLU-функционалом, подключаем NLU-сервис Caila для этого регистрируемся на app.jaicp.com, в настройках находим ключ API и прописываем его в conf/jaicp.properties. Теперь мы можем прямо в сценарии ссылаться на интенты, которые пропишем на app.jaicp.com.

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

Для начала заведем интенты. Нам нужно распознавать, что пользователь хочет перейти в определенный раздел приложения. Для этого в сущностях мы заводим сущность под каждый из разделов, добавляя синонимы, и в DATA прописываем то, как мы будем распознавать это уже на уровне приложения (settings, characteristics, и т.д. из кода выше).

У меня получилось вот так:

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

По названию мы потом будем отсылать к этому интенту в коде JAICF.

Чтобы удостовериться, что интенты распознаются как надо, можно сразу ввести несколько тест-фраз по кнопке Тестирование . Вроде все ок.

Сценарий: вызываем скилл

Я на всякий случай потер все стандартные стейты, оставив только catchAll то, что бот говорит, если он нас не понимает. Создаем стейт changeView, в activators прописываем созданный нами в JAICP интент, а в actions прописываем логику нам нужно добавить в ответ бота, в стандартные реакции канала Aimybox всю информацию для того, чтобы сделать переход.

Просто достаем слот views из того, что распознала Caila, прописываем в action то, что мы прописали ранее, чтобы Aimybox знал, какой скилл запустить, и отправляем распознанный слот в интенте. Для красоты добавляем туда Перехожу. Все-таки ж чатбот.

        state("changeView") {            activators {                intent("changeView")            }            action {                reactions.say("Перехожу..." )                var slot = ""                activator.caila?.run {slot = slots["views"].toString()}                reactions.aimybox?.response?.action = "changeView"                reactions.aimybox?.response?.intent = slot            }        }

Скиллы лучше выносить в отдельный пакет skills с фаликом класса под каждый скилл.
Дальше вариантов несколько. Можно поднять бота локально через ngrok, можно воспользоваться heroku. Получившуюся ссылку прокидываем в app.aimybox.com, через создание там кастомного навыка, в поле Aimylogic webhook URL. В примеры пишем пару примеров вызова: открой настройки, открой инфо.

После подключения канала можно проверить выдачу прямо в консоли, чтобы отловить баги, по кнопке Try in Action.

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

Вроде все передается правильно. Попробуем в приложении. Весь код уже готов, осталось только запустить и попробовать.

Работает! Теперь самое сложное.

Заполняем задачи голосом

Хочется одной командой заполнить задачку, проверить, что все правильно, исправить какие-то небольшие ошибки (все-таки распознавание не всегда работает идеально), и только после этого создать ее окончательно.

Для этого сделаем второй скилл. Будем отличать его от первого через response.action == "createTask", а то, какой конкретно тип задачки создается через response.intent.

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

class CreateTaskSkill(private val context: Context): CustomSkill<AimyboxRequest, AimyboxResponse> {    override fun canHandle(response: AimyboxResponse) = response.action == "createTask"    override suspend fun onResponse(            response: AimyboxResponse,            aimybox: Aimybox,            defaultHandler: suspend (Response) -> Unit    ) {        val intent = Intent(context, TaskFormActivity::class.java)        val additionalData = HashMap<String, Any>()        val type = response.intent        additionalData["viewed task type"] = when (type) {            "habit" -> Task.TYPE_HABIT            "daily" -> Task.TYPE_DAILY            "todo" -> Task.TYPE_TODO            "reward" -> Task.TYPE_REWARD            else -> ""        }

В каждой из тасок (включая награды) есть название и описание, также есть сложность у задач и вредность у привычек. Давайте научимся прокидывать их.

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

Забандлим полученные данные и запустим таску с этим бандлом. Не забудем добавить обработку забандленного кода в onCreate TaskFormActivity.

// Inserted code for voice activation        textEditText.setText(bundle.getString("activity_name")) // presetting task name        notesEditText.setText(bundle.getString("activity_description")) //presetting task description        if (bundle.getBoolean("sentiment")) {  // presetting task sentiment            habitScoringButtons.isPositive = true            habitScoringButtons.isNegative = false        } else {            habitScoringButtons.isNegative = true            habitScoringButtons.isPositive = false        }        when (bundle.getString("activity_difficulty").toString()) { // presetting task difficulty            "trivial" -> taskDifficultyButtons.selectedDifficulty = 0.1f            "easy" -> taskDifficultyButtons.selectedDifficulty = 1f            "medium" -> taskDifficultyButtons.selectedDifficulty = 1.5f            "hard" -> taskDifficultyButtons.selectedDifficulty = 2f            else -> taskDifficultyButtons.selectedDifficulty = 1f        }

Теперь настроим распознавание и передачу в коде JAICF и в Caila.

Готовим Caila: заводим сущность под распознавание типов тасок, сложности и вредности (для примера я завел их с помощью паттернов, для этого нужно выбрать Pattern вместо синонимов в левой части формы).

Не забываем в data прописать данные, которые мы будем обрабатывать на клиентской стороне habit, pattern и так далее.

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

Делаем интент:

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

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

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

@ шаблоны и регулярки, примеры и семантическая близость.
Теперь сценарий в JAICF.

state("createTask") {            activators {                intent("createTask")            }            action {                val taskType = activator.getCailaSlot("taskType").asJsonLiteralOr("")                reactions.say("Перехожу...")                reactions.aimybox?.response?.action = "createTask"                reactions.aimybox?.response?.intent = taskType.content                reactions.aimybox?.response?.run {                    data["taskName"] = activator.getCailaSlot("taskName").asJsonLiteralOr("")                    data["taskDescription"] = activator.getCailaSlot("taskDescription").asJsonLiteralOr("")                    data["taskSentiment"] = activator.getCailaSlotBool("taskSentiment").asJsonLiteralOr(true)                    data["taskDifficulty"] = activator.getCailaSlot("taskDifficulty").asJsonLiteralOr("easy")                }            }        } private fun ActivatorContext.getCailaRequiredSlot(k: String): String =    getCailaSlot(k) ?: error("Missing Caila slot for key: $k")private fun ActivatorContext.getCailaSlot(k: String): String? =    caila?.slots?.get(k)private fun ActivatorContext.getCailaSlotBool(k: String): Boolean? =    caila?.slots?.get(k)?.toBoolean()private fun String?.asJsonLiteralOr(other: String) = this?.let { JsonLiteral(this) } ?: JsonLiteral(other)private fun Boolean?.asJsonLiteralOr(other: Boolean) = this?.let { JsonLiteral(this) } ?: JsonLiteral(other)

Подключаем интент через активатор, записываем из полученных слотов тип в intent, название и описание в data, и не забываем проставить action, чтобы Aimybox с клиентской стороны знал, какой скилл выбрать.

Проверяем, работает! Предлагаю включить звук и прочекать:

Да, это техническое демо конечно, с точки зрения продукта можно придумать сценарии поудобнее. Но об этом в следующих статьях!
Ссылка на репозиторий с навыком JAICF.
Ссылка на репозиторий с кодом Aimybox.

Подробнее..

ИИ-модель Microsoft превзошла результат человека в тесте на понимание естественного языка SuperGLUE

19.01.2021 10:15:06 | Автор: admin

Алгоритм понимания естественного языка (Natural Language Understanding, NLU)Microsoft DeBERTaпревзошел человеческие возможности в одном из самых сложных тестов для подобных алгоритмов SuperGLUE. На данный момент модель занимает первое место врейтингес показателем в 90,3, в то время как среднее значение человеческих возможностей составляет 89,8 баллов.

Рейтинг алгоритмов по прохождению теста SuperGLUEРейтинг алгоритмов по прохождению теста SuperGLUE

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

Для того чтобы добиться текущего результата в 90,3 балла, DeBERTa получила масштабное обновление архитектуры: теперь она состоит из 48 слоев и имеет 1,5 млрд параметров. Microsoft сделает публичной модель и ееисходный код. Кроме того, DeBERTa будет интегрирована в следующую версию Тьюринговой модели Microsoft Turing (Turing NLRv4).Тьюринговые модели используются в таких продуктах Microsoft, как Bing, Office, Dynamics и Azure Cognitive Services, чтобы совершенствовать, к примеру, взаимодействие с чат-ботами, предоставление рекомендаций и ответов на вопросы, поиск, автоматизацию поддержки клиентов, создание контента и решение многих других задач на пользу сотен миллионов пользователей.

Архитектура модели DeBERTaАрхитектура модели DeBERTa

В отличие от других моделей, DeBERTa учитывает не только значения слов, но и их позиции и роли. К примеру, в предложении a new store opened beside the new mall (англ. новый магазин открылся рядом с торговым центром) она может понять, что близкие по контекстному значению store (магазин) и mall (торговый центр) играют разные синтаксические роли (подлежащим здесь является именно store). Более того, она способна определять зависимость слов друг от друга. Например, DeBERTa понимает, что зависимость между словами deep и learning гораздо сильнее, когда они стоят рядом (термин глубокое обучение), чем когда они встречаются в разных предложениях.

Несмотря на то, что модель DeBERTa превзошла человеческие показатели в тесте SuperGLUE, это не означает, что ИИ-модель достигла уровня человека в понимании естественного языка. В отличии от машин, люди хорошо умеют использовать знания, ранее полученные при выполнении различных задач, для решения новых это называется композиционным обобщением (англ. compositional generalization). Поэтому, несмотря на многообещающие результаты DeBERTa в тесте, необходимо продолжить исследования, для того чтобы развить у модели этот навык.

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

Подробнее..

Биометрия, персонализация голоса, NLU и речевая аналитика о чем расскажут на конференции Conversations

05.06.2021 12:14:00 | Автор: admin

21 и 22 июня в Питере (и онлайн) пройдет конференция по разговорному ИИ Conversations. Поток для разработчиков будет посвящен conversational lego технологиям вокруг разговорного AI, из которых состоят чат-боты, голосовые ассистенты и диалоговые решения. Персонализация голоса от Yandex Speechkit, стратегия управления диалогом от DeepPavlov, платформа речевой аналитики Тинькофф Банка, голосовая биометрия от SberDevices, анализ и разметка интентов при создании голосовых ботов от X5 Retail Group и другие тулзы рассказываем, с чем еще можно познакомиться на Conversations V.

Доклад про лайфхаки разговорного UI на Conversations'19: как сделать бота и не разозлить пользователяДоклад про лайфхаки разговорного UI на Conversations'19: как сделать бота и не разозлить пользователя

Конференция будет проходить два дня Business Day (21 июня) и Technology Day (22 июня). Онлайн-билет дает доступ к двухдневному стриму!

Technology Day откроет секция Conversational Lego, посвященная технологиям, которые лежат в основе различных разговорных решений от чат-ботов с NLU до прокачанных голосовых ассистентов.

Кто выступит и о чем расскажут?

  • Поиск смысла в океане данных: анализ и разметка интентов при создании голосовых ботов. Антон Кленицкий, X5 Retail Group

  • Голос бренда. Зачем нужны технологии персонализации голоса и как они устроены. Никита Ткачев, Yandex.Cloud

  • Биометрический бум: как технологии помогают нам в повседневной жизни. Станислав Милых, SberDevices

  • Стратегия управления диалогом: как сделать чат-бота более умным и объемным с помощью дискурс-менеджмента. Данила Корнев, CPO, DeepPavlov

  • Особенности алгоритмов распознавания речи и их влияние на опыт пользователей. Николай Шмырев, CEO, АЦ Технологии

  • Как спроектировать диалоговый интерфейс для мультилингвальных ботов. Brielle Nickoloff, Cofounder & Head of Product, Botmock.

Больше имен и тем на сайте Conversations!

Еще разработчиков в Technology Day ждет секция по голосовым ассистентам, где Яндекс, SberDevices, Mail.Ru Group расскажут о новых фичах Алисы, Маруси и семейства Салют. Например, Алексей Фивинцев из Mail.ru покажет, какой профит можно извлечь из разработки голосовых навыков для Маруси, используя интеграцию Маруси с ВКонтакте.

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

В общем, будет много интересного! Один билет в стрим два дня экспертизы, доступ к приложению для нетворкинга и всем материалам конференции. За новостями можно следить в официальномтелеграм-канале Conversations.

Подробнее..

Обработка и анализ текстов на Python и Spark NLP

08.04.2021 22:07:23 | Автор: admin

В наше время без анализа и обработки текстов,не обходится ни один проект, и так уж сложилось что Python обладает широким спектром библиотек и фреймворков для задач NLP. Задачи могут быть как тривиальные: анализ тональности(sentiment) текста, настроение, распознавание сущностей(NER) так и более интересные как боты, сравнение диалогов в саппорт-чатах - мониторить следует ли ваша тех.поддержка или сейлз текстовым скриптам, или постобработка текста после SpeechToText.

Для решения задач NLP имеется огромное количество инструментов. Вот короткий список таковых:

Речь как вы понимаете пойдет о последнем, так как он включает в себя практически все что умеют выше перечисленные библиотеки. Существуют как бесплатные pre-trained модели так и платные, узкоспециализированные например для healthcare.

Для работы Spark NLP понадобится Java 8 - она нужна для фреймворка Apache Spark с помощью которого и работает Spark NLP. Для экспериментов на сервере или на локальной машине потребуется минимум 16Гб ОЗУ. Устанавливать лучше на каком нибудь дистрибутиве Linux (На macOS могут возникнуть трудности), лично я выбрал Ubuntu инстанс на AWS.

apt-get -qy install openjdk-8

Также нужно установить Python3 и сопутствующие библиотеки

apt-get -qy install build-essential python3 python3-pip python3-dev gnupg2

pip install nlu==1.1.3

pip install pyspark==2.4.7

pip install spark-nlp==2.7.4

Экспериментировать можно так же на colab. Работает Spark NLP по принципу конвейеров (pipeline), ваш текст проходит некоторое количество стадий которые вы описали в pipe-лайне, и каждая стадия производит описанные манипуляции, к примеру: пайплайн для получения именованных сущностей. Ниже на картинке схема наиболее часто встречающихся стадий которые будет проходить ваш входной текст, каждая стадия конвейера добавляет свою колонку с данными после ее выполнения.

Пример конвейера в Spark NLPПример конвейера в Spark NLP

Пример создания стадий для пайплайна. (весь код примера по ссылке на colab)

documentAssembler = DocumentAssembler() \    .setInputCol('text') \    .setOutputCol('document')tokenizer = Tokenizer() \    .setInputCols(['document']) \    .setOutputCol('token')embeddings = BertEmbeddings.pretrained(name='bert_base_cased', lang='en') \        .setInputCols(['document', 'token']) \        .setOutputCol('embeddings')ner_model = NerDLModel.pretrained('ner_dl_bert', 'en') \    .setInputCols(['document', 'token', 'embeddings']) \    .setOutputCol('ner')ner_converter = NerConverter() \    .setInputCols(['document', 'token', 'ner']) \    .setOutputCol('ner_chunk')nlp_pipeline = Pipeline(stages=[    documentAssembler,     tokenizer,    embeddings,    ner_model,    ner_converter])
  1. documentAssembler - создает аннотацию типаDocument,которая может использоваться аннотаторами в будущем

  2. tokenizer - разбивает текст и пунктуацию на массив строк

  3. embeddings -создает векторные представления для слов

  4. ner_model - распознаватель именованных сущностей. к примеру: October 28, 1955 = DATE

  5. ner_converter - добавляет колонку с отдельными распознанными сущностями October 28, 1955

И все конечно хорошо, но приходится как-то много кода писать - описывая стадии пайплайна и сам пайплайн, не говоря про подключение библиотек и инициализацию Spark NLP, поэтому разработчики SparkNLP (johnsnowlabs) сделали более высокоуровневую библиотеку или синтаксический сахар над SparkNLP - называйте как хотите, но когда мы попробуем повторить вышеприведенный пример:

import nlupipeline = nlu.load('ner')result = pipeline.predict(  text, output_level='document').to_dict(orient='records')

мы получим все те же самые NER, но написав на порядок меньше кода.

Хотелось бы еще отметить что оба варианта получения именованных сущностей, требуют некоторое время на инициализацию Apache Spark, предзагрузку моделей и установку связи интерпретатора Python c Spark через pyspark. Потому вам не особо захочется по 10-100 раз перезапускать скрипт с кодом выше, нужно предусмотреть предзагрузку и просто обрабатывать текст посредством вызова predict, в моем случае я сделал инициализацию нужных мне конвейеров во время инициализации Сelery воркеров.

# паттерн Реестрpipeline_registry = PipelineRegistry()def get_pipeline_registry():    pipeline_registry.register('sentiment', nlu.load('en.sentiment'))    pipeline_registry.register('ner', nlu.load('ner'))    pipeline_registry.register('stopwords', nlu.load('stopwords'))    pipeline_registry.register('stemmer', nlu.load('stemm'))    pipeline_registry.register('emotion', nlu.load('emotion'))    return pipeline_registry@worker_process_init.connectdef init_worker(**kwargs):    logging.info("Initializing pipeline_factory...")    get_pipeline_registry()

Таким образом можно выполнять NLP задачи без боли в мозгу и минимумом усилий.

Подробнее..
Категории: Python , Nlp , Spark , Nlu

Преобразование текстовых запросов в SQL

11.01.2021 14:11:20 | Автор: admin
Большинство разработчиков, когда-либо сталкивавшихся с NLP задачами, рано или поздно задумывались над проблемой, обозначенной в заголовке статьи. Решений подобного рода создавалось достаточное количество, каждое со своими особенностями, плюсами и минусами. Первое, с которым мы с коллегами встретились лет 10 назад, и ссылку на которое я не смог сейчас даже найти, было оформлено в виде абсолютно нечитаемой диссертации. Мы честно, шаг за шагом пытались прорваться сквозь ее страницы, но отчаялись и утратили интерес к данной тематике на несколько лет. Но, рано или поздно к этой проблеме возвращаешься. И в целом в индустрии интерес к данному вопросу уже не один раз разогревался и остывал, а в последние годы он снова на подъеме.

image


Системы преобразования текстовых запросов в SQL

Что должна уметь такая система:
  • Найти в тексте сущности, соответствующие сущностям базы данных: таблицы, колонки, иногда значения.
  • Связать таблицы, сформировать фильтры.
  • Определить набор возвращаемых данных, то есть составить select list.
  • Определить порядок выборки и количество строк.
  • Выявить, помимо относительно очевидных, некие абсолютно неявные зависимости или фильтры, непрозрачные ни для кого, кроме дизайнеров схемы базы (смотри условие по полю bonus_type на картинке выше)
  • Разрешить неоднозначности при выборе сущностей. Дай мне данные по Иванову следует запросить информацию по контрагенту или сотруднику с такой фамилией? Данные по сотрудникам за февраль ограничить выборку по дате найма или по дате продаж? и т.д.

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

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

Алгоритмы и правила

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

Нейросети

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

Советую прочесть небольшой цикл статей: 1, 2, 3, 4, 5, в них совсем немного теории, рассказ о том как проводится обучение и тестирование на качество, краткий обзор решений. Дополнительно здесь подробнее о SparkNLP. Здесь о решении Photon от SalesForce. По ссылке еще один представитель open source сообщества Allennlp. Здесь данные по качеству систем, то есть показатели тестов. Здесь данные по использованию NLP библиотек и в частности подобных решений в enterprise.

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

Примеры работы с системами, основанными на нейросетях

Зачастую компании, предоставляющие сервисы подобного рода, демонстрируют прекрасные результаты на отлично сделанных видео и далее предлагают связаться с ними для детальной беседы. Но есть и доступные в сети онлайн демо. Особенно удобно экспериментировать с Photon, так как в данном случае схема базы сразу перед глазами. Второе демо, которое я видел в свободном доступе от Allennlp. Разбор некоторых запросов удивляет своей изощренностью, некоторые варианты чуть менее удачны. Общее впечатление смешанное, попробуйте поиграть с этими демо, если есть интерес, и составить свое мнение.

image

В целом получается достаточно интересная ситуация. Системы автоматического перевода текстовых неструктурированных запросов в SQL, основанные на нейронных сетях, становятся все лучше и лучше, качество прохождения тестовых наборов все выше и выше, но все равно их значение не превышает в лучшем случае 70% (spider dataset порядка 69% на сегодняшний день). Можно ли считать этот результат хорошим? С точки зрения развития подобных систем да, безусловно, результаты впечатляют, но использовать их уже сегодня в реальных системах без доработки возможно далеко не для всех видов задач.

Инструменты Apache NlpCraft

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

С чего начать? Если мы хотим по максимуму автоматизировать процесс разработки, нам помогут метаданные схемы базы и сами данные. Перечислим какую информацию мы можем извлечь из базы и для простоты ограничимся таблицами, не будем пытаться анализировать триггеры, stored procedures и т.д.

  • Имена таблиц и имена колонок самая важная информация. Эти имена по сути определяют сущности нашей модели, мы можем искать их в тексте запроса напрямую или через синонимы.
  • Признак обязательности (null / not null) поможет правильно составить запросы с фильтром (where clause).
  • Отношения между сущностями, определяющиеся через объявленные foreign keys и уникальные индексы, выявляют связи типа 1:1, 1:0, 1:n, n:m. Информация необходимая для построения корректных joins.
  • Типы данных столбцов могут помочь в создании правильных фильтров. Так, например, условия сравнения могут применяться для числовых столбцов и гораздо реже используются для строковых и т.д. Также по типам данных могут быть сделаны выводы, стоит ли включать эти данные в select list.
  • Анализ самих данных. Простор для творчества безграничен. Если в каком-то столбце на все множество записей приходится лишь ограниченный повторяющийся набор значений можно предположить, что тип данных рассматриваемого столбца enumeration, это может пригодиться для построения запросов. И это только один пример.
  • Наличие индексов. Указывает на то, что по данным индексированным полям предполагается делать соединения между таблицами или осуществлять фильтрацию. Это знание очевидно пригодится в процессе формирования все тех же фильтров.
  • Primary and unique keys информация об уникальности записей, помимо многого другого, поможет составить подзапросы, возвращающие гарантированное количество строк.
  • Комментарии для таблиц и столбцов (имеющиеся, например, в Oracle) очевидная дополнительная информация к именам.
  • Check constraints знание ограничений может помочь в построении все тех же фильтров по данным столбцам.

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

CREATE TABLE users (    id number primary key,    first_name varchar(32) not null,    last_name varchar(64) not null unique,    birthday date null,    salary_level_id number not null foreign key on salary_level(id));

В реальности все будет не так уж и радужно, имена будут сокращены и нечитаемы, типы данных зачастую окажутся совершенно неожиданными, а денормализованные поля и впопыхах добавленные таблицы типа 1:0 будут разбросаны там и тут. В итоге, если быть реалистами, большинство существующих какое-то продолжительное время в продакшене БД, можно будет разве что с большим трудом использовать для распознания сущностей без какой-то предварительной подготовки. Это касается любых систем, а основанных на нейросетях, возможно, даже в большей степени, чем прочих.

В данной ситуации можно посоветовать дать NLP модулю доступ к несколько облагороженной схеме заранее подготовленному набору views, с правильными названиями полей, необходимым и достаточным набором таблиц и столбцов, тут же можно учесть вопросы security и т.д.

Приступаем к проектированию

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

  • Опросите пользователей и выпишите 30-40 наиболее стандартных типов вопросов.
  • Выявите запрашиваемые сущности, определите их соответствие сущностям базы данных, таблицам, столбцам и т.д.
  • Объедините группы запросов в интенты. Пусть интенту соответствует один параметризованный SQL, тогда для выбранных на первом этапе вопросов вы получите максимум 20-30 интентов. При этом оставьте один общий интент, по умолчанию. Вы можете сами формировать для него SQL или воспользоваться одной из ML библиотек text2Sql, о которых мы говорили выше.
  • Далее задача легко параллелится. С одной стороны описываете элементы через синонимы, для начала с помощью минимального набора, пусть даже одного слова, этот список может быть расширен позднее. С другой стороны отлаживаете параметризованные SQL запросы. C третьей формируете интенты и пишите тесты на сам факт их срабатывания, на первом этапе можно даже без выполнения реальных запросов.

При таких объемах работы и достаточных ресурсах, время, требующееся для решения подобной задачи, измеряется днями, и на выходе вы имеете 80% покрытия потребностей пользователя, причем с достаточно высоким качеством исполнения. Далее вернитесь к первому пункту и добавьте еще интентов.

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

Инструментарий и API

Apache NlpCraft предлагает инструментарий для упрощения процедуры работы с базой данных.

Порядок работы:
  1. Сгенерируйте шаблон модели по jdbc url базы данных. Как я уже упоминал выше, иногда лучше подготовить набор views с более правильной репрезентацией данных и предоставить доступ к этому набору. Генерация шаблона проще всего производится с помощью утилиты CLI. Запускаем утилиту, указываем в качестве параметров схему базы, jdbc driver, список используемых и игнорируемых таблиц и другие параметры, подробнее в документации.
  2. В результате работы утилиты получаем модель в виде JSON или YAML файла, описывающую таблицы, столбцы, связи между таблицами, простейшие синонимы и т.д., то есть все, что можно извлечь из данных и метаданных схемы базы.
    Пример описания для одной и таблиц и одного из ее столбцов схемы примера:
    - id: "tbl:orders" groups: - "table" synonyms: - "orders" metadata:   sql:name: "orders"   sql:defaultselect:   - "order_id"   - "customer_id"   - "employee_id"   sql:defaultsort:   - "orders.order_id#desc"   sql:extratables:   - "customers"   - "shippers"   - "employees" description: "Auto-generated from 'orders' table.".....- id: "col:orders_order_id" groups: - "column" synonyms: - "{order_id|order <ID>}" - "orders {order_id|order <ID>}" - "{order_id|order <ID>} <OF> orders" metadata:   sql:name: "order_id"   sql:tablename: "orders"   sql:datatype: 4   sql:isnullable: false   sql:ispk: true description: "Auto-generated from 'orders.order_id' column."
    
  3. Полученная модель это шаблон, полученный на основании анализа данных и метаданных, он сформирован для экономии вашего времени. Теперь его нужно внимательно изучить, расширить и возможно исправить. Дополнить список синонимов, уточнить, если потребуется, значения по умолчанию, сортировку, указанные межтабличные связи, таблицы дополнений и т.д. Вся документация приведена на сайте.
  4. На основании обогащенной модели разработчик может воспользоваться компактным API, существенно облегчающим построение SQL запросов в функции интентов смотри детальный пример.

Ниже для наглядности фрагмент кода:

@NCIntent(  "intent=commonReport " +  "term(tbls)~{groups @@ 'table'}[0,7] " +  "term(cols)~{    id == 'col:date' ||     id == 'col:num' ||     id == 'col:varchar'  }[0,7] " +  "term(condNums)~{id == 'condition:num'}[0,7] " +  "term(condVals)~{id == 'condition:value'}[0,7] " +  "term(condDates)~{id == 'condition:date'}[0,7] " +  "term(condFreeDate)~{id == 'nlpcraft:date'}? " +  "term(sort)~{id == 'nlpcraft:sort'}? " +  "term(limit)~{id == 'nlpcraft:limit'}?")def onCommonReport(  ctx: NCIntentMatch,  @NCIntentTerm("tbls") tbls: Seq[NCToken],  @NCIntentTerm("cols") cols: Seq[NCToken],  @NCIntentTerm("condNums") condNums: Seq[NCToken],  @NCIntentTerm("condVals") condVals: Seq[NCToken],  @NCIntentTerm("condDates") condDates: Seq[NCToken],  @NCIntentTerm("condFreeDate") freeDateOpt: Option[NCToken],  @NCIntentTerm("sort") sortTokOpt: Option[NCToken],  @NCIntentTerm("limit") limitTokOpt: Option[NCToken]): NCResult = {  val ext = NCSqlExtractorBuilder.build(SCHEMA, ctx.getVariant)    val query =    SqlBuilder(SCHEMA).      withTables(tbls.map(ext.extractTable): _*).      withAndConditions(extractValuesConditions(ext, condVals): _*).      ...    // Все прочие операции по построению SQL запроса из найденных    // сущностей.  }

Здесь представлен фрагмент функции интента по умолчанию, реагирующего на любой определенный в запросе элемент базы и срабатывающего если в процессе матчинга не было найдено ни одного более строгого совпадения. В нем продемонстрировано использование API SQL elements extractor, задействованного при построении SQL запросов, а также работа с SQL builder примера.

Что еще раз хочется подчеркнуть, Apache NlpCraft не предоставляет готовый tool для перевода разобранного текстового запроса в SQL, эта задача находится вне рамок проекта, по крайней мере в текущей версии. Код построителя запросов доступен в примерах, а не в API, он имеет существенные ограничения, но он и состоит при этом всего из 500 строк кода с комментариями, или около 300 без них. При этом, несмотря на всю свою простоту и даже ограниченность, даже эта простейшая имплементация способна сформировать нужные SQL для весьма существенного количества самых разнообразных типов пользовательских запросов. В настоящей версии мы предлагаем нашим пользователям, заинтересованным в построении подобных систем, использовать данный пример в качестве шаблона и развивать его под свои потребности. Да, это задача не на один вечер, но вы получите результат несопоставимо более высокого качества, чем при использовании в лоб универсальных решений.

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

Заключение

Построить систему обращения к базе данных задача не из простых, но Apache NlpCraft уже взял на себя немалую часть рутинной работы, и во многом благодаря этому, разработка системы достойного качества займет измеримое время и ресурсы. Будет ли Apache NlpCraft community развивать направление автоматизации перевода текстовых запросов в SQL и расширять этот простой SQL пример до полноценного API покажет время и запросы пользователей, формирующие план и направление развития проекта.
Подробнее..
Категории: Sql , Open source , Java , Api , Natural language processing , Nlp , Apache , Nlu , Nlpcraft

Из песочницы Исследовательский практикум. Голосовые виртуальные ассистенты что с ними не так?

14.07.2020 22:23:11 | Автор: admin

Введение


Аналитики, исследующие сервисы чат-ботов и виртуальных ассистентов, обещают рост рынка как минимум 30% в год. В абсолютных цифрах, по состоянию на 2019 год, рынок оценивался более чем в 2 миллиарда долларов в год. Виртуальных голосовых помощников выпустили практически все ведущие мировые IT-компании, а основную работу по их популяризации уже провели Apple, Google и Amazon.

image

На российском рынке тоже наметились свои лидеры в этой области. Первым крупным игроком, запустивших собственного голосового ассистента в России, стал Яндекс. По данным компании, публикуемым официально, Алисой пользуется 45 миллионов пользователей в месяц, а число ежемесячных запросов к ассистенту составляет более 1 млрд. По мнению специалистов, 2020 год может стать для рынка голосовых ассистентов переломным конкуренция платформ и брендов приведет к росту узнаваемости ассистентов.

В общем, то, что рынок голосовых ассистентов интересная ниша, сомневаться не приходится. И первая идея, которая приходит в голову взять любой из доступных сервисов ASR (Automatic Speech Recognition) и TTS (Text To Speech), связать их с конструктором ботов, имеющим поддержку NLU (Natural Language Understanding), и все! Тем более что все это довольно легко и быстро можно реализовать в облачных платформах, таких как Twilio и VoxImplant.

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

Как работает типичный голосовой ассистент


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

image
Рис. 1. Речевой сигнал

Алгоритм работы ассистента:

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

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

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

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

    На выходе данного шага мы имеем фрагмент речи, преобразованный в текст и готовый для дальнейшей обработки.
  2. Результат работы голосового ассистента, полученный на первом этапе передается боту, с поддержкой NLU для выявления интентов (intents), сущностей (entities), заполнения слотов (slots) и формирования текста ответа.

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

Возникающие проблемы


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

  1. Задержки
  2. Задержки
  3. Задержки

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

    Исходя из реальной практики по реализации голосовых ассистентов, в случае задержки более чем в 1 сек. голосовой ассистент довольно часто будет слышать от собеседника-человека характерные для проверки связи фразы: Алло! или Меня слышно?. Это особенно свойственно для случаев, когда разработчики пытаются скрыть тот факт, что с человеком общается робот, особенно при исходящих телефонных обзвонах, используя вместо синтеза речи набор заранее предзаписанных аудио-файлов, сделанных реальным человеком.

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

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

    И все это должно быть сделано за минимальный интервал времени!
  4. Перебивание голосового ассистента пользователем и попытки уточнить в процессе прослушивания ответа. В текстовых чатах эта ситуация невозможна, т.к. доставка контента осуществляется практически мгновенно. При работе же голосовых ассистентов это вполне обычная ситуация. Более того, к моменту окончания озвучивания ответа, собственно сам ответ может быть уже совсем не актуален, т.к. собеседник успел проговорить дополнительную уточняющую информацию.
  5. Единая длинная смысловая фраза может быть разорвана на несколько фрагментов. В текстовых чатах такая ситуация встречается довольно редко и характера только для стиля письма, когда каждое словосочетание или даже каждое отдельное слово отправляется отдельным сообщением. В работе голосовых ассистентов, особенно при попытке решить проблему с минимизацией задержек, данная ситуация довольно обычное дело.
  6. Обратная ситуация одна фраза может содержать несколько интентов и сущностей. Причем сущности могут относиться к разным намерениям. В текстовых чатах это встречается, но реже.

    Пример:

    В каком отделении Банка вам будет удобно забрать готовую кредитную карту?
    На Ленинском проспекте. А кстати, когда оно работает? Там далеко от остановки?

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

    Пример:

    А-а-а-а ну-у-у-у
    Ну, как бы вам сказать, типа того как э-э-э
    Сейчас, одну минутку, да-а-а Надо подумать, сразу так и не скажешь

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

Что же делать?


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

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

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

Если вернуться к алгоритму, который должен реализовывать голосовой виртуальный ассистент, то он может выглядеть следующим образом (для иллюстрации рассмотрим входящий вопрос: Где находится ближайший банкомат?):

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

    Результат:

    a) где
    b) находится
    c) ближайший
    d) банкомат
  2. Происходит накопление слов, поступивших на вход, во входном буфере

    Результат:

    a) где
    b) где находится
    c) где находится ближайший
    d) где находится ближайший банкомат
  3. Накопленный результат передается на обработку текстовому ассистенту, с поддержкой NLU, для выявления интентов и сущностей.

    Результат:

    a) Запрос: где. Результат: намерение не определено
    b) Запрос: где находится. Результат: интент Расположение отделения с вероятностью 50%, Расположение банкомата с вероятностью 50%
    c) Запрос: где находится ближайший. Результат: интент Расположение отделения с вероятностью 50%, Расположение банкомата с вероятностью 50%, сущность Точка привязки = Текущее местоположение
    d) Запрос: где находится ближайший банкомат. Результат: интент Расположение банкомата с вероятностью 100%, сущность Точка привязки = Текущее местоположение

    image
    Рис. 2. Алгоритм работы голосового ассистента
  4. Если добавление нового слова, полученного на 1 шаге, не просто изменяет веса вероятности определенных ранее интентов, а меняет их состав либо за счет увеличения количества выявленных интентов, либо за счет полной замены их набора, то выполняются следующие действия:

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

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

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

Способы повысить качество работы ассистента


Давайте рассмотрим какие есть методы, что бы еще больше повысить качество работы нашего голосового виртуального ассистента:

  1. Пересортировка ответов в выходном буфере

    Можно ввести параметр очередности интентов и соответственно вариантов ответа по приоритетам. Это значит, что сообщения из выходного буфера озвучиваются в порядке приоритета. Например, запрашиваемое местоположение банкомата следует озвучить до информации о доступном в нем операциях (снятие/внесение наличных, открытие депозита и т.д.) или наоборот.
  2. Настройки перебивания

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

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

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

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

    Дополнительно имеет смысл при выявлении подобной ситуации, в сообщение, которое было оставлено в выходном буфере добавить предсообщение Если я Вас правильно понял. Причина желательности такого добавления наличие противоречивых данных в выходном потоке, как правило всегда является следствием противоречивости данных во входном потоке. Поэтому имеет смысл дать человеку понять, что присутствует некоторый элемент неуверенности, который может подтолкнуть его к необходимости дать дополнительные уточнения.
  5. Определение пола собеседника

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

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

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

Особенность реализации бизнес-кейсов


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

Знаете, есть такое правило, справедливое для менеджеров по продажам То, что нельзя продать по телефону, не следует продавать по телефону. По этой самой причине ответ вида Ближайший банкомат находится по адресу является не информативным для человека. Если бы он хорошо знал район где сейчас находится, т.е. знал бы названия всех близлежащих улиц и номера домов, то скорее всего он и так бы знал где здесь находится ближайший банкомат. Так что такой ответ скорее всего сразу вызовет формирование другого вопроса: А где тогда находится названный только что адрес?. Гораздо более информативным ответом будет вариант: Ближайший банкомат находится примерно в ста метрах от вас по направлению на юго-восток, а еще лучше так же дополнительно отправить человеку сообщение типа location на Yandex или Google карты.

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

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

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

Совершенству нет предела


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

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

Еще одна важная рекомендация не стоит пытаться учить человека говорить. Он это и без нас умеет отлично, т.к. язык это и так привычный и понятный интерфейс. Наглядный пример плохого стиля: Чтобы прослушать данное сообщение еще раз, скажите: Прослушать заново. Мы ведь с вами в обычной жизни так не говорим. Разве нет? Лучше просто спросить: Послушать сообщение ещё раз или перейти к следующему?

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

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

Исследовательский практикум. Голосовой UX как сделать голосового виртуального ассистента лучшей версией человека

07.10.2020 18:18:13 | Автор: admin

Почему это важно?


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

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

image

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

Научите ассистента говорить правильно


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

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


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

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

Понимание состояния собеседника


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

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

Существуют три типа восприятия:
  1. Визуальный, зрительный тип. Человек этого типа воспринимает и организует свой опыт и мышление в основном посредством зрительных образов (Лучше один раз увидеть, чем сто раз услышать). В его лексике представлены слова зрительного ряда: имена существительные, глаголы, наречия и прилагательные, которые как бы описывают картину увиденного. Примеры: прозрачный намек, перспективная мысль, впечатляющий удар.
  2. Аудиальный (слуховой) тип. Он представляет мир в аудинальных (слуховых) образах. В лексике это представлено соответствующим рядом слов. Примеры: кричащая идея, оглушительный удар, немой намек, шумная или тихая фигура.
  3. Кинестетический тип. Это тип, который воспринимает и оценивает мир, прежде всего, с помощью ощущений и чувств. Ему присуща своя лексика: тяжелый или легкий вопрос, мощная идея, жесткий намек, сильная или слабая мысль, давящая фигура, скользящий или уверенный удар.


image

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

Использование речевых стратегий


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

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

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

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

Итак, рассмотрим самые распространенные речевые техники.

Закон причины и следствия


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

Пример:
Что посеете, то и пожнете.

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

Трюизмы


Трюизм (англ. truism) банальность. Это общеизвестное утверждение, не требующее доказательств и воспринимаемое как истина. Например:
Все мы иногда ошибаемся.

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

Еще один способ использования трюизма вызвать доверие к последующим утверждениям. То есть подстроиться:
Мы постоянно меняемся. Тариф Индивидуальный меняется вместе с тобой.

В качестве трюизмов часто используют поговорки или пословицы, которые мы просто по привычке воспринимаем как некую истину:
Кто рано встает, тому бог дает. Если вы придете ночью или рано утром, то вы сможете купить товар дешевле.

Предположения


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

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

Другой очень распространенный пример из жизни:
Будешь гулять купи хлеба.

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

Противоположности


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


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

Если в допущениях связываются между собой два события, то есть две статичные вещи, то в данном случае связываются между собой два процесса (может быть даже ведущие в разные стороны потому и противопоставления):
Чем больше сомнений, тем легче выбор!

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


Наглядные примеры:
Чем больше вы отвлекаетесь, тем внимательнее слушаете.
Чем внимательнее вы прочтёте договор, тем быстрее вы примете решение.
Чем сильнее болит голова, тем быстрее боль проходит.
Насколько больше ты потратишь времени на изучение, настолько лучше ты будешь знать этот предмет.


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

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

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


Двойные вопросы


Техника, когда в одной фразе содержится два вопроса. Первый идёт как вспомогательный, а содержание второй части вопроса подсознательно воспринимается как истина, хотя возможно это совершенно не так. Пример такой конструкции:
А вы заметили, что
Чувствуете, что


Примеры:
А вы заметили, как вам идёт этот костюм?
Чувствуете, как с каждым моим словом вам становится всё лучше?


image

Несмотря на кажущуюся простоту, считается, что данный приём обладает достаточно мощным эффектом.

Ложный выбор


Прием, когда как бы даётся возможность выбрать, но на самом деле фраза подталкивает к принятию решения:
Вы договор подпишете сразу или сначала прочтёте?
Вы хотите купить стиральную машину или холодильник?


Все выборы


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

Оценка


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

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


Пример:
Хотел бы я принимать решения, так же быстро, как ты.
Скрытый смысл: вы принимаете решения быстро, особенно, когда это нужно мне.

Всех удивляет качество этого фотоаппарата.
Скрытый смысл: качество этого фотоаппарата это истина.

Странно, что вы обратили на это внимание.
Скрытый смысл: мне не нравится ваше упорство.

Отрицания в командах


Это техника, которая оперирует с тем, что человеческое бессознательное не умеет работать с отрицаниями. Оно их просто игнорирует. И на использовании этой возможности все и основано.
Пример:
Не спешите начать всё заново.
Скрытый смысл: спешите начать всё заново

Использование отрицаний позволяет аккуратно вписаться в контекст, и пройти через барьеры сознания (которое с отрицаниями как раз может справится).

Конструкция этой речевой стратегии крайне проста: вы определяете команду, а потом ставите перед ней частицу не.

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

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

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

Как следствие работы данной техники, сообщения о том, чего не будет, часто вызывают приток тех, кому-то чего не будет очень интересно:
Здесь вы не найдёте много интересного.

image
Подробнее..

Как мы анализируем предпочтения пользователей виртуальных ассистентов Салют

18.03.2021 10:09:11 | Автор: admin
Приветствую всех! Меня зовут Ибрагим, я работаю в SberDevices и занимаюсь машинным обучением. Сегодня я расскажу о том, как мы находим и анализируем интересы и предпочтения пользователей наших виртуальных ассистентов Салют.

Также поделюсь видео с моего недавнего выступления на онлайн-конференции Применение ML в Digital-продуктах, которую проводили коллеги из AGIMA и Epoch8.


В этом посте мы разберём следующее:

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

Вступление


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

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

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

Как мы используем BERT для задач NLU


Для начала поговорим немного об обработке естественного языка в контексте задач его понимания (natural-language understanding).

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

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



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



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

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

Находим первичные данные для нашей задачи


Переходим непосредственно к нашей задаче. Определение интересов и предпочтений пользователей на базе диалогов с виртуальным ассистентом это стандартная задача мультиклассовой классификации, когда для каждой реплики необходимо проставить один из классов. В диалоговом домене часто подобные задачи несут в себе определенные подводные камни. Например, большинство значащих, полезных классов в совокупности могут составлять лишь малую часть всего датасета (к примеру, не больше 1%). При этом 99% всех логов общения будут отнесены к классу, который можно назвать Другое или Нейтральное это всё, что не относится к вашей задаче. Поэтому подход, когда мы просто берем кусок датасета и отправляем его на разметку, может быть не самым рациональным. Имеет смысл сначала обучить модель, которая нам поможет достать часть датасета, несущую полезную для решения нашей задачи информацию. Потом проанализировать эту часть, понять, какие там есть классы, распределение между ними, сформулировать задание по разметке и сформировать конечный датасет для обучения необходимой нам модели.

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



Или же можно поискать дополнительные полезные источники информации. Этим способом воспользовались и мы. Помните, в детстве в журналах были такие разделы, где люди искали себе друзей по переписке? Они писали: Хей, меня зовут Ибрагим, я слушаю Blink-182, катаюсь на скейте. Давайте меняться напульсниками!. Мы нашли датасет подобных анкеточных писем, где не было личных данных или индексов, но предложения, реплики, где люди рассказывали о своих интересах, там присутствовали. Таким образом мы получили первую пару сотен реплик о том, где люди рассказывают что-то о себе, о каких-то своих предпочтениях.

Обогащаем датасет с помощью парафраз


Отлично, у нас есть первичный датасет. Теперь нам нужно его как-то обогатить.

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

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


Векторы, в которые мы кодируем наше предложение, по своей сути являются точками в признаковом пространстве. Что это значит? Это значит, что предложения, схожие по смыслу или по какому-то другому признаку, скорее всего, будут располагаться рядом. Например, предложения Мне нравится группа Metallica. и Я обожаю слушать тяжелую музыку. будут располагаться где-то рядом друг с другом. Это значит, что мы можем попробовать найти похожие по смыслу или синонимичные предложения к размеченным примерам, которые у нас уже есть. Такие предложения называют парафразами. Можно для каждого исходного предложения, для которого нам известен его класс, провести быстрый поиск ближайших соседей. Для этого можно воспользоваться библиотеками FAISS от Facebook, ScaNN от Google или другими и найти парафразы с определенным порогом по расстоянию. Таким образом можно обогатить изначальный датасет. И, условно, если у нас была пара сотен реплик, теперь мы можем получить пару тысяч реплик или предложений, где люди что-то рассказывали о своих интересах и предпочтениях.

Получаем полезную часть большого датасета


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



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

Кластеризуем данные, чтобы определить возможные будущие классы


Предложения, которые представлены точками в признаковом пространстве, могут образовывать группы, объединенные конкретным признаком. К примеру, группой может являться набор предложений, где люди рассказывают о любимой музыке, о любимых сериалах или книге. Поэтому можно взять все предложения, векторизовать их с помощью BERT, понизить размерность с помощью UMAP (потому что исходная размерность векторов BERT это 1024, если мы говорим про large модель). И затем кластеризовать полученные векторы с пониженной размерностью алгоритмом HDBSCAN. Таким образом можно получить группы и просмотреть глазами случайные предложения в них, чтобы понять, о чем люди рассказывают в диалогах. Теперь можно подумать, какие могут быть классы, подходящие под нашу задачу, и какое между этими классами распределение.



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

Немного о разметке данных


Полученный нами датасет со сформированным заданием можно отправить на разметку.

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

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

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

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

Предложения, в которых модель была уверена, но при этом ее предсказания не совпадают с проставленной разметчиком меткой, можно отправить на доразметку. Условно, дать задание: Выберите один из двух классов: либо представленный моделью, либо разметчиком. Переразметив всего 5% исходного датасета, можно здорово повысить его чистоту, а значит и качество работы будущей модели. Либо, если вы уверены в результатах работы модели, вы можете с ее помощью проставить автоматические теги.

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

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

Внедрение модели и оценка метрик


Теперь нужно внедрять саму модель. Здесь мы активно используем практики MLOps. Во-первых, мы активно версионируем все наши модели не только с датасетом и кодом, но и между собой. Для этого мы используем DVC, потому что особенно важно, когда у вас двухступенчатая архитектура, версионировать модели между собой, чтобы была консистентность и между большой моделью-векторизатором (в нашем случае BERT), и между маленькими нейронными сетями, которые решают свои собственные задачи.

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

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

Итоги


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

собрать первичный датасет с корректной разметкой;

обогатить его с помощью поиска ближайших соседей;

построить первую модель простой бинарной классификации (поможет нам вычленить максимально полезный участок датасета);

кластеризовать данные и оценить полученные классы и распределения;

обучить конечную модель (завернув всё в пайплайны, версионировав);

оценить результаты по метрикам.



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

***
Оставлю здесь ссылки по теме:

пост про то, как мы обучали BERT и как сделали его устойчивым для парафраз;
лекция моего коллеги для Sberloga о том, какие трюки мы использовали, чтобы сделать нашу модель лучше;
одна из итераций модели Bert-large для русского языка, которую мы используем, выложена в open source и доступна любому;
рассказ о том, как мы с коллегами из Сбера обучили и выложили в открытый доступ русскоязычную модель GPT-3.
Подробнее..

Hi-tech коммуникации, или как мы создаем голосового агента всего на 500 записях

18.03.2021 10:09:11 | Автор: admin
На Хабре не раз и не два писали о голосовых роботах, принципах их работы и задачах, которые они способны решать. Соответственно, общие принципы создания таких роботов (их мы предпочитаем называть цифровыми агентами) понятны многим. И это хорошо, ведь в этой статье мы хотели бы поговорить о быстром обучении роботов.

Нам удалось успешно обучать агентов на очень ограниченной базе звонков. Минимальное количество записей, на основе которых можно разработать полноценного цифрового агента всего 500. (Спойлер речь идет, скорее, о специализации ассистента, а не обучении с нуля). Как происходит обучение, и какие здесь есть подводные камни, особенности, что лежит в основе технологии? Об этом сегодня и поговорим.

Что должен уметь цифровой агент?


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

  • Определять в речи человека и классифицировать различные ответы, вопросы, возражения.
  • Подбирать подходящий по смыслу ответ или реакцию.
  • Определять кейсы, когда абонент не настроен на диалог и выражает негатив. Определять, когда абонент является ребенком и/или пожилым человеком, и корректно завершать звонок в таких случаях.
  • Определять в речи человека и фиксировать, если необходимо, различные сущности, которые называет абонент: имена, адреса, даты, номера телефонов и т.д.
  • Естественным образом реагировать на попытки перебить со стороны абонента. Так, если собеседник начинает говорить параллельно с ассистентом, последний останавливается, слушает возражение абонента и отрабатывает его. Пример разговора с перебиванием вы найдете чуть ниже.
  • Поддакивать и воспроизводить разные междометия (угу, ага) в уместные моменты, чтобы речь ассистента звучала максимально естественно.
  • Произносить, в зависимости от заданных условий (например, в зависимости от региона проживания конкретного абонента) различные переменные. Допустим, разную стоимость услуги или разные ее составляющие.
  • Воспроизводить на протяжении всего звонка background sound (фоновый шум). Это может быть, например, шум офиса, чтобы создать у абонента ощущение, что он общается с реальным сотрудником колл-центра и многие другие функции, т.к. это не весь важный функционал. Пример ниже.


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

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




Как обучить робота вести диалог


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

Как было раньше?


Несколько лет назад извлечение интентов и сущностей из речи человека и их классификация осуществлялись с помощью регулярных выражений (regex). Если по-простому это язык поиска по тексту. Для поиска используется строка-образец (она же паттерн), которая задает правило поиска. Чтобы установить правила поиска, в regex используется специальный синтаксис. Но у этого способа было несколько недостатков:

  • Необходимость в большом и квалифицированном человеческом ресурсе для создания регулярных выражений.
  • Необходимость постоянного анализа и ручной обработки больших объемов информации для улучшения качества распознавания система, работающая на регулярных выражениях, не способна к самообучению на размеченных данных.
  • Трудность, а иногда полная невозможность сложной классификации.
  • Ошибки, вызванные человеческим фактором.
  • Сроки подготовки регулярных выражений для конкретного голосового ассистента по сравнению с использованием интент-классификатора (NLU).
  • Средний срок подготовки паттернов (анализ диалогов, создание регулярных выражений на его основе, тесты, правки, доработки) для запуска проекта составлял порядка 3-7 дней; после этого для достижения необходимого качества требовалось еще несколько итераций анализа и масштабных доработок.

А что сейчас?


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

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

Сейчас обучение состоит из двух этапов: непосредственно обучение модели на датасете и дальнейшее дообучение в ходе коммерческой эксплуатации. На текущий момент подключение к движку NLU и экспресс-тесты распознавания занимают у нас всего несколько часов.
Качество, которое раньше достигалось неделями скрупулезной работы, сейчас обеспечивается сразу благодаря основной базе. К примеру, в сегменте b2c первоначальный % ошибок при распознавании согласия/отказа на совершение целевого действия уменьшился в 3 раза (с 10% до 2-3% от общего числа кейсов).

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

Для ее решения мы используем специализированный движок NLU Engine. Его основа семантический разбор текста, который поступает от ASR-систем. В ходе распознавания основных объектов распознавания два типа:

Entities (сущности).
Intents (намерения).

Важный момент: у нас не классическая диалоговая система в её традиционном понимании, система на машинном обучении не составляет ответ динамически, ответ определяется скрипт-дизайнерами. Фразы бота строго предопределены, никакой свободы воли для искусственного интеллекта здесь нет, к счастью или сожалению

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

Пример диалога


Ниже сложный HR-скрипт с обозначением intents и entities; распознанные сущности и интенты указаны под каждой репликой абонента и выделены курсивом.

Бот: Здравствуйте! Меня зовут Елена, звоню по поводу вашего резюме. Подскажите, предложение по вакансии актуально для вас?

Абонент: Да, здравствуйте

Intent: hello_confirm=true

Бот: Смотрите, сейчас у нас открыта вакансия специалиста офиса продаж. В обязанности входит консультация абонентов, продажа оборудования и работа с кассой. Оформление идет полностью официальное, белая зарплата, средний уровень дохода N рублей, максимальный уровень практически не ограничен, есть возможность карьерного роста. График 5 рабочих дней с двумя плавающими выходными. Также предоставляем все социальные гарантии, дмс, бонусы и скидки от компании.

Рассмотрите такие условия для себя?

Абонент: А еще раз, откуда вы звоните?

Intent: question =what_company

Бот: Это розничная сеть NNN

Абонент: А какой у вас график?

Intent: question=schedule

Бот: График работы формируется программой автоматически с учетом потока клиентов и нагрузки на офис с двумя плавающими выходными, то есть смены рабочие 6 8 10 12 часов в день могут быть и не более 40 часов в неделю. При этом график составляется заранее на 2 недели, то есть вы заранее знаете ваш график, ваши выходные, и можете уже планировать.

Абонент: Ну хорошо

Intent: confirmation=true

Бот: Давайте тогда сверим с вами анкетные данные
Подскажите, пожалуйста, ваши Фамилию Имя Отчество полностью

Абонент: Гудкова Елена Вячеславовна

Entity: name=true

Бот: Назовите, пожалуйста, вашу дату рождения

Абонент: тринадцатое ноль второе восемьдесят седьмого

Entities:
day=[30]
month==[February]
year==[1987]


Бот: Скажите, номер, на который вам звоню, могу записать как контактный?

Абонент: Да нет, наверное

Intent: confirmation=false

Бот: Подскажите тогда номер, по которому можно к вам обращаться?

Абонент: девятьсот десять восемьсот восемьдесят пятьдесят двадцать пять

Entity: telephone=true

Бот: Подскажите, какое у вас образование?

Абонент: Высшее

Entity: education=higher

Бот: Уточните, вы сейчас обучаетесь на очной основе?

Абонент: Нет

Intent: confirmation=false

Бот: Скажите, у вас гражданство Российской Федерации?

Абонент: Российское гражданство

Entity: citizenship=russia

Бот: Хорошо, это был последний вопрос. Спасибо, что ответили на мои вопросы, сейчас я отправлю анкету на рассмотрение руководителю. Анкеты рассматривают в течение 3 дней, и, в случае положительного решения, с вами свяжутся наши сотрудники. Всего доброго, до свидания.

Хорошо, скрипт разработан, что потом?


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

  • Способ интеграции.
  • Входные / выходные параметры.
  • Подключение SIP транка (если планируется к использованию телефония заказчика).
  • SMS-подключение или подключение к сторонним системам заказчика (CRM, Campaign management).

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

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

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

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

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

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

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

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

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru