Решения

Распознавание речи на GPU: Whisper и WhisperX

Задача страницы. Практический гид по запуску и масштабированию ASR на базе Whisper/WhisperX для двух режимов — real‑time (интерактив) и батчи (офлайн), с голосовой активностью (VAD), таймкодами и выравниванием (alignment). Дадим типовые пайплайны, sizing по GPU, конфиги (Docker/K8s/FastAPI SSE), метрики и экономику. Все примеры ориентированы на инфраструктуру с локальным NVMe‑кэшем и режимами On‑Demand/Interruptible.

TL;DR

Когда это нужно (сценарии)

  • Реал‑тайм стенография и ассистенты: звонки, колл‑центры, совещания, субтитры в прямом эфире, интерактивные демо.
  • Пакетная расшифровка медиаархивов: подкасты, вебинары, лекции, медиатеки, телеконтент.
  • RTSP/стримы с VAD: автоматическое включение распознавания на голосовой активности с паддингами.
  • Пост‑обработка: нормализация, пунктуация, сплит по спикерам, WhisperX‑выравнивание для покадровых субтитров.
  • ML‑пайплайны: поиск по речи, индексирование, RAG по транскриптам, автозаголовки/теги.

Архитектуры и пайплайны

1) Real‑time (низкая латентность, SSE/WebSocket)

				
					Audio In (Mic/WebRTC/RTSP)
   └─> Resample (16 kHz mono) ──> VAD (thresholds, pad)
        └─> Ring Buffer (200–500 ms)
             └─> GPU: Whisper (stream decode, FP16/BF16)
                  └─> [Optional] WhisperX Align (post)
                       └─> Post-process (punct/normalize)
                            └─> Stream out (SSE/WebSocket JSON chunks)

				
			

Особенности: короткие окна, стабильная RTF<1, backpressure, drop‑политики буфера, прогрессивные таймкоды, обрезка «щелчков» на границах VAD.

2) Батчи (массовая обработка, Interruptible)
				
					Object Storage ─┬─> Sharder (≤120 s, overlap=0.2–0.5 s)
                 │
                 ├─> Work Queue (priority, retries)
                 │
                 └─> N x GPU Workers (batch inference, AMP)
                        └─> Merge Segments (de-dup via overlap)
                             └─> [Optional] WhisperX Align + Diarization
                                  └─> Writer (JSON/SRT/VTT/TSV)
                                       └─> Object Storage / DB


				
			

Особенности: чанк ≤120 с, оверлап 0.2–0.5 с для стыков, ретраи при прерывании, NVMe‑кэш для временных WAV/мел‑спектрограмм.

3) Сервисная шина (мульти‑тенант)

				
					Ingress ─> Auth ─> Topic/Queue ─> GPU Pools (by profile) ─> Results Store
                                └> Autoscaler (U target)

				
			

Разнесите модели/языки по пулам «24/48/80 ГБ», включите авто‑скейл по U и RTF.

GPU‑профили и ориентиры

Базовый ориентир по одновременным потокам real‑time при RTF_target ≈ 0.5 и целевой загрузке U ≈ 0.7. Для батчей масштабируйте по формуле в разделе «Экономика».

Профиль GPU

Память

Типичные модели

Real‑time потоки*

Батчи (параллель)

Комментарии

24 ГБ (Compact)

24 ГБ

Whisper Small/Base

1–3

2–6

Легкие языковые модели, хороший интерактив в моно 16 кГц.

48 ГБ (Balanced)

48 ГБ

Whisper Medium

3–6

6–12

Баланс точности/скорости; устойчивый RTF для многоязычия.

80 ГБ (HQ)

80 ГБ

Whisper Large

6–12

12–24

Крупные модели, сложные домены, выравнивание + пост‑проц.

* Диапазоны зависят от аудио‑битрейта, языка, шумов, пост‑процесса и I/O. Для точного sizing снимайте RTF на своем корпусе.

См. тюнинг и профилировку: https://cloudcompute.ru/solutions/performance-tuning/https://cloudcompute.ru/solutions/throughput-vs-latency/https://cloudcompute.ru/solutions/fp8-bf16/

