Кто-то гадает по звёздам и кофе, кто-то — по фото, а нам в ВТБ пришлось обучить модель предсказания по телефонным звонкам. А заодно пройтись по граблям обработки текстов и звука, выяснить, какие фичи можно извлечь из аудио, как привлечь ML и прикрутить к нему нейросеть, — и сделать всё на основе opensource. 

Меня зовут Алексей Климачев, и я data scientist ВТБ. В прошлом году банк доверил мне провести исследование аудиоданных для целей collection. Если вам интересно, чем может быть полезна правильно обученная модель, анализирующая звонки и прогнозирующая их исход, что использовать для её обучения, с какими сложностями можно столкнуться в процессе и как их обойти, заглядывайте в статью.

Уж задали так задали

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

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

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

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

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

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

Выделяем фичи из аудио

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

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

Ещё важно учитывать качество записи: например, у студийных оно может быть очень высоким — 22 000 сигналов на секунду записи или даже больше. А у телефонных разговоров, которые послужили датасетом для модели, качество было существенно ниже — всего 8000 сигналов на секунду. Если качество аудиоданных различается, может потребоваться даже приведение их к «общему знаменателю» с помощью resampling.

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

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

Метрики и фичи

Метрики и фичи

MFCC — мел-частотные кепстральные коэффициенты. Мел (от слова «мелодия») характеризует высоту звука, воспринимаемую человеком на слух. MFCC представляют краткосрочную плотность мощности звука в коэффициентах и позволяют оценить «качество» голоса

Spectral Centroid — взвешенное среднее по величине частот сигнала, характеризует спектр звука и показывает расположение центра масс звука. По нему можно судить о «яркости» — высокой частоте звука

Spectral Rolloff — относительная частота, в пределах которой сосредоточена определённая часть всей энергии спектра (в нашем случае эта часть равна 85%). Проще говоря, показывает, в каких частотах сконцентрирована большая часть амплитуды

Zero Crossing Rate — доля (на длину звукового ряда) изменений звукового сигнала с положительного на отрицательный и наоборот. Чем она выше, тем менее отчётливее речь (а ещё она позволяет выделять ударные звуки)

Tempogram — предполагает разложение звукового ряда на коэффициенты, которые позволяют оценить его темп

Energy — нормализованная на длину ряда сумма квадратов величин сигнала: чем она выше, тем громче речь

Spectral Spread — среднее отклонение от центроиды, разделяет простые звуки (низкие значения) и шум (высокие значения) и характеризует диапазон частот звукового ряда

Spectral Flux — квадрат отклонения величин сигнала (текущего от предыдущего), позволяет разделять музыку и голос, т. к. у музыки более высокие значения Spectral Flux

Entropy of Energy — энтропия, рассчитанная по отрезкам звукового ряда (на energy), позволяет судить о резких изменениях в речи

Shannon entropy — энтропия, рассчитанная по отрезкам звукового ряда (на квадратах energy), тоже позволяет судить о резких изменениях в речи

Loudness — тут просто громкость речи

F0 — фундаментальная частота (наименьшая частота), можно использовать для выявления проблем с настроением

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

Shimmer — так называемое мерцание, почти как Jitter (и с тем же применением — выявлением изменений стиля речи), но использует амплитуду вместо частоты. Тоже 5 вариантов с разными способами вычисления

Detrended Fluctuation Analysis (DFA) — анализ колебаний, основанный на данных, из которых убран тренд (коэффициент). Позволяет выявлять отклонения в речи

Harmonics to noise ratio — отношение energy периодичного сигнала в звуке к energy шумного сигнала. Измеряет хрипоту в голосе

Впрочем, после размышлений последнюю фичу (которая Harmonics to noise ratio) решил не выделять: оказалось, на это требуется слишком много времени, оно того не стоит.

Для анализа аудио и извлечения фич я использовал библиотеки из открытых источников, в частности, выложенные на GitHub (например, раз, два).

Аугментации как способ расширить датасет

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

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

Извлекаем эмоции

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

И вот здесь мне повезло с открытыми источниками данных: я нашёл уже готовый датасет MELD, собравший около 10 000 отрывков из известного ситкома «Друзья». Для каждого отрывка была разметка с ранжированием по 7 типам эмоций: разочарованный, гневный, удивлённый, нейтральный, грустный, довольный, испуганный. Используя этот датасет, удалось извлечь фичи из аудиозаписей отрывков и бустингом обучить на них модель. В результате мы получили набор моделей для каждой эмоции, потом они применялись для возврата вектора вероятностей эмоций в аудиозаписи.

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

Нейросети в помощь

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

Что же получилось?

В результате у модели получилась следующая архитектура:

 Архитектура модели, предсказывающей успех звонка по звуку
Архитектура модели, предсказывающей успех звонка по звуку

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

MFCC-ряды подаются на вход в нейросеть. Я пробовал на этом этапе применять разные типы нейросетей: рекуррентные и свёрточные. Остановился на рекуррентной LSTM: она показала себя лучше.

Далее все полученные данные конкатенируются и подаются в FC-слой. А уже на выходе мы получаем вероятность успеха звонка по звуку. Что нам и требовалось.

А тем временем текстовая модель…

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

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

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

Сами тексты телефонных разговоров имеют иерархическую структуру: диалоги состоят из реплик, а те — из отдельных слов. Для работы с такими данными обычно применяют иерархические рекуррентные нейронные сети. В них один слой RNN кодирует последовательность слов в эмбединг-реплики, а второй слой — последовательность эмбедингов реплик в эмбединг всего диалога. Мы тоже могли воспользоваться этим способом, но выбрали другой путь. И он оказался даже лучше.

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

К полученной в результате последовательности BERT-эмбедингов реплик далее добавляли «флаги»: кто произнёс ту или иную реплику — оператор или клиент банка. Затем в таком виде данные пропускали через рекуррентную компоненту GRU, self-attention и несколько fully-connected слоёв. После чего на выходе модель могла предсказывать вероятность успеха звонка.

Использование BERT оказалось удачным шагом и позволило упростить и подготовку данных перед обучением модели. Как я уже писал, одна из характерных проблем текстов, полученных при распознавании телефонных звонков, — их сырой вид, без разбиения на предложения, без знаков препинания, зато с ошибками и прочим мусором. Так что изначально планировалось как-то причесать эти дикие тексты с помощью Spelling Correction или Denoising. Но когда мы попробовали улучшить модель с помощью такой предварительной обработки, оказалось, никакого прироста нам этот этап не даёт. То есть BERT даже сырые тексты спокойно прожевал и разобрался с ними, он и так умеет.

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

Готовая модель выглядела так:

 Архитектура модели, предсказывающей успех звонка по тексту
 Архитектура модели, предсказывающей успех звонка по тексту

Подводим итоги

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

— прирост +0,009 — при добавлении скоров по звуку;

— прирост +0,008 — при добавлении скоров по тексту.

А затем мы решили применить стекинг, объединить получившиеся удачные модели и посмотреть, что будет. Результаты получились неожиданными: при добавлении обоих скоров оценка составила +0,026. Больше, чем оценки моделей по отдельности.

И вот мы в конце пути. Или, вернее сказать, на перепутье: наши исследования завершены, модели предсказания успеха по звонку созданы, причём в трёх разных вариациях (по аудио, по тексту и по аудио и тексту), а дальше… Дальше предстоит ещё решить, как с наибольшей пользой применить эти модели.

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

Комментарии (1)


  1. Mr-Patty
    18.08.2022 18:26

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