263 lines
8.6 KiB
Markdown
263 lines
8.6 KiB
Markdown
# 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` в логах.
|
||
Дальнейшие запросы используют загруженные модели.
|