Конфиги и скелеты кода

Docker Compose (реал‑тайм + батчи + выравнивание)

				
					version: "3.9"

x-common-env: &common_env
  CACHE_DIR: /var/cache/asr
  MODELS_DIR: /models
  SAMPLE_RATE: "16000"
  CHANNELS: "1"
  VAD_THRESHOLD: "0.6"
  VAD_MIN_SPEECH_MS: "300"
  VAD_PAD_MS: "150"
  WHISPER_MODEL: "medium"     # small|base|medium|large
  WHISPER_PRECISION: "fp16"   # bf16|fp16
  ALIGN_ENABLE: "true"        # WhisperX
  LANG: "auto"                # или ru|en|...

services:
  asr-realtime:
    image: cloudcompute/asr-whisper:latest
    environment:
      <<: *common_env
      SERVICE_MODE: "realtime"
    deploy:
      resources:
        reservations:
          devices: [{ capabilities: ["gpu"] }]
    ports: ["8080:8080"]
    volumes:
      - /nvme/asr-cache:/var/cache/asr
      - /nvme/models:/models
    command: ["python", "serve_realtime.py", "--host=0.0.0.0", "--port=8080"]

  asr-batch:
    image: cloudcompute/asr-whisper:latest
    environment:
      <<: *common_env
      SERVICE_MODE: "batch"
      MAX_CHUNK_SECONDS: "120"
      OVERLAP_SECONDS: "0.3"
    deploy:
      resources:
        reservations:
          devices: [{ capabilities: ["gpu"] }]
    volumes:
      - /nvme/asr-cache:/var/cache/asr
      - /nvme/models:/models
      - /mnt/media:/data   # аудио архива
    command: ["python", "run_batch.py", "--input=/data/in/", "--output=/data/out/"]

  aligner:
    image: cloudcompute/whisperx-align:latest
    environment:
      MODELS_DIR: /models
    deploy:
      resources:
        reservations:
          devices: [{ capabilities: ["gpu"] }]
    volumes:
      - /nvme/models:/models
      - /mnt/media:/data
    command: ["python", "align.py", "--input=/data/out/", "--write-word-timestamps"]


				
			

K8s (скелет деплоймента под 1 GPU)

				
					apiVersion: apps/v1
kind: Deployment
metadata:
  name: asr-realtime
spec:
  replicas: 2
  selector: { matchLabels: { app: asr-realtime } }
  template:
    metadata:
      labels: { app: asr-realtime }
    spec:
      containers:
        - name: asr
          image: cloudcompute/asr-whisper:latest
          ports: [{ containerPort: 8080 }]
          env:
            - { name: WHISPER_MODEL, value: "medium" }
            - { name: WHISPER_PRECISION, value: "fp16" }
            - { name: SAMPLE_RATE, value: "16000" }
            - { name: VAD_THRESHOLD, value: "0.6" }
          volumeMounts:
            - { name: models, mountPath: /models }
            - { name: cache,  mountPath: /var/cache/asr }
          resources:
            limits:
              nvidia.com/gpu: 1
              memory: "24Gi"
              cpu: "4"
      volumes:
        - name: models
          hostPath: { path: /nvme/models }
        - name: cache
          hostPath: { path: /nvme/asr-cache }


				
			

Конфиг пайплайна (YAML)

				
					pipeline:
  resample:
    sample_rate: 16000
    channels: 1
  vad:
    enabled: true
    threshold: 0.6
    min_speech_ms: 300
    pad_ms: 150
  whisper:
    model: medium
    precision: fp16       # bf16|fp16
    language: auto
    beam_size: 5
    no_speech_threshold: 0.6
  alignment:
    enabled: true
    word_timestamps: true
  output:
    formats: [json, srt, vtt]
    timestamps: segment_and_word


				
			

FastAPI + SSE (стриминг частичных результатов)

				
					from fastapi import FastAPI, BackgroundTasks
from fastapi.responses import StreamingResponse
from sse_starlette.sse import EventSourceResponse
import asyncio, json

