Files
2026-05-19 10:12:57 +00:00

8.6 KiB
Raw Permalink Blame History

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: 04с
          │                       │
          └───────────┬───────────┘
                      ▼
               reconciliation
               (сшивка по словам)
                      │
                      ▼
  SPEAKER_00: Привет, как дела с проектом?
  SPEAKER_01: Нормально, закончил doctype.

Предварительные требования

  1. Docker + Docker Compose v2+

  2. NVIDIA Container Toolkit (nvidia-ctk)

    # проверить, что GPU виден Docker'у
    docker run --rm --gpus all nvidia/smi
    
  3. HuggingFace-токен для pyannote

Быстрый старт

# 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 — только транскрибация

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):

{
  "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 — только диаризация

curl -X POST http://localhost:8000/diarize \
  -F "file=@audio.aac" \
  -F "num_speakers=2"

Ответ:

{
  "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 — транскрибация + диаризация (параллельно)

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"

Ответ:

{
  "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:

# 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 Размер батча (132)
num_speakers pyannote auto Точное число спикеров
min_speakers pyannote Минимум спикеров
max_speakers pyannote Максимум спикеров
response_format gateway json json / srt / vtt / txt

Конфигурация через .env

# Обязательные
HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# Опциональные
WHISPER_MODEL=large-v3       # tiny/base/small/medium/large-v3/turbo
WHISPER_BATCH_SIZE=16        # размер батча
PYANNOTE_DEVICE=cpu          # cpu или cuda

Полезные команды

# Логи конкретного сервиса
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 ~24 мин
Pyannote CPU ~510 мин
/process (параллельно) ~510 мин
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 в логах. Дальнейшие запросы используют загруженные модели.