- Зачем: - спецификация не должна оставлять неоднозначности перед реализацией диаризации. - Что: - уточнены сведение слов, таймкоды и предупреждение о малом кластере. - очищены терминология и граница между ADR и спецификацией. - сохранён результат сравнения WeSpeaker и CAMPPlus. - Проверка: - git diff --cached --check и проверка относительных ссылок.
208 lines
16 KiB
Markdown
208 lines
16 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-бэкенд
|
|
не знает о кластерах, а диаризатор не знает о распознанном тексте.
|
|
|
|
Операция сведения суммирует временное перекрытие слова с интервалами каждого
|
|
кластера и назначает кластер с единственным наибольшим ненулевым перекрытием.
|
|
Если пересечения нет либо несколько кластеров делят наибольшее значение, слово
|
|
получает неизвестного говорящего: порядок кластеров не используется как
|
|
искусственная развязка ничьей. Соседние слова одного говорящего объединяются в
|
|
реплику; порядок слов и исходная временная шкала не меняются.
|
|
|
|
Все три 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;
|
|
- порог кластеризации 0,89;
|
|
- автоматическое число кластеров.
|
|
|
|
`--speakers N` передаёт явное число кластеров вместо автоматического. Для
|
|
предупреждения используется диагностическая граница из калибровки: малым
|
|
считается кластер с речью короче максимума из 5 секунд и 2% длительности записи.
|
|
Граница влияет только на предупреждение — кластер не отбрасывается, получает
|
|
обычный номер и не меняет статус команды.
|
|
|
|
## Формат Markdown
|
|
|
|
При двух и более найденных кластерах тело состоит из линейных реплик:
|
|
|
|
```markdown
|
|
[09:07] Speaker 1: Мы же у них не разворачиваемся…
|
|
|
|
[09:18] Speaker 2: Мне гораздо проще накатывать обновления…
|
|
```
|
|
|
|
- Печатается только начало реплики; доли секунды отбрасываются, а не округляются
|
|
(`09:07.96` → `[09:07]`). Для записей длиннее часа используется
|
|
`[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 ?`, пунктуацию на границе реплик
|
|
и хронологический порядок.
|
|
- Форматтер проверяется для обычных, часовых, неизвестных и малых кластеров,
|
|
`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).
|