Files
local-transcriber/docs/adr/007-word-level-speaker-diarization.md
T
Dmitriy Dementiev b8347004da docs(diarization): уточнены правила сведения и формат
- Зачем:
  - спецификация не должна оставлять неоднозначности перед реализацией диаризации.
- Что:
  - уточнены сведение слов, таймкоды и предупреждение о малом кластере.
  - очищены терминология и граница между ADR и спецификацией.
  - сохранён результат сравнения WeSpeaker и CAMPPlus.
- Проверка:
  - git diff --cached --check и проверка относительных ссылок.
2026-08-14 17:09:27 +03:00

9.3 KiB

ADR-007: Пословная диаризация через sherpa-onnx

Статус: Принято Дата: 2026-08-14

Контекст

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

Привязка одного говорящего ко всему сегменту распознавания оказалась слишком грубой. На трёх русскоязычных рабочих созвонах чужая реплика не короче секунды встретилась в 6–7% сегментов разговоров на двоих и в 27% сегментов встречи втроём. Сегменты распознавания проходят по тишине, а не по смене говорящего, поэтому сохранить контекст RNN-T и получить реплики можно только через более мелкую единицу сведения.

Эксперимент

Локальная связка sherpa-onnx с сегментацией Pyannote 3.0 и эмбеддингами WeSpeaker ResNet34 LM проверена на трёх записях с известным составом. Для автоматического определения числа голосовых кластеров выбран порог 0,89: это единственное проверенное значение, которое на трёх контрольных фрагментах дало 3 / 2 / 2 кластера. На полной встрече втроём остался ложный кластер длительностью 19,1 секунды; поэтому малые кластеры нельзя молча отбрасывать, а разметку нельзя считать эталоном точных границ и перекрывающейся речи.

Двуязычная CAMPPlus zh/en оказалась примерно на 30% быстрее и при известном числе участников улучшила прокси-метрику на двух записях, но для неё не нашлось общего автоматического порога без лишних кластеров или склейки реальных голосов. Поэтому она остаётся кандидатом только для будущего режима с обязательным явным числом участников, а не для первой версии.

На доступном слабом Intel baseline, Core i7-6820HQ с урезанным питанием, последовательные ASR и диаризация обработали час записи примерно за 23 минуты. Диаризация увеличивает полное время примерно в 2,4 раза, но остаётся быстрее реального времени и приемлема как явно включаемая функция. Конкретный Core i5 11-го поколения не проверен, поскольку такого устройства нет.

Исходные данные и ограничения зафиксированы в отчётах о калибровке, смешении говорящих и производительности на Intel.

Решение

Диаризацию реализуем как явно включаемый пост-процессинг через sherpa-onnx. Первая версия использует Pyannote segmentation 3.0, WeSpeaker ResNet34 LM, порог кластеризации 0,89 и автоматическое число кластеров; известное число участников можно передать явно.

Говорящий назначается слову с временной привязкой, а не сегменту распознавания. Каждый ASR-бэкенд приводит свой результат к общему набору слов с положением на временной шкале. Проходы ASR и диаризации независимо получают одно аудио, после чего отдельная операция сводит слова с интервалами разметки говорящих и объединяет соседние слова одного говорящего в реплики. Распознавание по-прежнему выполняется на полных сегментах и сохраняет контекст модели.

Диаризация не вводит грубый fallback на целый сегмент и не переключает устройство ASR ради получения пословных таймкодов. Существующий GPU→CPU fallback распознавания сохраняется и завершается до диаризации. Отсутствие пословных таймкодов или ошибка инициализации диаризатора останавливают запуск до ASR. Ошибка диаризации конкретного файла после успешного ASR не уничтожает полезный результат: сохраняется обычный транскрипт с явным предупреждением и ненулевым статусом, а батч продолжает остальные файлы. Порядок первой реализации, время жизни диаризатора, кеш и подробная матрица поведения находятся в спецификации.

Последствия

  • Общий контракт результата распознавания расширяется каноническими словами с временной привязкой; сегменты распознавания сохраняются для совместимости и контроля качества.
  • FasterWhisper, ONNX-ASR и OpenVINO должны экспортировать один и тот же пословный контракт. OpenVINO GenAI 2026.x уже предоставляет нужные таймкоды, поэтому ограничение находится в адаптере проекта, а не в движке.
  • sherpa-onnx становится обычной runtime-зависимостью, а две модели диаризации скачиваются и кешируются лениво при первом запросе.
  • Выход остаётся линейным Markdown с анонимными метками Speaker N. Сопоставление голосов с именами и специальная запись перекрывающейся речи не входят в ядро CLI.
  • Последовательный режим задаёт корректный baseline. Параллельный запуск и автоматическое включение на мощных устройствах требуют отдельных измерений после стабилизации.

Отклонённые альтернативы

Альтернатива Почему отклонена
Не делать диаризацию Оставляет главный продуктовый разрыв, хотя измеренная стоимость допустима для явной функции
Мажоритарный говорящий на весь сегмент распознавания Теряет чужие реплики на всех трёх проверенных записях
Сначала диаризация, затем ASR коротких интервалов Лишает RNN-T длинного контекста и ухудшает согласование и пунктуацию
pyannote.audio Тянет PyTorch и требует Hugging Face token с принятием лицензии
Сборка поверх приватных деталей onnx-asr Экономит небольшую отдельную зависимость ценой нестабильного внутреннего API и собственной кластеризации
Параллельные проходы в первой версии Нет прямого benchmark и измеренного общего пика памяти; сначала нужен корректный последовательный baseline