docs(diarization): зафиксированы ADR и спецификация
- Зачем: - решения карты должны стать устойчивой основой для реализации диаризации. - Что: - добавлены ADR-007 и спецификация пословной диаризации. - дополнен доменный словарь и удалены закрытые направления из backlog. - Проверка: - git diff --cached --check.
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
# Диаризация говорящих в транскрипте
|
||||
|
||||
## Проблема
|
||||
|
||||
Текущий транскрипт знает только сегменты распознавания. Их границы проходят по
|
||||
тишине и не совпадают со сменой говорящего, поэтому один сегмент может содержать
|
||||
несколько реплик. Назначение одной метки всему сегменту искажает структуру
|
||||
диалога и делает транскрипт слабым сырьём для конспекта или протокола встречи.
|
||||
|
||||
[ADR-007](../adr/007-word-level-speaker-diarization.md) выбирает явную
|
||||
пословную диаризацию через `sherpa-onnx`. Эта спецификация фиксирует форму первой
|
||||
реализации, не меняя принятые решения.
|
||||
|
||||
## Цели
|
||||
|
||||
- По явному запросу строить реплики говорящих, сохраняя текст и длинный контекст
|
||||
ASR.
|
||||
- Поддержать один контракт слов с временной привязкой на FasterWhisper,
|
||||
ONNX-ASR и OpenVINO.
|
||||
- Сохранить предсказуемый single- и batch-режим при отсутствии речи, ошибках
|
||||
диаризации и малых голосовых кластерах.
|
||||
- Выдать компактный Markdown, удобный и человеку, и последующей обработке LLM.
|
||||
|
||||
## Не входит
|
||||
|
||||
- Автоматическое включение диаризации без флага и `--no-diarize`.
|
||||
- Параллельный запуск ASR и диаризации.
|
||||
- Сопоставление `Speaker N` с именами участников.
|
||||
- Постоянный кеш разметки говорящих, голосовые эмбеддинги и диагностические
|
||||
файлы.
|
||||
- Отдельный синтаксис для перекрывающейся речи.
|
||||
- Изменение устройства ASR или диаризации ради восстановления функции.
|
||||
|
||||
## Пользовательский интерфейс
|
||||
|
||||
- `--diarize` включает диаризацию. По умолчанию она выключена; ключ в
|
||||
`.transcriber.toml` в первой версии не добавляется.
|
||||
- `--speakers N`, где `N >= 1`, задаёт известное число участников и сам включает
|
||||
диаризацию. Без него число кластеров определяется автоматически.
|
||||
- `--threads N` остаётся единым бюджетом активного CPU-прохода. Значение целиком
|
||||
получает сначала ASR, затем диаризация; `0` оставляет настройки библиотек.
|
||||
- `--verbose` показывает в консоли прогресс, число кластеров и интервалов,
|
||||
длительность прохода и предупреждения, но не создаёт дополнительные файлы.
|
||||
- `--force` пересчитывает и ASR, и диаризацию. Без него готовый транскрипт, как и
|
||||
сейчас, пропускается целиком.
|
||||
|
||||
`sherpa-onnx` входит в обычные runtime-зависимости. Модели
|
||||
`sherpa-onnx-pyannote-segmentation-3-0` и
|
||||
`wespeaker_en_voxceleb_resnet34_LM.onnx` скачиваются и кешируются лениво при
|
||||
первом запросе диаризации. Отдельного installation extra нет.
|
||||
|
||||
## Контракты данных
|
||||
|
||||
Результат ASR сохраняет существующие сегменты распознавания и дополнительно
|
||||
содержит упорядоченные канонические слова. Для каждого слова известны текст,
|
||||
начало и конец на временной шкале исходной записи. Backend-специфичные токены и
|
||||
слова нормализуются в адаптере бэкенда; их обратная сборка должна сохранять
|
||||
распознанный текст с точностью до нормализации пробелов.
|
||||
|
||||
Результат диаризации — отдельная упорядоченная разметка говорящих: временные
|
||||
интервалы с анонимным идентификатором голосового кластера. ASR-бэкенд не знает о
|
||||
кластерах, а диаризатор не знает о распознанном тексте.
|
||||
|
||||
Операция сведения назначает слову кластер с наибольшим временным перекрытием.
|
||||
Если пересечения нет либо наибольшее перекрытие не единственно, слово получает
|
||||
неизвестного говорящего. Соседние слова одного говорящего объединяются в
|
||||
реплику; порядок слов и исходная временная шкала не меняются.
|
||||
|
||||
Все три ASR-пути обязаны предоставлять пословный контракт до включения
|
||||
диаризации:
|
||||
|
||||
| Путь | Источник временных привязок |
|
||||
|---|---|
|
||||
| FasterWhisper | word timestamps CTranslate2 |
|
||||
| ONNX-ASR | timestamped result модели |
|
||||
| OpenVINO | word-level timestamps `WhisperPipeline` |
|
||||
|
||||
Грубая подстановка метки на весь сегмент распознавания запрещена.
|
||||
|
||||
## Пайплайн и время жизни
|
||||
|
||||
После prescan и только при наличии файлов для обработки загружаются ASR-модель
|
||||
и один batch-owned диаризатор. До первого ASR проверяются доступность пословных
|
||||
таймкодов, модели диаризации и возможность создать диаризатор. Ошибка этого
|
||||
этапа останавливает весь запуск без частичных транскриптов.
|
||||
|
||||
Каждый файл обрабатывается последовательно:
|
||||
|
||||
1. ASR;
|
||||
2. диаризация, если ASR нашёл речь;
|
||||
3. сведение слов с разметкой говорящих;
|
||||
4. форматирование и запись Markdown.
|
||||
|
||||
Один диаризатор последовательно переиспользуется для всех файлов батча. Данные
|
||||
конкретной записи не становятся состоянием следующей. Объект освобождается при
|
||||
завершении команды и не переносится через `TranscribeFileResult`.
|
||||
|
||||
Разметка говорящих хранится только до сведения. Единственный постоянный
|
||||
продуктовый артефакт — Markdown-транскрипт; локальный кеш файлов моделей живёт
|
||||
по существующим правилам загрузчиков.
|
||||
|
||||
## Конфигурация диаризации
|
||||
|
||||
Автоматический режим использует:
|
||||
|
||||
- Pyannote segmentation 3.0;
|
||||
- WeSpeaker ResNet34 LM;
|
||||
- `FastClusteringConfig.threshold = 0.89`;
|
||||
- автоматическое число кластеров.
|
||||
|
||||
`--speakers N` передаёт явное число кластеров вместо автоматического. Малый
|
||||
кластер — кластер с речью короче максимума из 5 секунд и 2% длительности записи.
|
||||
Он не отбрасывается и получает обычный номер, но вызывает предупреждение.
|
||||
|
||||
## Формат Markdown
|
||||
|
||||
При двух и более найденных кластерах тело состоит из линейных реплик:
|
||||
|
||||
```markdown
|
||||
[09:07] Speaker 1: Мы же у них не разворачиваемся…
|
||||
|
||||
[09:18] Speaker 2: Мне гораздо проще накатывать обновления…
|
||||
```
|
||||
|
||||
- Печатается только начало реплики, округлённое до целой секунды. Для записей
|
||||
длиннее часа используется `[HH:MM:SS]`, иначе `[MM:SS]`.
|
||||
- Метка `Speaker N` не получает Markdown-выделение.
|
||||
- Нумерация начинается заново для каждого файла; номера назначаются по порядку
|
||||
первого появления кластера в словах транскрипта.
|
||||
- Смена говорящего всегда начинает новую реплику.
|
||||
- Речь одного говорящего дополнительно разбивается по паузе не меньше 2 секунд
|
||||
и максимальной длительности реплики 60 секунд.
|
||||
- Слова без назначенного кластера группируются под `Speaker ?`.
|
||||
- Перекрывающаяся речь остаётся в хронологическом порядке без особого
|
||||
синтаксиса.
|
||||
- В шапку добавляется число голосовых кластеров и предупреждения. Таблица
|
||||
длительности по кластерам не выводится.
|
||||
|
||||
Без успешной разметки нескольких говорящих сохраняется нынешний формат
|
||||
обычного транскрипта с диапазонами времени.
|
||||
|
||||
## Деградация и статус команды
|
||||
|
||||
| Ситуация | Артефакт | Консоль и шапка | Статус |
|
||||
|---|---|---|---|
|
||||
| Диаризация не запрошена | Обычный транскрипт | Без новых сообщений | Текущий |
|
||||
| ASR не нашёл речь | Текущий пустой Markdown | Речь не обнаружена; диаризация не запускалась | 0 |
|
||||
| Найдено не меньше двух кластеров | Транскрипт с `Speaker N` | Число кластеров и предупреждения | 0, если нет иной ошибки |
|
||||
| Найден один кластер | Обычный транскрипт без `Speaker 1` | Причина в консоли и шапке | Ненулевой |
|
||||
| Есть слова без пересечения | Транскрипт с `Speaker ?` | Число таких слов | 0 |
|
||||
| Есть малый кластер | Транскрипт со всеми кластерами | Длительность малого кластера | 0 |
|
||||
| Разметка пуста при непустом ASR | Обычный транскрипт | Явное предупреждение | Ненулевой |
|
||||
| Ошибка диаризации конкретного файла | Обычный транскрипт | Явное предупреждение | Ненулевой |
|
||||
| Нет пословных таймкодов или не инициализировался диаризатор | Файлы не обрабатываются | Понятная ошибка до ASR | Ненулевой |
|
||||
|
||||
В батче деградированный файл записывается, учитывается как неуспешная
|
||||
диаризация, а остальные файлы продолжают обрабатываться. Итоговый статус батча
|
||||
ненулевой, если хотя бы один файл деградировал или завершился ошибкой.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
### Автоматические проверки
|
||||
|
||||
- Адаптер каждого ASR-бэкенда возвращает монотонные слова с временной
|
||||
привязкой; сборка слов сохраняет текст сегментов с точностью до пробелов.
|
||||
- Сведение покрывает смену говорящего, отсутствие пересечения, равное
|
||||
перекрытие, пунктуацию на границе реплик и хронологический порядок.
|
||||
- Форматтер проверяется для обычных, часовых, неизвестных и малых кластеров,
|
||||
`Speaker ?`, паузы 2 секунды и предела 60 секунд.
|
||||
- CLI проверяет несовместимые и граничные значения, не запускает ASR при ошибке
|
||||
preflight и соблюдает всю матрицу деградации в single- и batch-режимах.
|
||||
- Батч создаёт диаризатор ровно один раз, пропускает его для пустого ASR,
|
||||
переиспользует между файлами и не пишет промежуточный кеш.
|
||||
- `--threads`, `--verbose` и `--force` сохраняют описанную семантику.
|
||||
|
||||
Все автоматические тесты мокают движки и не скачивают реальные модели.
|
||||
|
||||
### Ручная проверка
|
||||
|
||||
Финальная сборка прогоняется на трёх записях из отчётов карты:
|
||||
|
||||
- текст до и после сведения совпадает с точностью до переносов и пробелов;
|
||||
- на плотном диалоге вручную проверяются устойчивость «голос → кластер», смены
|
||||
говорящего, пропуски, малый остаточный кластер и перекрывающаяся речь;
|
||||
- автоматическая конфигурация воспроизводит наблюдённую форму результата:
|
||||
три основных и один малый остаточный кластер на Data Test, по два кластера на
|
||||
T2 BDMA и Yantar;
|
||||
- последовательный прогон на доступном Intel baseline остаётся быстрее
|
||||
реального времени; фактические wall time и peak RSS записываются рядом с
|
||||
результатом проверки.
|
||||
|
||||
## Документация
|
||||
|
||||
README должен описать новые CLI-флаги, ленивую загрузку моделей, ожидаемую
|
||||
стоимость, формат `Speaker N`, предупреждения и batch-поведение. Направления
|
||||
«Диаризация» и «Ручка нарезки абзацев» удаляются из backlog: первое перешло в
|
||||
эту спецификацию, второе закрыто разрывом реплики на смене говорящего.
|
||||
|
||||
После стабилизации отдельно рассматриваются
|
||||
[параллельный запуск](https://git.dementev.space/ddmitry/local-transcriber/issues/22)
|
||||
и [hardware-aware default](https://git.dementev.space/ddmitry/local-transcriber/issues/23).
|
||||
Reference in New Issue
Block a user