HiFIGAN by teasgen
Отчет об обучении HiFiGAN на датасете LJSpeech в рамках курса DLA AMI HSE
Created on December 7|Last edited on December 8
Comment
ОбучениеРезультатыПроверим качество генерации In domain Wav -> Mel Spec -> WavПроверим качество генерации Out of domain Wav -> Mel Spec -> WavПроверим качество генерации Text-> Mel Spec -> WavВывод
Обучение
HiFiGAN - Vocoder GAN модель, обученная по Mel-спектрограмме генерировать Wav.
Генератор

вид сверху

чуть подробнее
Cтакаем TransposedConv1d поверх мел спеки, чтобы привести по длине к длине wav. Параллельно манипулируем с каналами так, чтобы количество hidden каналов увеличивалось каждый раз на 2, а потом свели к 1 каналу (в wav 1 канал)
В статье авторы эксперемнтируют с тремя версиями генератора, я выбрал V1 (эксперементировал с V2 при дебаге, но так как планировал учить долго, то решил взять модель побольше)

версии генератора
Дискриминатор

вид сверху на дискриминаторы
Multi-period Discriminator (MPD) - авторы вспоминают, что звук это сумма синусоид, поэтому трансформируют 1-d wav в 2-d тензор так, что каждый p-ый элемент 1-d встает в соответствующую строку в новом 2-d тензоре, то есть авторы так пытаются учесть частоту. Как в статье, я использовал в коде (то есть несколько дискриминаторов). Так как генератор не выдает длину всегда кратную всем , то я паддил аудио до ближайшего кратного с помощью отражающего паддинга

чуть подробнее MPD
Multi-scale Discriminator (MSD) - авторы как порядочные люди просто ссылаются на другую статью MelGAN, откуда я и нагло своровал все параметры, так что не знаю на самом деле какие параметры блоков использовали в MSD HiFiGAN, но у меня хорошо обучилось и с MelGAN архитектурой

чуть подробнее MSD из статьи MelGAN
Loss
4 части
- стандартный лосс генератора и дискриминатора

- еще хотим совмещать спектрограммы генерируемого wav и реального

- и чтобы совсем ближе делать генерацию вавки, то давайте еще сближать фичи дискриминаторов с каждого слоя для сгенерированной wav и реальной

