Gradius
> Теоретически, сделать ошибку ещё меньше - можно. Но мешает неоптимальная кластеризация k-means в силу своей природы. Проблема называется: "богатый становится ещё богаче" - не все вектора одинаково и равномерно обучаются, плюс некоторые вектора остаются неактивными.
Пришёл к выводу, что самый эффективный способ уменьшить ошибку векторного квантования - позволить себе большие кодовые книги. Потому что слишком много существует вариантов фрагментов спектра на 32 полосы и 8 фреймов. Разнообразие зашкаливает.
Нейросеть сжимает размерность сегмента, обучается давать предсказания на других образцах. Но всё портит векторное квантование: ограниченность по кодовой книге резко понижает качество реконструкции. Без квантователя результат намного лучше.
Исходный сигнал:

Восстановленный сигнал (без квантователя, без сглаживания границ сегментов):

Восстановленный сигнал (с квантователем на 64K векторов 16x1, без сглаживания границ сегментов):

Поэтому, число векторов должно быть не меньше чем 65536. Всё, что меньше - уже даёт убогую реконструкцию.
Второй важный момент: если обучать сеть сегментами с шагом 8, то в конечном итоге сеть не устойчива к временным сдвигам. Чтобы это исправить, пришлось дополнить обучающий набор - сдвинутыми на +1, +2, +3, ... +7 фреймов - можно считать это своего рода аугментацией данных, которые повышают инвариантность к сдвигу.
Результат не заставил себя ждать - получился устойчивый к временным сдвигам конвеер сжатия сегментов.
Ради интереса, декодировал эмбидинги квантователя (вектора в кодовой книге) - посмотрел, что есть вообще эталонные сегменты. Вот так они выглядят (это не все, а только часть из 65536):

Это именно декодированное представление в виде сегментов 32x8.
Сама кодовая книга(внутреннее представление 16x1) выглядит шумоподобной (каждый вектор её нормализован по L2 - на поверхности гиперсферы):

И ещё главное: делал ResNet - генеративный способ улучшения спектра - на вход подавал восстановленный спектр с первой сети (сжатый), на выходе второй сети получил улучшенный спектр. Ошибка упала в 1,5 раза.
Но вот беда, ResNet имеет уже более 4 000 000 параметров обучения, против 1 500 000 параметров сети сжатия. Обучение замедляется и памяти ПК не всегда хватает, приходится обучать частями.
Итого битрейт здесь вышел: (16+8)*( 1000/80) = 300 бит/c. 16 бит - номер сегмента ( 8 фреймов - спектры + признаки вокализованности) 8 бит - основной тон ( интерполируется линейно по фреймам, не добавил в сетку, потому что звук блеет) 80 мс - длина 1 сегмента ( 8 фреймов по 10 мс)
Всё-же надо искать более компактное представление речевого сигнала. MEL-спектр на 32 полосы с восемью фреймами по 10 мс - это довольно большая размерность для сжатия.
Есть идея сжать подобным образом линейные спектральные пары с LPC или MELP вокодера. Там всего 10 полос(для частоты семплирования 8 кГц. Для 16 кГц достаточно будет 16-20), и что важно, они амплитудно-независимы и пространственно независимы почти. И определяют формантные максимумы и прочие перцептивные признаки звука, отметая действительно лишнее для речевого сигнала.
И потом второй сетью(ResNet) улучшить спектр. Конфета будет или нет, блин? :)
Ну и совсем непраздный вопрос- потянет ли это всё AW T113-s3 ?
И всё-таки, я что-то делаю не так... Какого размера не были бы данные для обучения (последнее обучение - 500 000 сегментов), результат на тестовых данных - хрень полная.
Причём, инвариантность к сдвигам также закладывается в обучении.
На обучаемых данных всё работает прекрасно, но стОит сделать новый пример - со своим же голосом, результат убогий.
Вот пример спектрограммы высказывания, которое не участвовало в обучающей выборке:

