Замахнулся на битрейт 125 бит/c.
Это 8 фреймов по 16 мс, на которые отведено 16 бит.
Применил скалярный сферический квантователь BSQ.
Ошибка реконструкции спектра ниже 0.003343 не получается. 800 эпох. 6 часов обучения.
Спектр вышел таким:

Его двоичное латентное пространство (16 бит на 8 фреймов):

На слух звучит хрипловато (форманты местами потерялись), но разборчивость сохраняется.
Вместо двоичного скалярного квантователя есть идея вернуться к одному из векторных.
Плюс может в качестве входных признаков использовать не 80-полосную MEL-спектрограмму, а полноценную спектрограмму на 512/1024/2048 бинов - обе части - действительную и мнимую.
Так нейросетка получит больше входной информации и будет возможность восстановления звука без MELGAN - просто технически
1. Освоил MELGAN. Переобучил его на тайм-фрейм 20 мс (штатно на 16 мс)
2. Освоил Vocos. Переобучил его на спектрограмму из 513 бинов
3. Детально копаюсь с FocalCodec.
Удалось заменить тяжёлую модель кодера WavLM (360 МБ) на свою модель - из свёрток, аттеншенов и остаточных блоков. Моя модель всего 24 МБ.
Обучал свою модель кодера таким образом:
1) Подавал Wav-форму сигнала на обученный WavLM (модель доступна и упоминается в репозитории FocalCodec). На выходе получается тензор- считаем его эталоном. Модель заморозил, работает только на инференс.
2) С Wav-формы сигнала (см. п. 1) считается MEL-спектрограмма, которая подаётся на облегчённую модель, которая обучается. На выходе получается второй тензор, с размерностью такой же, как у эталонного тензора.
Обучение идёт таким образом, чтобы облегчённая модель научилась генерить на выходе почти одинаковые тензоры, которые выдаёт громоздкая модель WavLM.
Тензоры (эталонный и обучаемый) срезаются слева и справа на 2 вектора - чтобы исключить из обучения переходы из-за выравнивания фреймов спектрограммы (пады).
Лосс по L2 (MSE).
Аугментация данных - как в MELGAN: случайная амплитуда сигнала - множитель 0.3 - 1.0
После 200 эпох (это 18 часов обучения), облегчённая модель даёт те же вектора, что и модель громоздкого трансформера-кодера WavLM.
3) Далее из FocalCodec убирается WavLM и добавляется обученная (см. п. 2) облегченная модель.
В итоге результат удивил - кодек работоспособен!
Чисто субьективно разборчивость немного уступает модели WavLM: в облегченной версии кодера - изредка переходы между звуками могут звучать по-другому.
4) Попробовал напрямую подать выход с обученной модели кодера на обученный вокодер Vocos: результат идеален, местами даже лучше, чем если WavLM соединить напрямую с Vocos.
5) Далее стал разбираться с фокальным компрессором, декомпрессором и квантователем.
В описании на FocalCodec сказано, что цепочка компрессор-квантователь-декомпрессор - обучаются отдельно. На вход подаются тензоры с кодера (кодер заморожен).
Лосс должен быть составным: MSE + (0.1*NE)
NE - это потеря нормализованной энтропии. Загуглил что это такое по ссылкам описания Focal Codec. Найти это оказалось не так просто. Но я нашёл.
Речь идёт об энтропии в контексте использования кодовой книги. Шэнноновская двоичная нормализованная энтропия. Значения [0..1]. 1 - идеал: равномерно используется вся кодовая книга.
Как я понял, при обучении квантователя, нужно тщательно следить, чтобы энтропия кодовой книги была близка к 1:
Тоже самое написано и в описании на FocalCodec : кодовая книга должна равномерно использоваться. Что опровергает ранее мною сделанное заявление о том, что в книге могут быть мало-используемые редкие вектора.
В итоге переобучил связку "компрессор-квантователь-декомпрессор". Заменил модель в изменённый FocalCodec. Результат: кодек работает, но каких-либо улучшений я на слух не заметил.
6) Далее попытался снизить размерность тензора с выхода кодера, и повысить размерность перед вокодером Vocos. Для этого использовал пару линейных слоёв 1024=>256 и 256=>1024.
Переобучил квантователь/компрессор/декомпрессор вместе с этим слоями.
Результат: кодек сохранил свою работоспособность. Размеры моделей фокального компрессора и декомпрессора уменьшились: были по 19,5 МБ. Стали по 5,8 МБ.
Собственно, почему я стремлюсь облегчить модели? Две причины:
a) Кодек должен в реальном времени работать на Allwinner T113-s3.
б) Максимальный размер памяти 128 МБ (код+данные).
7) Следующим шагом было - отказ от кодера. Подача 80-полосной мел-спектрограммы на вход фокального компрессора. Декомпрессор по-прежнему даёт тензор размерности 1024 на Vocos.
Обучение аналогичное, как описал выше. Обучаемая цепочка: на вход фокального компрессора: 80-полосная спектрограмма, далее квантователь, далее фокальный декомпрессор с выходом размерностью 1024.
Опорная цепочка: на вход WavLM звуковая волна, на выходе опорный вектор размерности 1024.
Потери MSE + 0.1NE.
Результат: кодек по-прежнему работает. Но результат вышел по-хуже: иногда проглатываются слоги или заменяются на другие.
Выводы, которые были сделаны после всех экспериментов:
1) Для очень низких битрейтов(менее 300 бит/c) нужны очень мощные кодеры - на базе трансформеров, дающие супер-репрезентативные данные. А это требует большого объёма памяти и высокого быстроедйствия (WavLM, HuBERT и другие).
2) Эмулятор мощного кодера - более облегченная модель, обученная на трансформере. Имеет право быть.
3) Для квантователя важна равномерность использования кодовой книги
4) Генеративный концепт обучения нейросети (Vocos, MELGAN) - намного эффективен с целью воссоздать сигнал из входных скрытых представлений.
5) Vocos намного качественнее генерит звук, чем MELGAN. Кроме того, Vocos очень устойчив к деградации по входу, чего не скажешь о MELGAN, который хрипит, как только спектрограма начинает разваливаться.
Потому что Vocos учится восстанавливать комплексные коэффициенты Фурье, а не форму звукового сигнала. Форма сигнала там - обратное преобразование Фурье, что само по себе тривиально, если коэффициенты комплексные.
———
В целом, сумма размеров всех моделей кодека не превышает 128 МБ. Но тут надо ещё место на код оставить :)
Продолжаю копать...
Спасибо за посты Gradius.
Читаю, как детективный роман ))
flint2
> Читаю, как детективный роман ))
)))
С каждым разом, узнав что-то новое, думаю, что близок к решению. Но нет.. В процессе освоения нового, понимаешь, что как многое ещё неизвестно.
В этом и есть суть жизни - испытывать удовольствие от освоения нового, что приближает каждый раз решение или делает его более совершенным. ))
flint2
> Задумка такая: показать всё от подготовки контента для обучения
А если на arxiv.org запилить?
arXiv is a free distribution service and an open-access archive for nearly 2.4 million scholarly articles in the fields of physics, mathematics, computer science, quantitative biology, quantitative finance, statistics, electrical engineering and systems science, and economics. Materials on this site are not peer-reviewed by arXiv.
———
Сделал несколько зарисовок к своему предыдущему посту. Рисовал от руки, поэтому драфт.
1) Обучение легковесной сети-кодера (эмулятор) более мощной тяжёлой сетью-трансформером (WavLM):

2) Обучение FocalNet (компрессор и декомпрессор) через бинарный сферический квантователь (BSQ):

3) Обучение FocalNet без кодера - со спектрограммы напрямую:

Во всех трёх случаях - декодер Vocos оставался не пере-обученным(используемая модель Vocos - из FocalCodec).
Gradius
В этом и есть суть жизни - испытывать удовольствие от освоения нового, что приближает каждый раз решение или делает его более совершенным. ))
Подпишусь под каждым словом!
Gradius
А если на arxiv.org запилить?
Даже не знаю...
Здесь я не решаюсь, опыт был.
Может на Хабр https://habr.com/
Так что кроме кода, надо описать лингвистические и сценические приёмы.
(Чего стоит растолковать, как строятся сценарии.)
Например, словари могут состоять не из отдельных слов, а из групп, потому что в предложении они выражают одно понятие.
кто это
в свою очередь
должно быть
Здесь вводное слово может состоять из нескольких слов.
Тоже самое запятая может выступать в роли союза и другие части речи часто в предложениях выражены несколькими словами.
Сейчас всё чаще встречаю забугорные статьи где указывают на этот момент.
Я дошол до этого, когда ещё операционки были 32-разрядные.
https://disk.yandex.ru/d/BGcaB8FaGZf9Ww
Думаю описать всю кухню с точки зрения лингвистики и нейронных сетей, а на исполняемые файлы и библиотеки дать ссылки. Потому что если ещё с GitHub связываться - это вилы.
Вся ценность работы, я так думаю, заключается в том, что не нужны такие мощности, как для больших языковых моделей и более гибко и конкретно можно передать сети свои хотелки, "чертёж произведения" , чем GPT, DeepSeek и им подобные - они на другое заточены и интерфейс словесный.
Главное - это то, что можно "воспитать" сеть по своему разумению на своём домашнем компьютере.
Gradius
Сделал несколько зарисовок к своему предыдущему посту.
Нормально и главное доходчиво!
Gradius
> Эмулятор мощного кодера - более облегченная модель, обученная на трансформере. Имеет право быть.
Это известная техника, называется дистилляция. У тебя не совсем прямой вариант, а с дополнительным фиксированным слоем (спектрограмма).
> Для квантователя важна равномерность использования кодовой книги
Ну это самоочевидно, я еще на прошлой странице писал: идеальное сжатие должно быть неотличимо от шума.
}:+()___ [Smile]
> Ну это самоочевидно, я еще на прошлой странице писал: идеальное сжатие должно быть неотличимо от шума.
На тот момент времени у меня не хватило знаний/опыта, чтобы обобщить это на кодовую книгу.
Но я всё время думал... какими же свойствами должны обладать вектора в кодовой книге, чтобы они хорошо квантовались и равномерно использовались. Сперва думал про дивергенцию Кульбака-Лейблера. А оказалось, что нужно измерять энтропию.
Для измерения энтропии пришлось завести счётчик на каждую запись в кодовой книге: каждое использование записи, увеличивает её счёткик на 1. Имея счётчики, легко посчитать энтропию кодовой книги.
После 100 эпох обучения (это около 2,5 миллионов итераций) нормализованная энтропия вышла 0.992 (в идеале должно быть близко к 1). В самом начале обучения энтропия была 0,6=0,7.
Потери нормализованной энтропии - со знаком минус естественно.
}:+()___ [Smile]
> Это известная техника, называется дистилляция. У тебя не совсем прямой вариант, а с дополнительным фиксированным слоем (спектрограмма).
Не знал об этом. Этот термин встречал, но думал, что это связано со обработкой звука.
Gradius
В mermaidchart можно делать схемы онлайн.
Код не сложный и прикручен бот:
Yuriy0
> В mermaidchart можно делать схемы онлайн.
Мощно! 
———
Удалось переписать FocalCodec для работы в потоковом режиме: теперь можно скармливать кодеку данные по частям, а не всё сразу.
Первым делом, добился потокового режима в связке:
WavLM => Vocos.
Далее переобучил FocalNet с квантователем - на фиксированный размер сегмента:
=> Focal Compressor => Quantizer => Focal Decompressor =>
В даташите на FocalCodec написано, что FocalNet обучался на полных высказываниях, поэтому потоковый режим при таких условиях можно сделать, если откидывать контекст слева на 3 секунды.
Попробовал этот способ, мне не поравилось по причинам:
1) слишком высокая итоговая латентность
2) зря тратятся вычислительные затраты (3 секунды)
3) на слух очень сильно заметны скачки громкости/интонации/окраски голоса - на стыках сегментов.
Поэтому было принято решение переобучить FocalNet на фикированный размер сегмента. Результат превосходный:
недостатки 1) и 2) полностью отсутствуют - ничего не откидывается
недостаток 3) малозаметен
Полная структура изменённого кодека (облегченная модель кодера + потоковый режим):
STFT => MelLM => Focal Compressor => Quantizer => Focal Decompressor => Vocos
Gradius
> недостаток 3) малозаметен
Вот меня всегда удивляли такие проблемы: заметный стык означает, что конец прошлого и начало нового сегмента работают по разным формулам. По факту это обычно означает, что все элементы сегмента имеют уникальные формулы и коэффициентов для обучения больше, чем нужно. На мой взгляд, гораздо логичнее взять одну формулу и размножить ее на все элементы. При этом размер сегмента можно будет выбирать динамически, результаты от этого не зависят.
}:+()___ [Smile]
> заметный стык означает, что конец прошлого и начало нового сегмента работают по разным формулам.
Если взять всё высказывание целиком и пройтись свёрточным слоем по нему, то начало и конец высказывания будут иметь шов, так как слева в начале и справа в конце - нет данных. Ну и плюс не забываем о падах свертки, которые тоже вносят "краевые эффекты".
А если свёртку применять к фрагментам, то таких швов становится много, их число равно числу фрагментов +1.
Формула та же. Просто начальные условия("фаза данных") разные.
Для акустических сигналов потому и придумали окна, чтобы сшивание было бесшовным. Но таких окон нет для латентного пространства.
Gradius
> А если свёртку применять к фрагментам, то таких швов становится много, их число равно числу фрагментов +1.
Поэтому надо добавлять прошлый фрагмент, чтобы шва не было.
> Для акустических сигналов потому и придумали окна, чтобы сшивание было бесшовным. Но таких окон нет для латентного пространства.
На самом деле есть — можно подобрать такое окно, чтобы прямое и обратное преобразование было без потерь. Возможно, этот вариант был бы оптимален для данного случая, надо бы мне вспомнить теорию и конкретный вид оконной функции...
}:+()___ [Smile]
> Поэтому надо добавлять прошлый фрагмент, чтобы шва не было.
Ну наверное, если бы всё так просто было, то давно бы уже добавили :)
FocalNet имеет достаточно сложную структуру, со слоями обратной связи. Концепция чанков (контекст слева + актуальные данные + контекст справа) тут не очень подходит.
Оказалось проще обучить его на фиксированный размер сегмента. И кстати, на меньших размерах сегмента работоспособность сохраняется.
А это - вариант решения автора кодека (о чём говорил выше):
}:+()___ [Smile]
> На самом деле есть — можно подобрать такое окно, чтобы прямое и обратное преобразование было без потерь. Возможно, этот вариант был бы оптимален для данного случая, надо бы мне вспомнить теорию и конкретный вид оконной функции...
К счастью повезло: латентное пространство на выходе WavLM и его де-квантизация перед Vocos выдерживают оконные преобразования.
Применил треугольное окно (окно Бартлета), сшивал с перекрытием 50%.
Вот как было (видны швы):
Вот как стало:
Что-то подсказывает мне, что окно достаточно использовать только на декодирующей стороне. Сейчас окно есть как в кодере, так и в декодере.
Сжатое по времени и по полосе латентное пространство после FocalNet (его размерность = 13) через окно пропускать не нужно: оно уже шумоподобно.
P.S. Стыкуемость и наличие швов проще всего проверять на синусоидальных сигналах или чирпах.
P.P.S. Так и есть: достаточно использовать окна только на декодирующей стороне:
- Треугольное окно (линейное) - для латентного пространства перед Vocos
- Окно Ханна - после Vocos: на выходе - для сшивания PCM Wav.
Стало ещё лучше: теперь перепадов громкости/тембра/тона/стиля нет.
Gradius
> Так и есть: достаточно использовать окна только на декодирующей стороне:
Собственно, в этом и преимущество нейросетевых подходов: они могут автоматически подстраиваться в широких пределах.
А про математически идеальные окна я нагнал: обратимые окна конечного размера невозможны, даже несимметричные (за исключением тривиального прямоугольника и подобных разрывных форм).