From 7e348e400f0a8ee96db8181ce87a2de8b0ea18d8 Mon Sep 17 00:00:00 2001 From: Dmitriy Dementiev Date: Wed, 12 Aug 2026 15:22:51 +0300 Subject: [PATCH] =?UTF-8?q?docs(diarization):=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=20=D1=80=D0=B0=D0=B7=D0=B2=D0=B5=D0=B4?= =?UTF-8?q?=D0=BE=D1=87=D0=BD=D1=8B=D0=B9=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=20=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D1=81=D0=BC=D0=BE=D1=82?= =?UTF-8?q?=D1=80=D0=B5=D0=BD=20=D0=B1=D1=8D=D0=BA=D0=BB=D0=BE=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Зачем: - схема из бэклога приписывала спикера целому ASR-сегменту, и до замера было неизвестно, насколько сильно это огрубляет результат. - Что: - добавлен разведочный замер sherpa-onnx на одной записи: 11,1x RTFx против 16,4x у ASR, свип порога кластеризации и доля загрязнённых сегментов. - пункт бэклога переписан: движок описан как практически закрытый вопрос, главной развилкой названа единица привязки спикера к тексту. - зафиксировано, что 27% сегментов содержат не менее секунды чужой речи и на них приходится больше половины времени транскрипта. - Проверка: - методика и условия замера воспроизводятся по разделам «Оборудование и условия» и «Контрольная запись» в docs/benchmarks/2026-08-12-diarization-feasibility.md. Co-Authored-By: Claude Opus 5 (1M context) --- docs/backlog.md | 57 +++-- .../2026-08-12-diarization-feasibility.md | 197 ++++++++++++++++++ 2 files changed, 240 insertions(+), 14 deletions(-) create mode 100644 docs/benchmarks/2026-08-12-diarization-feasibility.md diff --git a/docs/backlog.md b/docs/backlog.md index f7d82a1..60dd751 100644 --- a/docs/backlog.md +++ b/docs/backlog.md @@ -181,27 +181,56 @@ LLM для чистки текста, сопоставление Speaker N с и ### Диаризация — разделение говорящих -**Что:** Опциональный пост-процессинг (не четвёртый бэкенд): сегменты -уже несут таймкоды; диаризация даёт интервалы «кто когда говорил»; -merge по перекрытию интервалов; formatter ломает абзац на смене спикера -и подписывает `Speaker 1:`. Ставится как extra: -`uv sync --extra diarization`. +**Что:** Опциональный пост-процессинг (не четвёртый бэкенд): диаризация +даёт интервалы «кто когда говорил», результат сводится с сегментами ASR, +formatter ломает абзац на смене спикера и подписывает `Speaker 1:`. +Ставится как extra: `uv sync --extra diarization`. **Почему:** Без спикеров MoM не собрать — это ключевой разрыв с облаком по внешнему ревью, и никакое качество распознавания его не компенсирует. Заодно естественно решает «разбивку на реплики» (приоритет №4). -**Варианты реализации (ключевое решение, нужен ADR):** +**Промежуточный статус 2026-08-12:** проведена разведка, описанная в +[разведочном замере диаризации](benchmarks/2026-08-12-diarization-feasibility.md). +Она закрыла вопрос о движке и открыла более важный вопрос о единице привязки. -- **sherpa-onnx** — диаризация целиком на onnxruntime (сегментация - pyannote в ONNX + спикер-эмбеддинги), без torch, в духе нашего - onnx-стека и «no cloud, no API keys». -- **pyannote.audio** — стандарт качества, но тянет torch и требует - HF-токен с принятием лицензии моделей — трение с духом проекта. +**Движок — вопрос практически закрыт.** `sherpa-onnx` ставится на Windows с +Python 3.13, содержит готовый `OfflineSpeakerDiarization`, не тянет torch и не +требует токена Hugging Face; модели сегментации и эмбеддингов весят около 33 МБ. +Скорость — 11,1× RTFx, то есть примерно полторы длительности ASR. Вариант +`pyannote.audio` остаётся отклонённым по прежней причине: torch и HF-токен с +принятием лицензии. Отдельный ADR имеет смысл заводить вместе с решением о +единице привязки, а не только про движок. -**Уточнить перед запуском:** качество обоих вариантов на русской речи -и перекрывающихся репликах; скорость на CPU (диаризация — второй проход -по всему аудио); лицензии моделей сегментации/эмбеддингов. +Замечание для будущих заходов: обе ML-части диаризации уже лежат в +`onnx-asr` 0.12 — `PyAnnoteVad` содержит полную локальную сегментацию pyannote +(powerset на трёх спикеров, склейка окон), а `WespeakerEmbeddings` даёт +эмбеддинги. Публичный API схлопывает сегментацию до речь/не-речь, `load_se` не +экспортирован, кластеризации нет. Собирать диаризацию самим на этих деталях — +экономия 33 МБ ценой опоры на приватный API; при разведке этот путь не +выбирался. + +**Единица привязки — настоящая развилка, решения нет.** Схема «мажоритарный +спикер на весь ASR-сегмент», записанная здесь раньше, замером не подтвердилась: +27% сегментов содержат не менее секунды чужой речи, и на них приходится больше +половины времени транскрипта. Причина — границы сегментов идут по тишине +(Silero VAD), а в ВКС собеседники отвечают встык. Варианты: + +- **пословная привязка** — `onnx-asr` отдаёт потокенные таймкоды + (`TimestampedResult`), сегмент режется на границе токена при смене + говорящего; ASR по-прежнему видит длинное аудио, контекст RNN-T и пунктуация + не страдают. Недоступно на OpenVINO GenAI — там потокенных таймкодов нет; +- **диаризация первым проходом**, ASR по интервалам говорящего — чистота + гарантирована, но короткие куски лишают RNN-T контекста и портят пунктуацию; +- **привязка к сегменту с честной пометкой** — оставить огрубление, но считать + чистоту и предупреждать в шапке, как уже делается для повторов и потери + хвоста. + +**Уточнить перед запуском:** воспроизводится ли доля 27% на других записях, +включая разговор на двоих; правильность границ диаризации на слух, а не только +совпадение числа говорящих; калибровка порога кластеризации (на пороге из +примеров получилось 29 спикеров вместо трёх); эмбеддинги, обученные не только +на английском; производительность на целевом Intel Core i5. --- diff --git a/docs/benchmarks/2026-08-12-diarization-feasibility.md b/docs/benchmarks/2026-08-12-diarization-feasibility.md new file mode 100644 index 0000000..084a8d0 --- /dev/null +++ b/docs/benchmarks/2026-08-12-diarization-feasibility.md @@ -0,0 +1,197 @@ +# Разведочный замер диаризации sherpa-onnx + +**Дата:** 2026-08-12 + +**Статус:** разведка на одной записи и одной нецелевой машине. Не приёмка. + +## Цель + +Ответить на два вопроса перед проектированием диаризации: + +1. Сколько времени диаризация добавляет к транскрипции. +2. Достаточно ли приписывать спикера целому ASR-сегменту по мажоритарному + перекрытию — то есть допустима ли схема из + [бэклога](../backlog.md#диаризация--разделение-говорящих) без пословной + привязки. + +Ни модель по умолчанию, ни код проекта в рамках замера не менялись. Все скрипты +выполнялись вне репозитория. + +## Ограничения замера + +Результаты ниже — разведка, а не основание для решения: + +- **одна запись** вместо трёх, принятых в [ADR-006](../adr/006-onnx-asr-backend.md); +- **нецелевая машина**: AMD Ryzen 7 8845H, тогда как приёмка производительности + по бэклогу требует Intel Core i5 11-го поколения; +- **границы диаризации не проверены на слух** — сверялось только число + говорящих и косвенный текстовый признак; +- **порог кластеризации подобран по этой же записи**, то есть на ней же и + проверен. + +## Оборудование и условия + +- ноутбук Lenovo 83D5 (та же машина, что в + [сравнении turbo](2026-08-12-openvino-large-v3-turbo-comparison.md#повторный-прогон-на-amd-ryzen-7-8845h)); +- AMD Ryzen 7 8845H, 8 ядер / 16 логических процессоров; +- 29,8 ГиБ LPDDR5X; +- Windows 11 Корпоративная, сборка 26200, схема питания «Сбалансированная»; +- Python 3.13.13, `onnx-asr` 0.12.0, `onnxruntime` 1.28.0, + `faster-whisper` 1.2.1, `sherpa-onnx` 1.13.5; +- прогоны последовательные, без конкурирующей нагрузки; +- модели предварительно скачаны; время загрузки моделей в замер не входит. + +## Контрольная запись + +| Файл | Длительность | Размер | SHA-256 | +|---|---|---|---| +| `2026-07-10 Data Test внутренний статус.mp4` | 26:00 | 34 217 769 | `1057616B42E8ADD00E0EB975B02BDEF0EC9F6CDFEC6DBF488E0C60423C9B7B87` | + +Запись выбрана потому, что рядом лежит согласованный MoM, из которого известен +состав: **три участника** — Маша, Дима, Роман. Это даёт независимую опорную +точку для проверки числа говорящих. + +## Модели диаризации + +| Роль | Модель | Размер | Источник | +|---|---|---|---| +| Сегментация | `sherpa-onnx-pyannote-segmentation-3-0` | 6,9 МБ | релизы `k2-fsa/sherpa-onnx` | +| Эмбеддинги | `wespeaker_en_voxceleb_resnet34_LM.onnx` | 26,5 МБ | релизы `k2-fsa/sherpa-onnx` | + +Обе загружаются в `onnxruntime`, torch и токен Hugging Face не требуются. +Эмбеддинги обучены на англоязычном VoxCeleb; их пригодность для русской речи в +этом замере не проверялась. + +## Стоимость по времени + +Параметры ASR — модель по умолчанию ONNX-пути, `gigaam-v3-e2e-rnnt` INT8, +язык задан явно. + +| Стадия | Время | RTFx | +|---|---:|---:| +| Декодирование аудио | 1,6 с | ~990× | +| ASR `gigaam-v3-e2e-rnnt` INT8 | 95,3 с | 16,4× | +| Диаризация, 8 потоков | 140,7 с | 11,1× | +| **Последовательно, итого** | **236 с** | **6,6×** | + +Диаризация дороже самой транскрипции и занимает около 60% общего времени. При +последовательном исполнении 26-минутная запись обрабатывается 3,9 минуты вместо +1,6; часовая — примерно 9 минут вместо 3,7. + +Масштабирование по потокам слабое: 4 потока дают 154 с, 8 потоков — 141 с, +выигрыш 9%. Закладываться на увеличение числа потоков не следует. + +Проходы ASR и диаризации независимы по данным, поэтому их можно совместить во +времени; тогда общее время стремится к максимуму из двух, а не к сумме. Прямой +замер параллельного режима не проводился. + +Пиковую память процесса снять не удалось из-за ошибки в измерительном скрипте. + +## Порог кластеризации + +Число говорящих подбиралось автоматически (`num_clusters=-1`); варьировался +порог `FastClusteringConfig.threshold`. + +| Конфигурация | Найдено спикеров | Из них с речью ≥30 с | Речь по спикерам, мин | +|---|---:|---:|---| +| порог 0,5 | 29 | 11 | 7,4 / 5,5 / 1,6 / 1,2 | +| порог 0,7 | 12 | 7 | 7,4 / 7,0 / 2,2 / 2,2 | +| **порог 0,9** | **4** | **3** | **9,6 / 8,1 / 5,7 / 0,3** | +| явное `k=5`, порог 0,5 | 4 | 3 | 9,6 / 8,1 / 5,7 / 0,3 | + +На пороге 0,9 число содержательных кластеров совпало с составом из MoM: три +говорящих с 9,6, 8,1 и 5,7 минуты речи плюс остаточный кластер на 0,3 минуты. +На пороге 0,5 получилось 29 говорящих вместо трёх — десятикратное +переразбиение. + +Время от порога не зависит (153–157 с во всех конфигурациях): кластеризация +стоит доли секунды, платится за сегментацию и эмбеддинги. + +Два следствия. Первое: порог кластеризации — основная ручка качества, и +значение по умолчанию из примеров `sherpa-onnx` для этого материала непригодно. +Второе: одного удачного совпадения на одной записи недостаточно, чтобы принять +0,9 за дефолт, а явное указание числа участников нужно как страховка. + +## Чистота ASR-сегментов + +Основной вопрос замера. Для каждого из 274 ASR-сегментов посчитано перекрытие +с интервалами каждого говорящего (диаризация на пороге 0,9, 340 интервалов). +Чистота — доля мажоритарного говорящего в суммарном перекрытии сегмента. + +| Категория | Сегментов | Доля | Времени | +|---|---:|---:|---:| +| Чистых, один говорящий | 164 | 59,9% | 8,3 мин | +| С поддакиванием, <1 с чужой речи | 30 | 10,9% | 2,4 мин | +| **С чужой репликой, ≥1 с** | **74** | **27,0%** | **11,2 мин** | +| Без спикера вообще | 6 | 2,2% | 0,0 мин | + +| Порог чистоты | Сегментов ниже порога | Доля | Времени | +|---|---:|---:|---:| +| < 0,95 | 100 | 36,5% | 13,0 мин | +| < 0,90 | 91 | 33,2% | 11,6 мин | +| < 0,80 | 72 | 26,3% | 9,1 мин | +| < 0,70 | 56 | 20,4% | 7,1 мин | + +**27% сегментов содержат не менее секунды чужой речи, и на них приходится +11,2 минуты из 21,9 — больше половины транскрипта.** У 20% сегментов +мажоритарный говорящий занимает менее двух третей сегмента. + +### Подтверждение из текста ASR + +Загрязнённые сегменты содержат диалог, и это видно независимо от диаризации — +`gigaam-v3-e2e` обучена с диалоговой пунктуацией и сама ставит тире на смене +реплики: + +``` +[533,8-551,1] 17,3 с, мажоритарный spk3, чистота 0,67 + «— В нашем, по-моему.— В нашем?— В нашем.— А, отлично.— Ну, у нас просто + есть некий дата-тест, который на самом деле...— Мы же у них не + разворачиваем.—» + +[432,9-448,6] 15,7 с, мажоритарный spk2, чистота 0,58 + «Дим, а вот то, что ты из образа вытаскивал, там есть чё-то на что + посмотреть?— Бэг, бэг там есть, пи» + +[694,2-706,5] 12,3 с, мажоритарный spk2, чистота 0,37 + spk2 = 5,4 с, spk1 = 4,9 с, spk3 = 4,3 с — три человека в одном сегменте +``` + +Акустическая диаризация и пунктуация ASR указывают на одно и то же, поэтому +доля 27% вряд ли объясняется только ошибками кластеризации. + +Причина загрязнения — в способе нарезки: границы сегментов на ONNX-пути даёт +Silero VAD, то есть они проходят по тишине. В разговоре по ВКС участники +отвечают встык, паузы длиной с порог VAD не возникает, и диалог попадает в один +сегмент. Предположение о том, что задержка канала связи сама создаёт паузу на +смене говорящего, этими данными не подтверждается. + +Приведённые сегменты — сырой выход ASR. `formatter.py` объединяет их в абзацы +длиной до 60 секунд, поэтому в готовом транскрипте загрязнение будет выше. + +## Выводы + +- Диаризация через `sherpa-onnx` работает на целевой платформе без torch и без + токена Hugging Face; связка сегментация + эмбеддинги весит около 33 МБ. +- Стоимость — 11,1× RTFx, примерно 1,5 длительности ASR. Последовательный + запуск даёт 2,5-кратное замедление, совмещение проходов может сократить + накладные расходы, но отдельно не измерялось. +- Автоматическая оценка числа говорящих с порогом из примеров даёт 29 спикеров + вместо трёх. Порог требует калибровки, а явное указание числа участников — + отдельной ручки. +- Привязка спикера к целому ASR-сегменту по мажоритарному перекрытию + огрубляет результат существенно: 27% сегментов и больше половины времени + транскрипта содержат чужую речь длиннее секунды. Схема из бэклога в этом виде + непригодна. +- `onnx-asr` отдаёт потокенные таймкоды (`TimestampedResult`), поэтому + пословная привязка на ONNX-пути достижима. У OpenVINO GenAI такого выхода + нет, что ограничивает диаризацию на `--device openvino-*`. + +## Что нужно проверить дальше + +- Повторить измерение чистоты сегментов ещё на двух-трёх записях, включая + разговор на двоих, и убедиться, что 27% — не свойство именно этой встречи. +- Проверить границы диаризации на слух или сверкой с внешним транскриптом: + совпадение числа говорящих не доказывает правильность интервалов. +- Сравнить эмбеддинги, обученные не только на английском, на русской речи. +- Измерить производительность на целевом Intel Core i5 11-го поколения. +- Измерить параллельный режим ASR и диаризации.