Вообще сегменты плохо стыкуются. Всё-же идея "крошить спектр на устреднённые куски" не приводит к успеху.
Купил книгу Ultra Low Bit-Rate Speech Coding Authors: V. Ramasubramanian , Harish Doddala.

Много чего нового узнал. Далее, я руководствуюсь содержимым этой книги.
Беплатный фрагмент книги выложен тут: https://play.google.com/books/reader?id=2roUBQAAQBAJ
P.S. И всё-же резать на сегменты надо в стационарных точках, а не равными долями
Gradius
P.S. И всё-же резать на сегменты надо в стационарных точках, а не равными долями
Это уже похоже на правду!
Обратимся немного к истории:
ГОЛУБЦОВ Стрит Вячеславович (1928–1977) – ученый-радиоинженер, изобретатель и конструктор, кандидат технических наук, один из пионеров автоматического распознавания слуховых образов (АРСО). В 1958 году из НИИ-2 был переведён в НИИ-3, где продолжал исследования в области закрытой низкоскоростной телефонной связи. Он являлся первым разработчиком ультра низкоскоростного фонемного вокодера в Союзе и методов автоматического распознавания речи [Голубцов, 1969], [Голубцов, 1972]. По этой же тематике им защищалась кандидатская диссертация.
Исследования Т.К. Винцюка имели целью создание «квазифонемного» вокодера с ультранизкими скоростями кодирования в диапазоне 600–150 бит.
Его работы по «квазифонемному» вокодеру основывались на возможности представления речевого потока последовательностью из 512 (1024) эталонных элементов – сегментов речи. Они показали, что со снижением информационной скорости все теснее становится связь ультранизких вокодерных алгоритмов с распознаванием речи [Винцюк, 1987], [uk.wikipedia].
Работы С.В. Голубцова и Т.К. Винцюка находились на передовых позициях в мировых исследованиях, ставивших целью создание фонетических и сегментных вокодеров, способных передавать разборчивую и узнаваемую речь [Ramasubramanian, 2012].
«фонетическая функция речевого сигнала» как универсального природного инструмента кодирования-декодирования речевой информации любого происхождения [Пирогов, 1967].
Ну тоесть выходит, что эту задачу (кодек речи со сверх-низким битрейтом, близким к лингвистическому фонемному битрейту) уже дано решили в XX веке.
Любые попытки найти труды вышеуказанных деятелей в интернете, не увенчались успехом.
Почему мы не наблюдаем открытых решений и реализаций этих кодеков?
Такие кодеки, как минимум, обладают следующими характеристиками:
1) Легковесный (модель ограничивается до 2 млн. параметров) 2) Битрейт от 200 бит/c и ниже 3) Потоковый 4) Хорошая разборчивость речи 5) Хорошая натуральность речи
Решил сделать заход с другой стороны: распознаватель фонем. Обучение делал на DARPA TIMIT. И тестирование тоже на нём. Обучил тем, что в папке Train. Тестировал тем, что в папке Test.
Пришлось сделать парсер данных: считывать WAV-файлы и фреймировать их в спектрограмму с 32-мя полосами. Убрал энергию фрейма, чтобы исключить зависимость от громкости. Плюс взял две производные по времени.
Итого шейп входного слоя нейросети такой: (BatchSize, 1, 32, 3). 1 - это по времени 1 фрейм, 32 полосы, и 3 канала: FB, dFB, ddFB.
Нормализовал данные по каждой полосе в пределах [0..1].
Обучил свёрточную нейросеть. Функция потерь - categorical_crossentropy, Метрика - accuracy.
1 фрейм - это окно Хемминга 25 мс со сдвигом 10 мс. Получилось около 1,5 миллиона векторов для обучающей выборки.
Сгенерировал метки фонем для обучающего набора: в границах каждой фонемы (парсятся из PHN файлов в БД TIMIT) - все фреймы имеют вероятность нужной фонемы 1.
Ван-хотом сделал матрицу размерностью 61 (число фонем + тишина): вероятность нужной фонемы:1, остальные фонемы - вероятность 0.
Прогнал 200 эпох. Accuracy вышла 97,5%. После 100 эпох Accuracy была 95%. Можно было додавить по-выше, но я пока оставил 97,5%.
Составил матрицу конфузий. Вышло красиво:

