docs(diarization): зафиксированы ADR и спецификация
- Зачем: - решения карты должны стать устойчивой основой для реализации диаризации. - Что: - добавлены ADR-007 и спецификация пословной диаризации. - дополнен доменный словарь и удалены закрытые направления из backlog. - Проверка: - git diff --cached --check.
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
# ADR-007: Пословная диаризация через sherpa-onnx
|
||||
|
||||
**Статус**: Принято
|
||||
**Дата**: 2026-08-14
|
||||
|
||||
## Контекст
|
||||
|
||||
Разделение говорящих — главный структурный разрыв между локальным транскриптом
|
||||
и облачными сервисами в сценарии подготовки конспектов и протоколов встреч.
|
||||
Диаризация при этом не является ещё одним движком распознавания: она независимо
|
||||
строит [разметку говорящих](../../CONTEXT.md#language), которую затем нужно
|
||||
свести с результатом ASR.
|
||||
|
||||
Привязка одного говорящего ко всему сегменту распознавания оказалась слишком
|
||||
грубой. На трёх русскоязычных рабочих созвонах чужая реплика не короче секунды
|
||||
встретилась в 6–7% сегментов разговоров на двоих и в 27% сегментов встречи
|
||||
втроём. Сегменты распознавания проходят по тишине, а не по смене говорящего,
|
||||
поэтому сохранить контекст RNN-T и получить реплики можно только через более
|
||||
мелкую единицу сведения.
|
||||
|
||||
## Эксперимент
|
||||
|
||||
Локальная связка `sherpa-onnx` с сегментацией Pyannote 3.0 и эмбеддингами
|
||||
WeSpeaker ResNet34 LM проверена на трёх записях с известным составом. Для
|
||||
автоматического определения числа голосовых кластеров выбран порог 0,89: это
|
||||
единственное проверенное значение, которое на трёх контрольных фрагментах дало
|
||||
3 / 2 / 2 кластера. На полной встрече втроём остался ложный кластер длительностью
|
||||
19,1 секунды; поэтому малые кластеры нельзя молча отбрасывать, а разметку нельзя
|
||||
считать эталоном точных границ и перекрывающейся речи.
|
||||
|
||||
На доступном слабом Intel baseline, Core i7-6820HQ с урезанным питанием,
|
||||
последовательные ASR и диаризация обработали час записи примерно за 23 минуты.
|
||||
Диаризация увеличивает полное время примерно в 2,4 раза, но остаётся быстрее
|
||||
реального времени и приемлема как явно включаемая функция. Конкретный Core i5
|
||||
11-го поколения не проверен, поскольку такого устройства нет.
|
||||
|
||||
Исходные данные и ограничения зафиксированы в отчётах о
|
||||
[калибровке](../benchmarks/2026-08-14-diarization-calibration.md),
|
||||
[смешении говорящих](../benchmarks/2026-08-14-asr-segment-speaker-mixing.md) и
|
||||
[производительности на Intel](../benchmarks/2026-08-14-diarization-intel-i7.md).
|
||||
|
||||
## Решение
|
||||
|
||||
Диаризацию реализуем как явно включаемый пост-процессинг через `sherpa-onnx`.
|
||||
Первая версия использует Pyannote segmentation 3.0, WeSpeaker ResNet34 LM,
|
||||
порог кластеризации 0,89 и автоматическое число кластеров; известное число
|
||||
участников можно передать явно.
|
||||
|
||||
Говорящий назначается [слову с временной
|
||||
привязкой](../../CONTEXT.md#language), а не сегменту распознавания. Каждый
|
||||
ASR-бэкенд приводит свой результат к общему набору слов с положением на
|
||||
временной шкале. Проходы ASR и диаризации независимо получают одно аудио, после
|
||||
чего отдельная операция сводит слова с интервалами разметки говорящих и
|
||||
объединяет соседние слова одного говорящего в реплики. Распознавание по-прежнему
|
||||
выполняется на полных сегментах и сохраняет контекст модели.
|
||||
|
||||
Первая реализация последовательна на всех устройствах: ASR, диаризация,
|
||||
сведение, Markdown. В батче один диаризатор создаётся после prescan,
|
||||
переиспользуется для всех файлов и освобождается вместе с командой. Разметка
|
||||
говорящих живёт только в памяти текущего запуска; постоянного кеша результата
|
||||
нет.
|
||||
|
||||
Грубого fallback на целый сегмент и автоматического переключения устройства
|
||||
нет. Отсутствие пословных таймкодов или ошибка инициализации диаризатора
|
||||
останавливают запуск до ASR. Ошибка диаризации конкретного файла после успешного
|
||||
ASR не уничтожает полезный результат: сохраняется обычный транскрипт с явным
|
||||
предупреждением и ненулевым статусом, а батч продолжает остальные файлы.
|
||||
Подробная матрица поведения находится в
|
||||
[спецификации](../specs/2026-08-14-speaker-diarization.md).
|
||||
|
||||
## Последствия
|
||||
|
||||
- Общий контракт результата распознавания расширяется каноническими словами с
|
||||
временной привязкой; сегменты распознавания сохраняются для совместимости и
|
||||
контроля качества.
|
||||
- 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 |
|
||||
Reference in New Issue
Block a user