Лосс дискриминатора - стандартный
Лосс генератора -
Обновление весов
- Считаем градиент по дискриминатору, при этом не забывая сделать detach для выхода генератора
- Считаем градиент по генератору
Баги
- Надо делать когда логгируешь тензоры, иначе быстро ООМимся по cuda памяти
- Долго сидел с тем, что ничего не училось, хотя на ванбатче все было ок. Случайная мысль через несколько часов (p.s вспомнил что год назад видел такое) - я считаю неправильно лосс на мультибатче. И пошел просто проверять все тензоры, в итоге нашел, что mel spec для gt была вида [BS, C, L], а mel spec для generated была вида [BS, 1, C, L], происходил неправильный бродкаст и ломался.
Нашел загадочные слова в статье "weight normalization" и выяснил, что это означает, что нужно обернуть все свертки в модели в torch.nn.utils.weight_norm (p.s только в MSD генераторе для первого дискриминатора без пуллинга использовали спектральную нормализацию, я на 100% уверен, что это неважно вообще, но ладно)
Когда пофиксил баги и вставил нормализацию, то все запустилось
Рецепт обучения
- Оптимизатор - для генератора и дискриминатора AdamW с wd = 1e-2 и
- LR = 3e-4 для G
- LR = 2e-4 для D (< LR_G чтобы чуть медленнее обновлялся, но это скорее для души, на деле вряд ли имело значение)
- Шедулеры
- 1 стадия обучения (стадии описаны чуть ниже) - ExponentialLR для G и D с gamma = 0.999
- 2-3 стадии обучения (стадии описаны чуть ниже) - OneCycleLR c Cosine убыванием
- Клипал градиенты для G до 10
- При обучении выбираю рандомный подсрез аудио заданной длины
Авторы статьи учили на отрезках аудио длиной 8k, а решил пойти чуть дальше, так как в дз в итоге нам надо генерировать длинное аудио и сделал 3 последовательных обучения
- Делаем жесткий претрейн на 8к длину (~0.33 sec) 60 эпох - в итоге получаем робовойс. BS = 64
- Делаем жесткий файнтюн на 22к длину (~1 sec) еще 400 эпох - получаем хороший голос. BS = 32
- Понижаем init LR в 10 раз. Жестойчаще файнтюним на 44к длину (~2 sec) еще 100 эпох - получаем не отличимую генерацию на LJ, при этом по лоссам на тесте не переобучаясь. BS = 16
В графиках разрыв в итерациях из-за того, что я меня батч сайз, чтобы влезать на карту, поэтому просчет итераций немного поехал, но по запускам в wandb легко прослеживается цепочка
Итак,
- После 8k обучения - заметны различия в спеки, слышен робовойс
- После 22k обучения - спеки на глаз тяжело отличимы, в голосе слышны редкие подрагивания, но никакого робовойса уже давно нет
- После 44k обучения - спеки неотличимы на глаз, голос идеально повторяет оригинал на мой скромный слух
Причем судя по L1-Mel test у меня нет переобучения под train, а значит я хорошо генерализовался In domain.
Теперь перейдем к результатам
Результаты
Все числа получены для модели, обученной на 44k с лучшим чекпоинтом по L1 метрике спек на тесте (это почти последняя эпоха)
Проверим качество генерации In domain Wav -> Mel Spec -> Wav
Для этого я взял 5 случайных wav семплов из теста LJ, сделал для них Mel Spec и сгенерировал вавку
Имхо я не слышу никаких отличий gt от synt
- не вижу в вавке артефактов
- не вижу артефактов в спеке + не вижу отличий в спеке, но L1 метрика показывает не 0, так что все же генерация не идеальная, но для человека неотличима
| Wavs | WVMOS |
|---|---|
| Ground truth | 4.20 |
| Generated | 4.01 |
Проверим качество генерации Out of domain Wav -> Mel Spec -> Wav
Тут получается результат явно хуже и можно легко отличить генерацию от оригинала, хотя все слова слышны хорошо
Мое предположение почему так - происходит жесткий out of domain, я учился много эпох на LJ с голосом одной девушки, поэтому vocoder переобучился под ее голос и не может повторять другие, так что отличаются частоты (по спекам видно, что они довольно отличаются от секции выше). Хотя при этом все произносится четко, без постороннего шума
| Wavs | WVMOS |
|---|---|
| Ground truth | 3.43 |
| Generated | 2.52 |
Проверим качество генерации Text-> Mel Spec -> Wav
Text -> Mel spec я делал в коде с помощью https://github.com/pytorch/hub/blob/master/nvidia_deeplearningexamples_waveglow.md#example так как у ребят такой же способ получчание спеки из аудио и очень легко переиспользуется модель.
| Wavs | WVMOS |
|---|---|
| Generated | 3.70 |
Я считаю получившиеся аудио очень неплохого качества, neuro-MOS у них около 4 (по оценке WVMOS). Единственное отличие от генерации коротких звуков из первой секции результатов - появляется будто немного фон, по которому слушатель может определить, что это генерация. Есть два решения
- Поучить на более длинные последовательности
- При генерации спеки по тексту я делил текст по мини текстам между запятыми, иначе энкодер не работал с длинными последовательностями. Дальше спеки объединял в одну большую. Длинная спека дает артефакты, поэтому можно было бы генерировать вавку на мини спеки и потом уже соединять вавки
Однако в дз требовалось добиться качества - "слова понятны, шума нет"
В этой секции я мог бы дополнительно прогнать модель на текстах из LJ, но хорошее качество на длинных текстах и ��ак говорит за себя, поэтому проверка на LJ транскрипциях избыточна.
Вывод
Я обучил HiFiGAN на генерации аудио по mel-спектрограмме. При условии генерации голоса из обучения результат получается впечатляющим, на новые голоса генерализоваться не получилось. WVMOS к моему разочарованию очень плохо показывает качество wav, потому что для GT семплов далеко не 5 ставил. Однако по метрикам на тесте и слепому тестированию эта модель отлично обучилась, поэтому если потребуется адаптировать вокодер под разнообразные голоса, то надо расширять обучающую выборку, но я просто чилловый парень и сделал лучшее на этих данных

я
P.S спасибо за курс
Add a comment