Или более подробно так:
Но на тестовых данных (папка Test в TIMIT) всё мягко говоря, неважно.
Матрица конфузий уже не так красива: диагональ слабая, много шума (ложных определений фонем):

Или более подробно так:
Коэффициенты нормализации для тестового набора брал те же, что и для тренировочного.
Архитектура сети (у последнего слоя Dense активация Softmax, у первого слоя Dense активация Sigmoid):

Ну тоесть, прекрасно работает только на чём обучил. На тестах заваливается, хотя род тестовых данных точно такой же, как и у данных обучения (БД та же - TIMIT папки Test/Train).
Что я делаю не так?
Gradius
> Ну тоесть, прекрасно работает только на чём обучил. На тестах заваливается, хотя род тестовых данных точно такой же, как и у данных обучения (БД та же - TIMIT папки Test/Train).
>
> Что я делаю не так?
Был классический случай переобучения. Нужно было дропауты раскидать после каждого слоя свёртки и после каждого прямого слоя.
Пробовал регуляризацию ядер/смещения/активации подёргать L1/L2(lasso/rigle) - эффективность обучения резко падает. И эффективность валидации тоже.
И ещё сделал охват не одного фрейма фонемы, а сразу несколько. Проще говоря - дал контекст слева и контекст справа.
Матрица конфузий стала по-лучше. Accuracy была 23%, стала 70% на данных валидации (не участвовали в обучении).
Дополнительно 61 классов фонем в TIMIT свёл в стандартные 39.
Плюс дополнительно ввёл ранний останов при обучении, мониторинг по валиации - если начинает просасывать, веса сохраняются с наилучшим случаем, когда Accuracy валидации максимальна.
Матрица конфузий:

