Локальный ассистент для зумов, часть 8: модель, ради которой я поменял диаризацию за один вечер - QubStore

Локальный ассистент для зумов, часть 8: модель, ради которой я поменял диаризацию за один вечер

Содержание статьи

В третьей части я написал, что не вижу чужой модели, ради которой стоит менять связку. sherpa-onnx на диаризации, свой второй проход поверх, канал собеседников двадцатиминутного звонка размечается минут семь. Жить можно.

23 сентября NVIDIA выложила Nemotron 3 Diarization. Вечером 28-го я включил её в разбор своих созвонов. Тот же канал — три секунды.

Что это

Sortformer на сто миллионов параметров, до восьми голосов. На каждые 10 мс у каждого голоса своя вероятность, два голоса в одном кадре для неё норма. Старая связка решает по отпечатку куска речи. Двое говорят разом — отпечаток общий, кто-то пропадает.

Русского в языках обучения нет. Веса под NVIDIA OpenMDW, в репозиторий их не кладу, человек качает сам. Запускаю MLX-порт через mlx-audio 0.5.6, только Apple Silicon.

Синтетика врёт

Сначала прогнал по своим тестовым фикстурам, их озвучивает say из macOS. Фикстура с перебиваниями — почти идеально: DER 0,039, все 4,5 секунды одновременной речи на месте. Обычная, где четыре голоса говорят по очереди, — все четыре в одном слоте, DER 0,628.

Подумал на длину. Обрезал до 27 секунд, добавил тишины. То же самое.

Модель заводит нового говорящего, когда голоса пересекаются. Синтетические голоса по очереди для неё один человек. Судить её можно только по живым записям.

AMI

Две встречи из AMI, четыре человека, английский, строгий эталон pyannote по словам. Всё на M1 Max.

ES2004a

IS1009a

RTF

мой проход после встречи

0,606

0,453

0,35

Nemotron

0,269

0,277

0,003

Это DER. В третьей части у меня было 12%, но там считалась одна путаница, на одной моей встрече и по моей разметке. Здесь всё сразу и строго.

Голоса Nemotron почти не путает. Большая часть его ошибки — пропущенные короткие слова между паузами. Мой проход на ES2004a склеил четверых в три голоса.

И в сто раз быстрее.

57 встреч

Записи у меня живут два дня. Когда пошёл гонять Nemotron по своим встречам, их нашлось 57. В начале сентября я поднимал срок до 14 дней под одну фичу, которую так и не начал, и забыл вернуть.

57 записей канала собеседников, 34,8 часа звука, 3,5 минуты счёта.

Эталона на своих встречах нет. Судьёй взял ревизию — облачный разбор после кнопки Стоп, про него была четвёртая часть. Она по смыслу реплик пишет, кто что говорил и где метки врут.

Где старый проход собрал троих под одной меткой, Nemotron дал три голоса. Мужской и женский голос, слипшиеся в одну реплику стенограммы, развёл. Где старый раздал десять меток на встречу, в которой по ревизии четверо, Nemotron нашёл в канале собеседников троих. Четвёртый я, в микрофоне.

Пределы тоже видны. На восьми встречах он упёрся в свои восемь голосов. Очные встречи в один микрофон не проверял.

Потом вернул хранение на два дня. Ушло 7,6 ГБ.

Встроить

Собирался неделю держать его в тени, рядом со старым. Потом дошло, что тень уже есть: 57 встреч. Встраивал в тот же вечер.

Приложение подписано, внутри свой Python, mlx там нет. Положу в бандл — сломаю подпись. Значит, отдельный процесс в своём окружении: пересборка стенограммы запускает движок чужим интерпретатором и забирает JSON.

С моим окружением как есть чужой интерпретатор запускать нельзя. Приложение выставляет свой каталог байткода, может приехать PYTHONPATH, переменные родительского venv. Рецепт чистого окружения у меня уже был: при первом запуске приложение проверяет, всё ли стоит, и зовёт для этого Python так же. Я собирался написать четвёртую копию. Поймал ревьюер на входе.

Входных кругов было шесть. Первые четыре подряд нашли по два Critical. Четвёртый назвал корень: семьдесят строк разметки внутри rebuild(), в замыкании, без тестов. Там два порога, фильтр эха и нумерация голосов. Сначала вынес его и закрепил тестами на то, как он работает сейчас. Старый и новый rebuild() на трёхстах случайных разметках дали одинаковые стенограммы. Потом подключил движок.

Эхо

Перед выкатом прогнал две записи этого же дня, старый движок и новый рядом.

В микрофон попадает звук из динамиков. Отрезок микрофона, больше чем наполовину накрытый речью собеседников, выбрасывается как эхо. Доля считалась по одному отрезку собеседника.

У sherpa отрезки длинные, правило работало. Nemotron режет реплику паузами на куски. Эхо поверх трёх кусков по 30% проходило как живая речь в микрофоне.

Сколько речи оставалось в микрофоне:

8 минут

20 минут

сейчас, sherpa

17,5%

14,4%

Nemotron, старое правило

69%

44%

Nemotron, доля по всем отрезкам сразу

17,8%

16,1%

Шесть кругов ревью обсуждали стыки, фильтр эха в том числе. Дробление отрезков не увидел никто, я тоже. Увидел замер.

Меняешь поставщика данных — прогони на его выходе всех, кто эти данные ест.

Что ещё поймали

Выходное ревью нашло дыру похуже. Движок может ответить успехом без единого отрезка. Канал собеседников тогда молча пустой, sherpa не зовётся, стенограмма без собеседников. Теперь пустой ответ — отказ. Один отрезок на всю запись тоже.

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

Мутатор по ветке сделал 170 мутантов, 21 выжил. Девять — в нарезке пересечений, на границах: кусок начинается там, где кончается другой, два начинаются в одну секунду. Случайные разметки туда не попадают, подобрал маленькие перебором под каждого выжившего. Остальные — в старом коде, который я только перенёс, в строках лога и в чтении WAV. Все стали тестами.

Тестов больше 4,6 тысячи. Флаг включил в тот же вечер.

Что осталось

Микрофон по-прежнему размечает sherpa. Обычно там один голос, мой.

Окружение движка ставил руками, в свою папку. Продукт должен ставить его сам, со своим локом.

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

Рецепт чистого окружения теперь живёт в нескольких местах. Должен быть один.

Живой контур пока на старом движке. Потоковый Nemotron на AMI даёт те же 0,27 при RTF 0,04, так что туда тоже.

Очные встречи и русский с эталоном — не мерил.

В main с 28 сентября, войдёт в ближайший релиз.

Код и релизы: github.com/charoiteai/Charoite_audio, Apache 2.0. Девлог по-английски: charoiteai.github.io/Charoite_audio. Первая часть — про устройство и запись, вторая — про граф встреч как память, третья — про диаризацию и три ложных диагноза, четвёртая — про ревизию после кнопки Стоп, пятая — про облако и что уходит наружу, шестая — про полчаса тишины, которых не было в протоколе, седьмая — про семь пакетов в плане и один в коде.

Ссылка на основную публикацию