app = FastAPI()

async def stream_transcribe(audio_iter):
    # audio_iter -> async generator of PCM frames @16kHz mono
    async for partial in asr_infer(audio_iter):
        # partial: {"text": "...", "ts_start": 12.34, "ts_end": 13.20, "confidence": 0.92}
        yield json.dumps(partial)

@app.post("/stream")
async def stream_endpoint():
    async def event_gen():
        async for chunk in stream_transcribe(audio_source()):
            yield {"event": "partial", "data": chunk}
        yield {"event": "done", "data": "{}"}
    return EventSourceResponse(event_gen())

# Заглушки под аудио и инференс:
async def audio_source():
    while True:
        frame = await next_pcm_frame_or_none()
        if frame is None:
            break
        yield frame

async def asr_infer(audio_iter):
    # VAD + ring buffer + whisper decode (FP16/BF16), progressive timestamps
    async for seg in decode_with_vad(audio_iter):
        yield seg


				
			

Пакетная обработка (псевдокод)

				
					from pathlib import Path
from concurrent.futures import ThreadPoolExecutor

def shard_audio(wav_path, max_chunk=120.0, overlap=0.3):
    # вернёт список чанков (start, end)
    ...

def transcribe_chunk(wav_path, t0, t1):
    # вырезать в tmp, подать в модель, вернуть сегменты с локальными ts
    ...

def merge_segments(list_of_segments, overlap=0.3):
    # склеить и удалить дубли из оверлапа
    ...

def batch_job(input_dir, output_dir):
    for wav in Path(input_dir).glob("*.wav"):
        chunks = shard_audio(wav)
        with ThreadPoolExecutor(max_workers=GPU_PARALLEL) as ex:
            parts = list(ex.map(lambda c: transcribe_chunk(wav, *c), chunks))
        segments = merge_segments(parts)
        if ALIGN_ENABLE:
            segments = whisperx_align(segments, wav)
        write_outputs(segments, output_dir, wav.stem, formats=["json","srt","vtt"])


				
			

Наблюдаемость, метрики, алерты

Ключевые метрики (Prometheus‑стиль):

  • RTF: asr_rtf{mode=rt|batch} — real‑time factor (GPU_secs/audio_secs), цель < 1.0 для интерактива.
  • Latency: asr_latency_seconds{phase=enqueue|infer|align|post}, следим за p50/p95.
  • Throughput: asr_segments_per_sec, asr_tokens_per_sec.
  • Quality: asr_confidence_avg, asr_vad_drop_rate, asr_overlap_fixups.
  • GPU: asr_gpu_utilization, asr_gpu_memory_bytes, asr_gpu_mem_peak_bytes.
  • I/O: asr_nvme_read_mb_s, asr_nvme_write_mb_s.

Алерты (пример):

  • RTF_p95 > 0.9 в течение 3 мин — масштабируем пул или упрощаем пост‑процесс.
  • GPU_mem_peak_bytes / HBM > 0.9 — снизить размер батча/модель/включить offload.
  • asr_vad_drop_rate > 0.05 — пересмотреть пороги VAD/буферы.

См. хабы: https://cloudcompute.ru/solutions/monitoring-logging/https://cloudcompute.ru/solutions/llm-inference/observability/

Экономика и формулы

Обозначения: c_gpu — цена GPU/час, U — целевая загрузка GPU (0–1), RTF — real‑time factor.

  • Сколько GPU нужно (стриминг):
    GPU_count = ceil( (Σ RTF_streams) / U )
    Пример: 8 потоков, каждый RTF≈0.4, U=0.7ceil( (8×0.4)/0.7 ) = ceil(4.57) = 5 GPU.
  • Стоимость минуты аудио:
    Cost_per_min = (c_gpu * RTF / U) / 60.
  • Батчи:
    Время партии T_batch ≈ (RTF × L_audio_total) / (GPU_count × U).
    Cost_total ≈ c_gpu × GPU_count × T_batch.

См. калькулятор и подходы:
https://cloudcompute.ru/solutions/cost-planner/https://cloudcompute.ru/solutions/throughput-vs-latency/

