javascript

Как и зачем определять голосовую почту

  • вторник, 12 декабря 2017 г. в 03:12:38
https://habrahabr.ru/company/Voximplant/blog/344224/
  • Разработка веб-сайтов
  • Программирование
  • JavaScript
  • CRM-системы
  • Блог компании Voximplant



«Абонент не абонент — пожалуйста, оставьте ваше сообщение после звукового сигнала!» — мы слышим этот автоматический ответ множество раз и уже привыкли вешать трубку, точно зная, что никто и никогда не проверяет «голосовую почту». Я, как и все опрошенные знакомые, без гугла даже не смогу ее проверить! Зачем операторам эта странная штука? А чтобы брать деньги за звонки, которые иначе будут бесплатными. Причем не только с обычных абонентов, но и с компаний, которые используют автоматику для звонков. Представьте себе магазин, который подтверждает заказы не колл-центром через полчаса, а роботом через десять секунд. И часть этих звонков «уходит» в голосовую почту, тратя деньги компании и ломая статистику. Под катом — детективная история про early media, big data, machine learning и TensorFlow.

Что за «бесплатные звонки»?


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


У такого решения есть плюсы и минусы. Если входящих и исходящих звонков примерно поровну — то «никто никому не должен». Больше входящих звонков — оператор зарабатывает деньги. Больше исходящих — тратит. Операторы хотят зарабатывать, поэтому всеми силами стараются максимизировать входящие и минимизировать исходящие. Одним из таких механизмов минимизации трат является договоренность об «Early Media».

Early Media — когда абонент не абонент


Что происходит, когда абонент «А» со своего сотового телефона звонит абоненту «Б», у которого тоже сотовый телефон? Много всего происходит, но если максимально упростить, то оператор «А» по текстовому протоколу SIP отправляет оператору «Б» запрос на звонок, а тот начинает через вышки искать абонента «Б» (на самом деле по SS7 поверх PRI, но не будем о грустном). Чтобы у абонента «А» в это время не было тишины в трубке и можно было продавать всякие «замени гудок», операторы договорились о состоянии «Early Media»: пока оператор «Б» ищет своего абонента, он может по SIP ответить «early media» и начать передавать звук по протоколу RTP. Гудки, музыку или «извините, абонент не абонент».

Также операторы договорились, что «early media» не будет тарифицироваться как входящий звонок, оператор «А» не платит оператору «Б» за эту музыку или гудки. А чтобы никто не читерил, еще договорились в состоянии «early media» звук отдавать только в сторону звонящего и обрывать такой звонок через 60 секунд. Хотя и при таких ограничениях находятся умельцы, делающие что-нибудь полезное в early media на «бесплатных» 8-800-, но это отдельная история. А наша история о голосовой почте.

Голосовая почта как «честный» способ взять деньги



Если оператор не нашел своего абонента — то он не заработал на входящем звонке денег. Телеком-операторы, как и любые коммерческие организации, деньги зарабатывать любят, поэтому была придумана гениальная «голосовая почта». Фраза «оставьте сообщение после сигнала» дает принимающему оператору возможность «принять» звонок даже когда абонент не доступен. Честно куда-нибудь записать 20 секунд тишины и, главное, взять за это деньги со звонящего оператора. Самые хитрые даже не ждут «пииип» и сразу принимают звонок — чего деньги терять?

Что человеку никак — то роботу беда-печалька


Абонентам сотовой связи голосовая почта, как правило, никак. Лично для меня нет разницы, будет в трубке сказано «абонент временно недоступен» или «абонент временно недоступен, оставьте ваше сообщение после сигнала». Я, как и все мои знакомые, повешу трубку на слове «недоступен». А какие копейки при этом один оператор заплатит другому за такой звонок — мне не очень интересно.

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

А ведь автоматика не ограничивается «позвонить после того, как покупатель нажал кнопку „купить“ на веб странице ритейлера и предложить нажать единичку или сказать „подтверждаю“, чтобы подтвердить заказ». Есть автоматические нотификации о, например, билете на концерт. Статистика показывает, что абоненту был звонок и он прослушал сообщение — а на самом деле сообщение «прослушала» голосовая почта. Или еще хуже: автоматика обзванивает клиентов чтобы, к примеру, обсудить условия заказанной уборки дома. Клиенту она синтезирует «привет, это робот такой-то компании, звоню по поводу заказанной уборки, соединяю с оператором», оператору синтезирует «дозвонились до такого-то клиента» и показывает карточку заказа в CRM, а дальше оператор 20 секунд разговаривает с тишиной в голосовой почте.

Первые попытки определить голосовую почту


Автоматикой телефонных и видеозвонков мы занимаемся давно, так что задачу определять голосовую почту начали решать несколько лет назад. Что общего у всех голосовых почт? У них у всех есть «пи-и-и-и-и», которое между «оставьте ваше сообщение после сигнала» и переводом звонка из «early media» в «accepted». Плохая новость — «п-и-и-и-и» у всех разное. Один гудок, несколько, на одной частоте, на двух, разной длительности и частоты. Более того — операторы любят этот «пи-и-и-и-и» время от времени менять. Интересно, зачем?..

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

Deep Learning. Везде Deep Learning


Потерпев неудачу с обычной математикой, мы решили, что надо попробовать перемножать матрицы. Ведь это не просто математика, а Deep Learning и Artificial Intelligence! Был установлен TensorFlow и закипела работы: записи разговоров и голосовых почт скармливались разным моделям в надежде, что они найдут невидимые нам паттерны: характерные временные задержки, ровная интонация, определенный набор слов, всё вот это.


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

Поэтому данные нужно было обработать. Мы подключили к Python специфичные телеком-библиотеки, написанные на С/C++ и реализующие логику работы с голосом: шумоподавление, эхоподавление, выделение несущей и многие другие. После обработки запись превращалась в набор параметров, на которых уже обучалась нейросеть.

Результат сразу стал гораздо веселее, и следующие полгода мы играли в IT-алхимиков: подбирали модель, варианты обработки входных данных и результатов применения модели, чтобы в результате по нескольким секундам записи определять голосовую почту. Результат получился очень хорошим — теперь достаточно безэмоционально начать разговор с фразы «Абонент временно недоступен», чтобы получить нотификацию о том, что скорее всего на другой стороне трубки голосовая почта. А что дальше делать с полученной информацией каждый клиент решает сам в облачном JavaScript. Для программиста использование детектора выглядит вот так: