Все статьи

· Casimiro Ferreira· 9 мин чтения

Приведение в порядок плохого аудио: шумоподавление и расширение полосы пропускания в audiosronnx

  • ONNX
  • denoising
  • bandwidth extension
  • speech

Запись может быть плохой двумя разными способами, и исправления не пересекаются.

Первый способ: фоновый шум лежит поверх речи — трафик, вентилятор, гул комнаты. Нужный сигнал есть; в него подмешано что-то ещё. Удаление этого — шумоподавление.

Второй способ: запись изначально не захватила полный сигнал. Телефонное аудио дискретизируется на 8000 отсчётов в секунду (8 кГц); полноценная запись обычно на 48 кГц. Частота дискретизации задаёт самую высокую частоту, которую может представить цифровой сигнал, так что в 8-килогерцовом звонке вообще нет содержания выше 4 кГц — не тихого, не отфильтрованного, а просто никогда не записанного. Сделать такое аудио снова звучащим полным означает изобрести правдоподобные высокие частоты, которые никогда не были захвачены. Это расширение полосы пропускания.

audiosronnx трактует их как две разные задачи с двумя разными точками входа, потому что использование не той приводит к неверному результату. Запустите расширитель полосы пропускания на зашумлённом сигнале, и он добросовестно восстановит высокочастотную версию шума. Шумоподавление должно происходить первым.

from audiosronnx import load_denoise, load_sr

clean, rate = load_denoise("dpdfnet").denoise("noisy_call.wav")   # remove noise
wide, _     = load_sr("lavasr").upscale(clean, rate)              # extend to 48 kHz

load_denoise() и load_sr() отвергают движки друг друга — запрос у load_denoise расширителя полосы пропускания вызывает ошибку, а не молча выполняет не ту задачу.

Почему это идёт перед распознаванием

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

Это делает очистку этапом конвейера, который стоит перед распознаванием, а не альтернативой ему. Реальный конвейер для зашумлённого 8-килогерцового телефонного звонка выглядит так: подавить шум, затем расширить до 48 кГц, затем запустить распознавание или идентификацию диктора на результате. Замена на лучший распознаватель без предварительного исправления входа тратит усилия не там — модель деградирует на том же повреждённом сигнале, каким бы хорошим она ни была.

Реестр движков

audiosronnx поставляет десять шумоподавителей и семь расширителей полосы пропускания, все загружаемые по имени через load_denoise() / load_sr(), все на чистом ONNX без torch во время выполнения.

Шумоподавители:

ДвижокЧастотаРазмерЛицензия
dpdfnet (по умолчанию)8/16/48 кГц8.7–14.9 МБApache-2.0
mossformer248 кГц229 МБApache-2.0
frcrn16 кГц57.5 МБApache-2.0
mpsenet16 кГц9.7 МБMIT
gtcrn16 кГц0.54 МБMIT
cmgan16 кГц7.8 МБMIT
metadenoiser16 кГц19–34 МБCC-BY-NC-4.0
mossformergan16 кГц17.7 МБApache-2.0
voicefixer44.1 кГц415 МБMIT
deepfilternet48 кГц~2 МБMIT

Расширители полосы пропускания:

ДвижокВходРазмерЛицензия
lavasr (по умолчанию)8–48 кГц~52 МБApache-2.0
novasr16 кГц~0.2 МБApache-2.0
flowhighлюбой~200 МБMIT
hifiganbweлюбой~4 МБMIT
apbweлюбой (полоса 12 кГц)~120 МБMIT
sidon16 кГц~410 МБMIT
callenhancer8–16 кГц~3 ГБ / ~1.3 ГБ int8CC-BY-NC-4.0

Самая маленькая модель в библиотеке, gtcrn, — 0.54 МБ. Самая большая, voicefixer, — 415 МБ — почти в 800 раз больше, и она выполняет другую задачу: это модель реставрации, которая обрабатывает шум, реверберацию, клиппинг и недостающую полосу пропускания вместе, а не по одной проблеме за раз.

Большинство весов — MIT или Apache-2.0. Два — нет: metadenoiser и callenhancer поставляются под CC-BY-NC-4.0, некоммерческой. Эта лицензия покрывает веса, а не аудио, обработанное с их помощью, и библиотека указывает это в каждой точке использования — audiosronnx list сообщает это для каждого движка. Ничто не мешает вызывающей стороне выбрать metadenoiser за его архитектуру во временной области, но выбор должен делаться осознанно.

Реестр существует потому, что ни одна модель не выигрывает на каждой записи. dpdfnet — вариант по умолчанию, потому что не требует дополнительных зависимостей и покрывает 8, 16 и 48 кГц одной моделью. mossformer2 — лучший измеренный выбор на полнополосном входе. mossformergan показывает самый высокий опубликованный показатель PESQ (3.47) среди поставляемых шумоподавителей. gtcrn — выбор, когда ограничивающий фактор — объём, при 0.54 МБ. На одном тестовом клипе с широкополосным гауссовым шумом шумоподавители восстановили от 3.5 до 5.9 дБ SNR при входном SNR 19 дБ, поднимаясь до 7.5–13.7 дБ при более трудном входе в 5 дБ. Это синтетический, враждебный случай шума: он последовательно ранжирует движки, но мало говорит о шуме голосов толпы или артефактах кодеков, и именно поэтому реестр держит десять моделей, а не поставляет только победителя.

cmgan — самый ясный случай модели, оставленной намеренно, несмотря на проигрыш: она уступает и по PESQ, и по SNR модели gtcrn, будучи в четырнадцать раз больше, и всё равно остаётся — чтобы опубликованные результаты, построенные на cmgan, оставались воспроизводимыми, и чтобы отдельная архитектура оставалась доступной для сравнения.

На стороне расширения полосы пропускания sidon и callenhancer выполняют другую задачу, чем lavasr или novasr: вместо добавления правдоподобной высокой полосы поверх существующего сигнала они ресинтезируют речь с нуля через нейросетевой вокодер, что может исправить повреждение кодеком, которого не может коснуться расширитель полосы, — ценой намного более высоких вычислительных затрат. callenhancer обучен специально на телефонном аудио, поэтому его веса несут некоммерческую лицензию.

Что не прошло отбор

audiosronnx поставляет движок только тогда, когда он экспортируется в единый статический граф ONNX, работает на CPU через onnxruntime, несёт понятную лицензию и был проверен от начала до конца против оригинальной реализации — не просто против сырой модели, поскольку граф, совпадающий с сетью, но не с окружающей её нормализацией, производит аудио, которое звучит нормально, но тихо неверно.

Файл docs/not-shipped.md проекта документирует каждого кандидата, который был оценён и отклонён, с конкретной причиной, что делает его одним из самых полезных документов в репозитории, потому что он показывает реальные границы того, что может сегодня «чистый ONNX, только CPU», а не просто декларирует их.

У итеративных сэмплеров нет статического графа для экспорта. Модели диффузии и flow-matching запускают сеть много раз на одно высказывание, с циклом, длина которого не фиксируется на этапе экспорта. AudioSR (конвейер латентной диффузии размером примерно 6 ГБ с отдельными VAE, LDM и вокодером) и SGMSE оба попадают сюда — собственный стриминговый последователь SGMSE 2025 года достигает реального времени только на потребительской GPU, не говоря уже о CPU.

Позиционно-переменные свёртки выглядят дисквалифицирующими, но по большей части нет. resemble-enhance долго отклонялся в этом документе из-за LVCNet, позиционно-переменной свёртки вокодера, по теории, что ядра, предсказываемые поточечно через unfold и einsum, не могут свернуться в статический граф. При прямой проверке это оказалось неверно — обе операции имеют эквиваленты в ONNX. Реальный сбой — это отдельная, хорошо понятная ошибка трассировки («ONNX export of convolution for kernel of unknown shape»), уже решённая в другом месте кодовой базы для ресемплеров BigVGAN. Что по-прежнему держит resemble-enhance вне реестра — это масштаб: четыре сети, включая сэмплер ОДУ CFM и автоэнкодер, на 44.1 кГц — решение об объёме работы, а не невозможность.

У некоторых моделей нет ничего обученного для экспорта. RNNoise поставляется как вручную написанный C, а не граф в обучаемом фреймворке — портирование означало бы переобучение эквивалентной сети с нуля. Fast-ULCNet публикует только код архитектуры, без единого чекпоинта.

Ограничительная лицензия — это решение о маркировке, а не автоматическая дисквалификация — именно поэтому поставляются callenhancer и metadenoiser. Что действительно дисквалифицирует — это веса, опубликованные вообще без лицензии: mdctGAN был отклонён именно по этой причине, вдобавок к фронтенду на основе torch.fft, который надёжно не экспортируется.

Вызов torch.stft внутри модели — это реальный структурный блокер. Цикл сэмплера NU-Wave2 — не проблема — он мог бы выполняться в numpy вне графа, так же как STFT в каждом другом движке. Блокирует то, что его метод forward вызывает torch.stft и torch.istft внутри, что библиотека намеренно исключает из каждого поставляемого графа, и что также является оператором, который в целом экспортируется наименее надёжно. Исправление означало бы разделение модели на границе преобразования, реальное переструктурирование, а не замену оператора.

Воспроизведение архитектуры модели — не то же самое, что воспроизведение её результата. LiSenNet — это 56 тысяч параметров, менее 300 КБ — это был бы самый маленький движок в библиотеке. Его публично доступный ONNX-порт запускается и производит правдоподобно выглядящее ослабленное аудио, но при измерении от начала до конца он разрушает сигнал: −10.8 дБ SNR при входе 11 дБ. Точное воспроизведение собственной референсной реализации порта даёт идентичный отрицательный результат, что означает, что сама референсная реализация не соответствует фронтенду, который описывает её собственная документация — пока нет верной цели, по которой можно было бы проверять.

Закономерность во всех этих случаях в том, что интересные сбои редко бывают вида «модель слишком большая» или «диффузия медленная». Они конкретны: неподдерживаемая операция с точной заменой (torch.complex не имеет операции ONNX, но atan2(im, re) вычисляет тот же угол фазы), тензор, построенный из формы входа во время выполнения, которую трассировщик не может зафиксировать, или преобразование, размещённое не на той стороне границы графа.

Подтверждение, что результат действительно стал лучше

Файл ONNX, который запускается, — не доказательство того, что запись улучшилась. Два разных режима сбоя выглядят одинаково снаружи: шумоподавитель, приглушающий речь вместе с шумом, и расширитель полосы пропускания, добавляющий высокую полосу с неверным гармоническим содержанием, оба производят аудио, которое воспроизводится без ошибок и может даже звучать чище при беглом прослушивании.

Родственная библиотека speechonnxmetrics существует, чтобы сделать это суждение измеримым, а не впечатлением. Она оценивает аудио по MOS (Mean Opinion Score, оценка воспринимаемого качества от 1 до 5) двумя способами: безреференсные нейросетевые предикторы вроде DNSMOS и UTMOS, которые оценивают запись без чистого оригинала для сравнения, и интрузивные метрики вроде STOI и SI-SDR, которым нужен чистый референс и которые измеряют, насколько результат на самом деле к нему близок.

import speechonnxmetrics as s

s.score("clean.wav", ["utmos"])
# -> {'utmos': 4.41}

s.score("denoised.wav", ["stoi", "si_sdr"], ref="clean.wav")
# -> {'stoi': 0.66, 'si_sdr': -26.9}

Прогон до и после шумоподавителя или расширителя, и форма реального сравнения проясняется: DNSMOS или UTMOS на сыром и обработанном аудио, чтобы увидеть, сдвинулось ли вообще воспринимаемое качество, и — когда существует чистый референс, что верно для синтетических тестов шума, но редко для реального телефонного звонка — SI-SDR или STOI, чтобы увидеть, действительно ли обработанный сигнал сошёлся к нему, а не просто зазвучал иначе. Это та же дисциплина, что стоит за цифрами SNR в таблице шумоподавителей выше: число, привязанное к конкретному условию шума, а не прилагательное. Более широкое семейство библиотек на чистом ONNX, в которое это входит, включая саму speechonnxmetrics, описано в Семействе речевых библиотек на чистом ONNX.

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

Вопросы о применении этого к конкретному конвейеру: свяжитесь с нами.