Безопасность и политики

  • PII и чувствительные данные: включайте маскирование в пост‑процессе (телефоны, карты, e‑mail), храните оригиналы минимально.
  • Ретеншн: временные WAV/чанки на NVMe — авто‑очистка по TTL; логирование без сырого аудио.
  • Ключи/токены: секреты только через переменные окружения/Secret‑хранилища.
  • Изоляция: раздельные пулы GPU по тенантам/языкам, контроль доступа к бакетам.

Подробнее: https://cloudcompute.ru/solutions/security/https://cloudcompute.ru/solutions/llm-inference/guardrails/

Траблшутинг

Симптом

Возможная причина

Решение

«Глотает» начало фраз

VAD/буфер слишком агрессивны

Увеличить VAD_PAD_MS и размер ring‑buffer

Задержка скачет

I/O перегрузка, маленький буфер

NVMe‑кэш, увеличить буферы, ограничить конкуренцию

VRAM OOM

Слишком крупная модель/батч

Понизить модель/батч, FP16/BF16, offload промежуточных тензоров

RTF ≈ 1 и выше

Перегрузка GPU/пост‑процесс

Упростить beam, снять выравнивание в онлайне, добавить GPU

Дубли на стыках чанков

Недостаточный overlap/merge

Увеличить overlap до 0.3–0.5 с, dedup по логитам/ts

Плохая пунктуация

Шум, неверный язык

Фильтрация шума, зафиксировать LANG, пост‑проц NORM

«Щелчки» в конце сегментов

Обрезка VAD без паддинга

VAD_PAD_MS ≥ 100–200 мс

Сбои на Interruptible

Нет ретраев/идемпотентности

Очередь с ретраями, idempotent‑ключи, чанк ≤120 с

Падает выравнивание

Нечеткие сегменты

Запуск Align в батче после merge, калибровка порогов

Высокий CPU

Ресемплинг/декодинг на CPU

Библиотеки с SIMD, групповой ресемплинг, закрепление потоков

См. тюнинг: https://cloudcompute.ru/solutions/performance-tuning/https://cloudcompute.ru/solutions/interruptible-patterns/

Как запустить в cloudcompute.ru

  1. Зайдите в Шаблоны запусков: https://cloudcompute.ru/solutions/templates/
    Выберите темплейт ASR Whisper (Real‑time) или ASR Whisper (Batch).
  2. Укажите профиль GPU: 24 ГБ / 48 ГБ / 80 ГБ и включите NVMe‑диск под /nvme.
  3. Смонтируйте каталоги:
    /nvme/models — веса, /nvme/asr-cache — кэш, /mnt/media — вход/выход батчей.
  4. Настройте переменные окружения из примеров docker-compose.yml (модель, язык, пороги VAD, формат вывода).
  5. Для продакшна рекомендованы: автоскейл по U/RTF, отдельные пулы по языкам/моделям, мониторинг/алерты.

Дополнительно:
https://cloudcompute.ru/solutions/triton-inference-server/ — если хотите сервировать через Triton/ONNX/TensorRT.
https://cloudcompute.ru/solutions/gradio-fastapi/ — быстрый UI/эндпойнты.
https://cloudcompute.ru/solutions/containers-ci-cd/ — сборка и выкладка контейнеров.

Чек‑лист перед продом

  • Замерены RTF p50/p95 под целевой корпус (язык/шум/битрейт).
  • Выбран профиль GPU (24/48/80 ГБ) с запасом по U ≥ 0.2.
  • NVMe‑кэш и каталоги корректно примонтированы.
  • Пороговые параметры VAD_THRESHOLD/PAD согласованы с UX.
  • Форматы вывода (JSON/SRT/VTT) валидируются на примерах.
  • Алерты по RTF/GPU‑HBM/дропам на месте.
  • Политики PII и ретеншна внедрены; логи без сырого аудио.
  • Interruptible‑джобы имеют ретраи и идемпотентные ключи.
  • Автоскейл проверен (нагрузочный прогон ≥ 30 мин).
  • Канареечный деплой и откат готовы.