Полее подробно с метками на осях:
Это конечно, ещё далеко от топовых распознавателей фонем (Accuracy 93-97%), но уже что-то вырисовывается :)
Gradius
> Это конечно, ещё далеко от топовых распознавателей фонем (Accuracy 93-97%), но уже что-то вырисовывается :)
Не удаётся сделать точность распознавания на проверочных данных выше 77%. Перепробовал все типы Аттеншенов (их очень много) - в лучшем случае даёт улучшение на 1-2%.
Кроме того, сеть с классификацией фонем - склонна к переобучению, как говорил ранее - пришлось вставлять дропауты после каждого слоя свертки и прямого. Дополнительно поставил небольшую регуляризацию L2 в веса. Интересный факт: при таком ограничении на переобучение - вначале точность на проверочных данных выше, чем точность на данных обучения. Затем, через несколько итераций они повышаются и выравниваются. Далее, как только точность на обучающих данных превышает точность проверочных - последняя перестаёт расти.
Проверил на автоэнкодере факт переобучения. Точно такая же фигня: сеть просто тупо выучивает обучающий набор, а на тестовом показатели MSE/MAE деградируют в разы. Но стоит сделать дропауты с регуляризацией (как их тюнить - это ещё один большой вопрос), то сразу картина меняется в лучшую сторону: на проверочных данных показатели MSE также падают.
Из чего делаю вывод, что без регуляризации ценность нейросетей - сомнительна: никаких скрытых зависимостей сеть не выводит без дополнительных телодвижений, а тупо занимается зубрёжкой данных на обучающем наборе.
А то "скрытые зависимости, обобщения, выучивание ценных скрытых представлений в латентном пространстве..." Ага, размечтались! Без жёсткой кастомизации полезный результат фиг получишь.
—
Запустил MELGAN: https://github.com/descriptinc/melgan-neurips
Это просто говорилка спектрограмм: на вход 80-полосная МЕЛ-спектрограмма, на выходе - звук.
Проверил: речь иногда с дефектами: частота основного тона иногда лажает (дрожит) и иногда слышны посторонние звуки в виде раската грома. Но в целом всё разборчиво.
Из плюсов MELGAN:
1) есть обученные модели - нашёл целых три:
linda_johnson.pt
multi_speaker.pt
best_netG.pt
Последняя модель даёт слегка детский голос, что может быть полезно в некоторых применениях.
2) Нужна только МЕЛ-спектрограмма, без всяких f0 и ap(v/uv).
До этого воспроизводил спектрограммы вокодером World, есть реализация на С++ и на Питоне (PyWorld):
https://github.com/JeremyCCHsu/Python-Wrapper-for-World-Vocoder
Этот вокодер (точнее - полноценный кодек: сырые параметры речи без сжатия) кроме спектрограммы требует ещё ЧОТ(f0) и флаг апериодики (вокализация - причём не дискретная по типу V/UV, а плавная).
Есть ещё RealTime-вариант этого кодека, но там апериодика грубая - по типу V/UV. И F0 определяется неточно, что в итоге приводит к некорректному звучанию женских голосов на повышенных эмоциях.
Точность воспроизведения незнакомого голоса у World намного выше, чем у MELGAN, но это достигнуто ценой раздельной передачи F0 и AP, что требует дополнительной полосы канала.
И звучит World хорошо, когда для определения F0 используется не DIO (который работает быстро), а LoveTrain (работает очень медленно).
Ещё этот World сохраняет спектрограммы в необычном виде - вокальные сегменты размазывает по F0, убирая траектории формант, а на невокальных сегментах - вводит зашумление. Так называетмый CheapTrick. А это для нейросети(которая сжимает параметры) создаёт определённые проблемы. Пробовал убрать Cheaptrick, чтобы сохранить спектрограммы в исходном виде - работоспособность сохраняется, но уровень громкости падает.
Для себя не решил окончательно что использовать - MELGAN или World. У каждого есть свои минусы.
Использовать другие нейросети, озвучивающие спектрограммы, нецелесообразно, из-за их повышенных требований к памяти и вычислительным ресурсам. MELGAN - самый легковесный из всех вариантов. А World - вообще решает туже самую задачу чисто технически - без всяких нейросетей.
Составил матрицу конфузий. Вышло красиво:
Прям Конфуций :)
Удалось переделать MELGAN для потокового режима. Теперь можно скармливать спектрограмму по частям и декодировать её. При этом декодер бесшовно склеивает получившуюся форму волны.
Задача оказалась далеко не тривиальной из-за того, что MELGAN изначально хочет спектрограмму всю целиком. Пришлось анализировать код сети, какие там пады слева-справа, какой хоп и размер окна. А также фиксировать спектрограмму и проводить её анализ.
Получилось что 1 хоп - это 256 семплов, что соответствует 1 фрейму MEL-спектрограммы. А пады занимают по 1,5 фрейма - это по 384 семпла: слева и справа. Окно 1024 семпла.
В итоге, чтобы получить бесшовную спектрограмму, сложенную по частям, приходится слева и справа отбрасывать по 2 фрейма, и склеивать серединки с шагом LEN-PAD.
А при декодировании нужно получившиеся семплы пропускать через окно Ханна (другие окна: Хемминга, Треугольное, Блэкман - тоже работают) и складывать с перекрытием 50%.
Можно сделать перекрытие 25%: отрицательный приподнятый косинус - четверть слева, единица - две четверти в центре и приподнятый положительный косинус - последняя четверть справа. Производная косинуса в максимуме (стык с центром который =1) равна нулю - в итоге на слух сопрягается также бесшовно - суммарный вес в любой области наложения всегда =1
Перешёл на PyTorch. Ибо моё терпение лопнуло от этой парочки Keras/Tensorflow - невозможно заставить компилироваться код с VAE+GAN: весь фреймворк вечно меняется. А ставить старьё уже нереально на мой комп.
Словил кучу депрессняка и фрустраций по поводу применения нейросетей для потокового сжатия речи на сверх-низких битрейтах.
Я пока в начале пути и упёрся в трудности реализации желаемого...
Напоминаю: нужен легковесный речевой кодек на 150 бит/c (или ниже), способный работать в потоковом режиме и дающий хорошую разборчивость речи.
Gradius
Я пока в начале пути и упёрся в трудности реализации желаемого...
Слава богу!... и не так всё просто, как иным кажется.
(Особенно of line на своём компьютере)- не про тебя.
Я тоже упёрся...
Упёрся в большую цифру...
...
Крылья не складываем и по хрену мороз.
Уважаю!
Что нового:
1) Полностью перешёл на Torch/PyTorch. Ибо Keras/Tensorflow достали своими вечно-меняемостью и неуклюжестью.
Это всёравно, как наивно ждать, когда кто-то перепишет хедеры с C++ на Pascal. Помню, болел такой фигнёй в начале 2000-х :)
2) Ознакомился с разными типами регуляризации. В том числе - с такими, которые полезны для обучения нейросетей для обработки звука/временного ряда. Без регуляризации, как я уже говорил ранее, нейросеть бесполезна.
3) Ознакомился с разными векторными квантователями:
- VQ-EMA
- Soft VQ
- GSSoft VQ
- Space Fill VQ
Их применение позволяет провести обучение сети более эффективнее - лосс падает в разы и/или сеть быстрее обучается, что похвально.
Классический VQ-VAE обучается долго и квантование дубовое совсем там.
В процессе всё ещё...
3) Ознакомился с разными векторными квантователями:
- VQ-EMA
- Soft VQ
- GSSoft VQ
- Space Fill VQ
Их применение позволяет провести обучение сети более эффективнее - лосс падает в разы и/или сеть быстрее обучается, что похвально.
Спасибо, что дал вектор направления для дальнейших экспериментов.
Я прикупил винт и перешёл на большие объёмы данных(терабайты), что разительно сказалось на качестве. Уже не просто человеческий текст получается, но и в соответствии с научными представлениями о мире.
Это отрывок НФ рассказа, который я пытаюсь генерировать.
Время будет, опишу про подготовку данных и все дальнейшие телодвижения. Тебе это не подойдёт, но может кому ещё пригодится.
1) Вариационный авто-энкодер, регуляризация: https://medium.com/@raajanwankhade/variational-autoencoders-vaes-… -11e2931d05f3
К вопросу о том, почему важна регуляризация. Вводится ещё потеря - дивергенция Кульбака-Лейблера, которая помогает векторам в скрытом пространстве кучковаться по-теснее. Без этого нейросеть не сможет корректно интерполировать значения, лежащие вне обучающей выборки:


