- Зачем: - решения карты должны стать устойчивой основой для реализации диаризации. - Что: - добавлены ADR-007 и спецификация пословной диаризации. - дополнен доменный словарь и удалены закрытые направления из backlog. - Проверка: - git diff --cached --check.
202 lines
15 KiB
Markdown
202 lines
15 KiB
Markdown
# Диаризация говорящих в транскрипте
|
|
|
|
## Проблема
|
|
|
|
Текущий транскрипт знает только сегменты распознавания. Их границы проходят по
|
|
тишине и не совпадают со сменой говорящего, поэтому один сегмент может содержать
|
|
несколько реплик. Назначение одной метки всему сегменту искажает структуру
|
|
диалога и делает транскрипт слабым сырьём для конспекта или протокола встречи.
|
|
|
|
[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).
|