В 21 веке лавинообразно распространяется телефонное мошенничество, а доля разоблачения и поимки таких преступников мала. Можно ли определять мошенников в первые минуты разговора, если их телефонные номера постоянно меняются? Рассмотрим в статье. В какой-то момент устав от проблемы телефонных мошенников, мы задались вопросом их идентификации до того момента, когда они полностью завладеют нашим вниманием и нашими средствами. Да, крупные компании предлагают установить бесплатные определители номера, которые оповещают о подозрительных номерах. Но принимая во внимание, что телефонные номера у мошенников постоянно меняются, обозначенные определители не дают высокого уровня защиты. Помимо номера есть ещё голос мошенников. В данном ключе неопределённость о том, что мошенник может намеренно менять голос с помощью технических средств, мы опускаем в связи со сложностью их технической реализации, а навыки подражателя для ML моделей не страшны. Поэтому мы хотим создать модель, которая будет работать параллельно разговору и идентифицировать говорящего. Так, набрав базу из записанных телефонных разговоров и выбрав точно определённые беседы, мы сможем обучить модель на нужных голосах. Базовый подход к работе со звуковыми данными в ML заключается в предобработке записей: в сэмплировании звуков с заданной частотой дискретизации; в выделении частот быстрым преобразованием Фурье и расчёте спектральных и мел-частотных кепстральных коэффициентов; расчёте длительности сигнала, средней частоты, стандартного отклонения частоты, медианы частоты квантилей (в кГц, например), коэффициентов асимметрии, эксцесса, спектральной энтропии и плотности, центроида частоты, пиковой частоты, фундаментальной частоты, частоты перехода через ноль, частоты цветности (в случае со спектрограммой) и т. п., и дальнейшей передаче полученных датасэтов с признаками на вход моделям (нейронным сетям или другим классификаторам). Конечно, это всё круто и интересно, но затратно по времени и не всегда полезно заниматься подробным «feature engineering»-ом. Есть ли способ «побыстрее» в плане чтобы «загрузил модель, настроил, и она работает»? Думаем, есть. Обратим взор на два проекта (это наш выбор, но список этим не ограничивается): набор инструментов NVIDIA NeMo и фреймворк wav2vec2 от платформы Meta (facebook). Wav2vec2 - это фреймворк для репрезентативного обучения речевых моделей. Он следует двухэтапному процессу обучения, включающему предварительное обучение и тонкую настройку (дообучение) и хорошо справляется с задачами распознавания речи, особенно в случаях ограниченности ресурсов. Модель wav2vec2 обучена на немаркированных файлах с более чем 50 000 часов устной речи. Подобно моделированию языка с маскировкой БЕРТА, модель изучает контекстуализированные речевые представления путем случайной маскировки векторов признаков перед передачей их в сеть transformer. На рисунке приведена иллюстрация w2v-кодировщика и его предварительного обучения. Основная часть модели состоит из кодировщика на основе CNN (свёрточной нейронной сети), контекстной сети на основе трансформера и модуля квантования. CNN-кодировщик преобразует необработанный звук X в скрытые речевые представления Z. Контекстная сеть объединяет 12 блоков трансформеров с 8 слоями внимания. Затем к замаскированным представлениям добавляется относительное позиционное вложение. Преобразователь контекстуализирует замаскированные представления и генерирует контекстные представления C. Модуль квантования используется для дискретизации скрытых речевых представлений Z в Q. В модуле квантования имеется G = 2 кодовых книги. Каждая из них содержит V = 320 записей размером 128. Модуль квантования сначала преобразует Z в логиты. Затем Функция gumbel softmax используется для выбора одной записи из каждой кодовой книги полностью дифференцируемым способом. Все выбранные элементы объединяются в результирующие векторы [e1; e2; ...; EG], которые линейно размечаются на q. Но в таком исходном виде модель нам не поможет, так как мы пытаемся идентифицировать говорящего без привязки к тексту. Необходимо произвести finetuning с некоторой доработкой. Так согласно статье необходимо в модель добавить средний объединяющий слой и полносвязный слой поверх w2v-encoder. После этого модель необходимо настроить, произвести finetunig. Это можно сделать, но нам бы хотелось более «коробочного» решения. Поэтому мы обратились к разработкам Nvidia. NVIDIA NeMo - это набор инструментов для создания новых современных моделей разговорного искусственного интеллекта. В NeMo есть отдельные коллекции для моделей автоматического распознавания речи (ASR), обработки естественного языка (NLP) и преобразования текста в речь (TTS). Каждая коллекция состоит из готовых модулей, которые включают в себя все необходимое для обучения на ваших данных. Каждый модуль может быть легко настроен, расширен и составлен для создания новых архитектур моделей разговорного искусственного интеллекта. Распознавание говорящих (speakers recognition ­ SR) - это обширная область исследований, которая решает две основные задачи: идентификация говорящего (кто говорит?) и проверка говорящего (является ли говорящий тем, за кого он себя выдает?).  Нам интересно распознавание говорящего без привязки к тексту, то есть мы не отслеживаем особенности построения фраз и диалекты. Обычно такие системы SR работают с неограниченными речевыми высказываниями, которые преобразуются в векторы фиксированной длины, называемые эмбединги дикторов/говорящих. Эти эмбединги также используется в автоматическом распознавании речи (ASR) и синтезе речи. Так как нам не важно, что говорит диктор, то не важно и на каком языке. Поэтому для предварительного обучения мы можем использовать датасет HI-MIA, например. Высказывания в нём были записаны по сценарию «далеко от микрофона и с посторонними шумами» и «близко к микрофону с шумами», чтобы модель научилась определять именно голос и манеру произношения. Также нужно учесть, что все аудио звуки нужно привести к 16 кГц. (можно с помощью модуля librosa) Для нашего эксперимента установим такие зависимости: Устанавливаем NeMo: Устанавливаем TorchAudio: Получаем данные: Загрузим датасет: В процессе скачивания через указанный модуль файлы уже были преобразованы и созданы json-манифесты для обучения.  Но если вы будете использовать, например, an4 датасет или какой-нибудь другой, то придётся выполнить следующие строки (на примере an4): Сначала получим файл (ы) scp, содержащий все файлы wav с абсолютными путями для каждого из наборов train, dev и test. Это можно легко сделать с помощью команды find bash Если посмотреть на первые 3 строчки scp файла для обучения: То получим, например, такой вывод: Поскольку мы создали scp-файл для обучения, мы используем модуль scp_to_manifest.py, чтобы преобразовать этот файл scp в файл манифеста, а затем при необходимости разделить файлы на «train & dev» для оценки моделей во время обучения с помощью флага --split. Нам не понадобилась бы опция --split для тестовой папки. Соответственно, укажем id номер поля, разделенного символом /, которое будет рассматриваться как метка диктора/говорящего. После загрузки и преобразования ваша папка с данными должна будет содержать каталоги с файлами манифеста в таком виде: data/<path>/train.jason data/<path>/dev.json data/<path>/train_all.json Каждая строка в файле манифеста описывает обучающий образец - audio_file path, содержит путь к файлу wav, duration - это длительность в секундах, а label - это метка класса говорящего: {«{«audio_file path»: «<absolute path to dataset>data/an4/wav/an4 test_clstk/menk/cen4-menk-b.wav», «duration»: 3.9, «label»: «menk»} Итак, создадим манифесты: Сгенерируем scp для тестовой папки, а затем преобразуйте его в манифест. Задаём путь к файлам манифеста: Поскольку цель большинства систем, связанных с определением дикторов, состоит в том, чтобы получить хорошие эмбединги по дикторам, которые могли бы помочь отличить их друг от друга, мы сначала обучим модель end-to-end методом, оптимизируя модель TitaNet. Мы модифицируем декодер, чтобы сделать эти эмбединги фиксированного размера независимыми от длины входного аудио. Модель основана на архитектуре ContextNet ASR (ASR – автоматическое определение речи), состоящей из структуры кодера и декодера. Используется кодировщик модели ContextNet в качестве средства извлечения признаков верхнего уровня и передаёт выходные данные на объединяющий слой внимания. Этот уровень вычисляет характеристики внимания по измерениям канала, чтобы фиксировать представления дикторов на уровне произнесения, не зависящие от времени. Titanet - это сверточная модель с разделением каналов по глубине в масштабе 1D с архитектурой, подобной Contextnet, в сочетании со слоем группировки (pool) внимания каналов. На рис. 2 описывается кодировщик модели ContextNet-B×R×C и декодер объединения внимания, где B - количество блоков, R – количество повторяющихся подблоков на блок, а C – количество фильтров в слоях свертки каждого блока. Кодировщик начинается с блока пролога B0, за которым следуют мегаблоки B1 … BN−1 и заканчивается блоком эпилога BN. Блоки Prologue и epilogue отличаются от мегаблоков тем, что они оба имеют один и тот же модуль свертки (Conv), уровни batchnorm и relu слои и имеют фиксированные размеры ядра 3 в prologue и 1 в epilogue для всех предлагаемых сетевых архитектур. Они не содержат остаточные соединения и dropout слои. Каждый мегаблок начинается с разделяемого по временному каналу сверточного слоя с шагом 1 и расширением 1, за которым следуют batchnorm, relu и dropout. Вернёмся к процессу настройки модели. Импортируем необходимые модули. Модель TitaNet определена в конфигурационном файле, в котором объявлено несколько важных разделов. Разделы: Модель: все аргументы, которые будут относиться к Модели - препроцессоры, кодировщик, декодер, оптимизаторы и планировщики, наборы данных и любая другая связанная информация. учитель: Любой аргумент, который должен быть передан в PyTorch Lightning. Следующими строками мы выведем конфигурацию: Установим пути к манифестам: Чтобы включить его набор данных test_ds, добавьте его в конфигурацию и замените файл манифеста как config.model.test_ds.manifest_file path = test_manifest. Также установим количество классов в датасете: (для an4 будет 74). Модели Nemo являются модулями PyTorch Lightning и поэтому полностью совместимы с экосистемой PyTorch Lightning. Импортируем модули Проверяем доступность GPU, устанавливаем количество эпох, убираем признак стратегии и аугментации: У Nemo есть менеджер экспериментов, который обрабатывает протоколирование и контрольные точки для нас: TitaNet - это модель экстрактора эмбедингов диктора, которая может использоваться для задач идентификации дикторов - она генерирует одну метку для всего предоставленного аудиопотока. Поэтому мы инкапсулируем его внутри EncDecSpeakerLabelModel следующим образом: Любая модель NeMo по своей сути является моделью PyTorch Lightning, ее можно легко обучить в одну строку: Если у вас есть файл тестового манифеста, мы можем легко вычислить точность, выполнив: Мы можем значительно сократить время, затрачиваемое на обучение этой модели, используя обучение с несколькими графическими процессорами наряду со смешанной точностью. Смешанная точность: Тренажер с распределенным бэкендом: Можно и комбинировать эти флаги. NeMo также позволяет сохранять и восстанавливать модели. При использовании NEMO для обучения рекомендуется использовать фреймворк exp_manager. Ему поручено обрабатывать контрольные точки и ведение журнала. При использовании фреймворка exp_manager, у нас есть доступ к каталогу, в котором существуют контрольные точки. exp_manager с настройками по умолчанию сохранит для нас несколько контрольных точек - 1) Несколько контрольных точек с определенных этапов обучения. У них будут: --val_loss=tags 2) Контрольная точка в последнюю эпоху обучения обозначается символом --last. 3) Если модель завершит обучение, у нее также будет контрольная точка --last. Выведем последнюю точку: Чтобы восстановить модель используем метод: LightningModule.load_from_checkpoint(): restored_model = nemo_asr.models.EncDecSpeakerLabelModel.load_from_checkpoint(final_checkpoint) Для тонкой настройки все, что нам нужно сделать, это обновить конфигурацию нашей модели с помощью путей манифеста и изменить количество классов декодера, чтобы создать новый декодер с обновленным количеством классов. Настраиваем конфигурацию самой модели: Мы настроили данные и изменили декодер, необходимый для finetune, теперь нам просто нужно создать учителя и начать обучение с меньшей скоростью обучения в течение меньшего количества эпох. Для примера также изменим некоторые параметры trainer: Установим параметр учителя в восстановленную модель: Настроим оптимизатор и планировщик: Дообучение: Теперь мы можем сохранить всю конфигурацию и параметры модели в одном файле .nemo и в любое время восстановить из него В целом это всё. Данное руководство к действию не очень сложное для реализации, и им можно пользоваться. Для работы модели рекомендуем использовать GPU. Использование подобных моделей освобождает пользователей от долгих изысканий новых признаков для обучения модели. Добавим, что в github Nvidia (https://github.com/NVIDIA) есть ещё много полезных инструментов для ML. Профессиональное сообщество