О регуляризации и о дивергенции KL:

2) Потери в векторном квантователе при его совместном обучении вместе с нейросетью: https://medium.com/data-science/improving-vector-quantization-in-… -915f5814b5ce
В статье предложена новая модель векторного квантователя - на базе инъекции шума при обучении. В результате чего, потери квантователя можно не учитывать при обучении (он не нарушает градиент из-за нулевого мат-ожидания нормального шума) и кодовая книга используется почти полностью!

Использование кодовой книги при обычном VQ (низкий процент использования кодовой книги, много мёртвых записей):

Использование кодовой книги в предложенном методе:

Испытал его: на сегодняшний день NSVQ - лидер по низкому лоссу и качеству использования кодовой книги.
3) Ещё одна новая архитектура векторного квантователя - SFVQ: https://medium.com/data-science/interpretable-latent-spaces-using… -e4eb26691b14
Важные свойства:
- Кодовая книга пытается заполнить всё пространство
- Записи в кодовой книге строго упорядочены и соседствуют с близкими образцами - наиважнейшее свойство, если ставится цель - найти фонемо-образующие элементы в сигнале.


Последняя картинка - показывает связь между фонемами (ручное разбиение) и кластеризацией SFVQ в кодовой книге.
Из рисунка видно, что обычный векторный квантователь (VQ) не учитывает связь между близлежащими элементами - индексы смежных семплов практически рандомизированы.
Совсем другое дело SFVQ - видна корреляция индексов кодовой книги смежных семплов. Здесь под семплом имеется ввиду - один фрейм спектрограммы (вертикальная полоска).
Испытал его пока отдельно от нейросети.
4) Регуляризация для временных рядов - Time Jitter. Почитать о ней можно здесь: https://arxiv.org/pdf/1901.08810v2
Страница 4: A. Time-jitter regularization
Мы хотели бы, чтобы модель изучила представление речи, которое соответствует медленно меняющемуся фонетическому содержанию в пределах высказывания: в основном постоянный сигнал, который может резко изменяться на границах фонем.
Вдохновленные анализом медленных признаков, мы сначала экспериментировали со штрафованием разницы во времени между представлением кодера до или после узкого места. Однако эта регуляризация привела к коллапсу скрытого пространства – модель научилась выводить постоянное кодирование. Это распространенная проблема последовательных VAE, которые используют члены потерь для регуляризации скрытого кодирования.
Это как раз то, о чём я писал ранее, когда добавлял регуляризатор, штрафующий сеть за различие соседних векторов. Но получилось как в цитате - выводилось скрытое пространство с практически одинаковыми векторами, компоненты которых различались друг от друга на +/-0.0001. А нейросеть использовалась как рычаг, чтобы усилить разницу. Что естественно, не годится для моей задачи.
Далее они предлагают этот самый джиттер:
Пересматривая проблему, мы поняли, что мы хотим, чтобы представление каждого кадра соответствовало значимой фонетической единице. Таким образом, мы хотим помешать системе использовать последовательные скрытые векторы как отдельные единицы. Иными словами, мы хотим предотвратить коадаптацию скрытых векторов.
Поэтому мы вводим регуляризатор дрожания времени. Во время обучения каждый скрытый вектор может заменить либо одного, либо обоих своих соседей. Это не позволяет модели полагаться на согласованность между группами токенов. Кроме того, эта регуляризация также способствует стабильности скрытого представления с течением времени: скрытый вектор, извлеченный на временном шаге t, должен стремиться также быть полезным на временных шагах t-1 или t+1.
Фактически, регуляризация имела решающее значение для достижения хорошей производительности на более высоких частотах извлечения токенов.
Слой регуляризации вставляется сразу после узкого места кодировщика (т. е. после снижения размерности для обычного автокодировщика, после выборки реализации скрытого слоя для VAE и после дискретизации для VQ-VAE). Он включается только во время обучения. Для каждого временного шага мы независимо выбираем, следует ли заменить его токеном сразу после или до него. Мы не копируем токен более одного временного шага.
Как говорится: "век живи - век учись..." :)
Всё доступно в исходных кодах на гитхаб. Ссылки приведены в статьях.
P.S. Без правильных регуляризаций нейросеть не выведет полезное представление скрытого пространства, которое нужно в том числе и для сжатия речи на битрейтах, близких к лингвистическому.
Я бы назвал регуляризации - формочками для отливки полезных представлений внутреннего пространства :)
SFVQ выглядит сомнительно: упорядоченность достигается путем ухудшения качества отдельных векторов.
Тогда уж лучше отдельным постпроцессом пройтись и отсортировать векторы, полученные обычным способом.