# 3_diarization — микросервисная транскрибация с диаризацией Три Docker-контейнера: Whisper (GPU) + pyannote (CPU) + API Gateway. Работают параллельно. Итоговое время ≈ max(whisper, pyannote), не сумма. ## Архитектура ``` POST /process (audio.aac) │ ┌──────┴──────┐ │ Gateway │ :8000 └──────┬──────┘ ┌───────────┴───────────┐ ▼ ▼ ┌──────────────┐ ┌──────────────┐ │ Whisper │ │ Pyannote │ │ (GPU) │ │ (CPU) │ │ :8001 │ │ :8002 │ └──────┬───────┘ └──────┬───────┘ │ │ ▼ ▼ текст + слова кто когда говорил с таймкодами SPEAKER_00: 0–4с │ │ └───────────┬───────────┘ ▼ reconciliation (сшивка по словам) │ ▼ SPEAKER_00: Привет, как дела с проектом? SPEAKER_01: Нормально, закончил doctype. ``` ## Предварительные требования 1. **Docker** + **Docker Compose** v2+ 2. **NVIDIA Container Toolkit** (`nvidia-ctk`) ```bash # проверить, что GPU виден Docker'у docker run --rm --gpus all nvidia/smi ``` 3. **HuggingFace-токен** для pyannote - Создать токен: https://huggingface.co/settings/tokens - Принять условия (заполнить форму на каждой странице): - https://huggingface.co/pyannote/segmentation-3.0 - https://huggingface.co/pyannote/speaker-diarization-3.1 ## Быстрый старт ```bash # 1. Клонировать / скопировать папку 3_diarization cd 3_diarization # 2. Создать .env из примера и вставить свой HF-токен cp .env.example .env nano .env # вставить HF_TOKEN=hf_... # 3. Собрать и запустить docker compose up --build -d # 4. Подождать ~1–2 минуты, пока модели загрузятся docker compose logs -f # Ждём: # whisper-service: "Model loaded in X.Xs" # pyannote-service: "Pipeline loaded in X.Xs" # 5. Проверить здоровье curl http://localhost:8000/health ``` ## API ### POST /transcribe — только транскрибация ```bash curl -X POST http://localhost:8000/transcribe \ -F "file=@audio.aac" \ -F "language=ru" \ -F "initial_prompt=Event Forge, Frappe, doctype" \ -F "batch_size=16" ``` Ответ (JSON): ```json { "language": "ru", "language_probability": 0.99, "duration": 197.6, "processing_time": 8.2, "segments": [ { "start": 0.0, "end": 3.2, "text": "Привет, это запись.", "words": [ {"start": 0.0, "end": 0.4, "word": " Привет"}, {"start": 0.4, "end": 0.6, "word": ","}, {"start": 0.7, "end": 1.1, "word": " это"}, {"start": 1.1, "end": 1.5, "word": " запись."} ] } ] } ``` ### POST /diarize — только диаризация ```bash curl -X POST http://localhost:8000/diarize \ -F "file=@audio.aac" \ -F "num_speakers=2" ``` Ответ: ```json { "speakers": ["SPEAKER_00", "SPEAKER_01"], "num_speakers": 2, "processing_time": 45.3, "turns": [ {"start": 0.0, "end": 4.2, "speaker": "SPEAKER_00"}, {"start": 4.2, "end": 9.1, "speaker": "SPEAKER_01"} ] } ``` ### POST /process — транскрибация + диаризация (параллельно) ```bash curl -X POST http://localhost:8000/process \ -F "file=@audio.aac" \ -F "language=ru" \ -F "initial_prompt=Event Forge, Frappe, doctype, Vladimir" \ -F "num_speakers=2" \ -F "batch_size=16" ``` Ответ: ```json { "language": "ru", "duration": 197.6, "processing_time": 47.1, "whisper_time": 8.2, "pyannote_time": 45.3, "speakers": ["SPEAKER_00", "SPEAKER_01"], "num_speakers": 2, "utterances": [ { "speaker": "SPEAKER_00", "start": 0.0, "end": 4.1, "text": "Привет, как дела с проектом?", "words": [...] }, { "speaker": "SPEAKER_01", "start": 4.2, "end": 9.0, "text": "Нормально, закончил doctype для метрик.", "words": [...] } ] } ``` ### Форматы вывода Добавьте `response_format` в `/transcribe` или `/process`: ```bash # SRT-субтитры с именами спикеров curl -X POST http://localhost:8000/process \ -F "file=@audio.aac" \ -F "language=ru" \ -F "response_format=srt" # VTT -F "response_format=vtt" # Текст (спикер: реплика) -F "response_format=txt" ``` ## Параметры | Параметр | Где | По умолчанию | Описание | |------------------|---------------|--------------|---------------------------------------| | `language` | whisper | auto | Код языка (ru, en, de, ...) | | `initial_prompt` | whisper | — | Термины/имена через запятую | | `beam_size` | whisper | 5 | Размер beam search | | `batch_size` | whisper | 16 | Размер батча (1–32) | | `num_speakers` | pyannote | auto | Точное число спикеров | | `min_speakers` | pyannote | — | Минимум спикеров | | `max_speakers` | pyannote | — | Максимум спикеров | | `response_format`| gateway | json | json / srt / vtt / txt | ## Конфигурация через .env ```bash # Обязательные HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx # Опциональные WHISPER_MODEL=large-v3 # tiny/base/small/medium/large-v3/turbo WHISPER_BATCH_SIZE=16 # размер батча PYANNOTE_DEVICE=cpu # cpu или cuda ``` ## Полезные команды ```bash # Логи конкретного сервиса docker compose logs -f whisper-service docker compose logs -f pyannote-service # Перезапустить один сервис (например, после смены модели) docker compose restart whisper-service # Остановить всё docker compose down # Пересобрать всё с нуля docker compose down && docker compose up --build -d ``` ## Производительность (RTX 5060 16 ГБ) Ориентировочные цифры для 1 часа аудио: | Операция | Время | |-----------------------|------------| | Whisper large-v3 b=16 | ~2–4 мин | | Pyannote CPU | ~5–10 мин | | /process (параллельно)| ~5–10 мин | | Reconciliation | ~1 сек | Bottleneck — pyannote на CPU. Если нужно ещё быстрее — поменяйте `PYANNOTE_DEVICE=cuda` в `.env` и пересоберите pyannote-service с GPU-версией PyTorch (потребует изменения Dockerfile). ## Траблшутинг **Контейнер whisper-service не стартует / `no CUDA-capable device`** — проверьте `nvidia-container-toolkit`. Тест: `docker run --rm --gpus all nvidia/smi`. **pyannote-service падает с `401 Unauthorized`** — невалидный HF_TOKEN или не приняты условия моделей на HuggingFace. **`CUBLAS_STATUS_NOT_SUPPORTED`** — int8 на Blackwell. В конфиге уже стоит `WHISPER_COMPUTE_TYPE=float16`. **OOM при batch_size=16** — уменьшите `WHISPER_BATCH_SIZE=8` в `.env`. **Первый запрос медленный** — модели загружаются при старте контейнера (large-v3 ~3 ГБ, pyannote ~500 МБ). Дождитесь `Model loaded` в логах. Дальнейшие запросы используют загруженные модели.