# WhisperX con diarización

WhisperX amplía Whisper de OpenAI con tres mejoras críticas: **marcas de tiempo a nivel de palabra** mediante alineación forzada de fonemas, **diarización de oradores** usando pyannote.audio, y **hasta 70× la velocidad en tiempo real** a través de inferencia por lotes con faster-whisper. Es la herramienta de referencia para canalizaciones de transcripción en producción que necesitan sincronización precisa e identificación de oradores.

**GitHub:** [m-bain/whisperX](https://github.com/m-bain/whisperX) **PyPI:** [whisperx](https://pypi.org/project/whisperx/) **Licencia:** BSD-4-Clause **Artículo:** [arxiv.org/abs/2303.00747](https://arxiv.org/abs/2303.00747)

## Características clave

* **Marcas de tiempo a nivel de palabra** — ±50 ms de precisión mediante alineación forzada con wav2vec2 (vs ±500 ms en Whisper estándar)
* **Diarización de oradores** — identifica quién dijo qué mediante pyannote.audio 3.1
* **Inferencia por lotes** — hasta 70× la velocidad en tiempo real en RTX 4090
* **Prefiltrado VAD** — Silero VAD elimina silencios antes de la transcripción
* **Todos los modelos Whisper** — desde tiny hasta large-v3-turbo
* **Múltiples formatos de salida** — JSON, SRT, VTT, TXT, TSV
* **Detección automática de idioma** — o forzar un idioma específico para un procesamiento más rápido

## Requisitos

| Componente | Mínimo                | Recomendado              |
| ---------- | --------------------- | ------------------------ |
| GPU        | RTX 3060 12 GB        | RTX 4090 24 GB           |
| VRAM       | 4 GB (modelo pequeño) | 10 GB+ (large-v3-turbo)  |
| RAM        | 8 GB                  | 16 GB+                   |
| Disco      | 5 GB                  | 20 GB (caché de modelos) |
| Python     | 3.9+                  | 3.11                     |
| CUDA       | 11.8+                 | 12.1+                    |

**Token de HuggingFace requerido** para diarización — acepta la licencia en [pyannote/speaker-diarization-3.1](https://huggingface.co/pyannote/speaker-diarization-3.1).

**Recomendación de Clore.ai:** RTX 3090 (~~$0.30–1.00/día) para el modelo large-v3-turbo con tamaño de lote 16. RTX 4090 (~~$0.50–2.00/día) para el máximo rendimiento con tamaño de lote 32.

## Instalación

```bash
# Instalar WhisperX
pip install whisperx

# Verificar GPU
python -c "import torch; print(torch.cuda.get_device_name(0))"
```

Si encuentras conflictos de versión de CUDA:

```bash
pip install torch==2.5.1+cu124 torchaudio==2.5.1+cu124 --index-url https://download.pytorch.org/whl/cu124
pip install whisperx
```

## Inicio rápido

```python
import whisperx
import json

device = "cuda"
compute_type = "float16"  # "int8" para menor VRAM
batch_size = 16            # reducir a 4-8 si la VRAM es limitada

# 1. Cargar modelo
model = whisperx.load_model("large-v3-turbo", device, compute_type=compute_type)

# 2. Cargar y transcribir audio
audio = whisperx.load_audio("interview.mp3")
result = model.transcribe(audio, batch_size=batch_size)
print(f"Language: {result['language']}")

# 3. Alinear para marcas de tiempo a nivel de palabra
model_a, metadata = whisperx.load_align_model(
    language_code=result["language"], device=device
)
result = whisperx.align(
    result["segments"], model_a, metadata, audio, device,
    return_char_alignments=False,
)

# 4. Imprimir resultados
for seg in result["segments"]:
    print(f"[{seg['start']:.2f}s → {seg['end']:.2f}s] {seg['text']}")
    for w in seg.get("words", []):
        print(f"  '{w['word']}' @ {w.get('start', 0):.2f}s")

# 5. Guardar
with open("transcript.json", "w") as f:
    json.dump(result, f, indent=2, ensure_ascii=False)
```

## Ejemplos de uso

### Transcripción con diarización de oradores

```python
import whisperx
import gc
import torch

device = "cuda"
HF_TOKEN = "hf_your_token_here"  # desde huggingface.co/settings/tokens

# Paso 1: Transcribir
model = whisperx.load_model("large-v3-turbo", device, compute_type="float16")
audio = whisperx.load_audio("meeting.mp3")
result = model.transcribe(audio, batch_size=16)

# Liberar memoria GPU antes de cargar el modelo de alineación
del model; gc.collect(); torch.cuda.empty_cache()

# Paso 2: Alinear
model_a, metadata = whisperx.load_align_model(
    language_code=result["language"], device=device
)
result = whisperx.align(result["segments"], model_a, metadata, audio, device)
del model_a; gc.collect(); torch.cuda.empty_cache()

# Paso 3: Diarizar
diarize_model = whisperx.DiarizationPipeline(
    use_auth_token=HF_TOKEN, device=device
)
diarize_segments = diarize_model(audio, min_speakers=2, max_speakers=6)

# Paso 4: Asignar oradores a palabras
result = whisperx.assign_word_speakers(diarize_segments, result)

for seg in result["segments"]:
    speaker = seg.get("speaker", "UNKNOWN")
    print(f"[{speaker}] [{seg['start']:.1f}s → {seg['end']:.1f}s] {seg['text']}")
```

### Uso en línea de comandos

```bash
# Transcripción básica
whisperx audio.mp3 --model large-v3-turbo --device cuda

# Forzar idioma (más rápido, omite detección)
whisperx audio.mp3 --model large-v3-turbo --language en --device cuda

# Con diarización de oradores
whisperx audio.mp3 --model large-v3-turbo --diarize --hf_token hf_your_token

# Salida de subtítulos SRT
whisperx audio.mp3 --model large-v3-turbo --output_format srt --output_dir ./subs/

# Modo baja VRAM
whisperx audio.mp3 --model medium --compute_type int8 --batch_size 4 --device cuda

# Procesar por lotes un directorio
for f in /data/audio/*.mp3; do
  whisperx "$f" --model large-v3-turbo --output_dir /data/transcripts/
done
```

### Script de generación SRT

```python
import whisperx

def transcribe_to_srt(audio_path, output_path, model_name="large-v3-turbo"):
    device = "cuda"
    model = whisperx.load_model(model_name, device, compute_type="float16")
    audio = whisperx.load_audio(audio_path)
    result = model.transcribe(audio, batch_size=16)

    model_a, metadata = whisperx.load_align_model(
        language_code=result["language"], device=device
    )
    result = whisperx.align(result["segments"], model_a, metadata, audio, device)

    with open(output_path, "w") as f:
        for i, seg in enumerate(result["segments"], 1):
            start = format_ts(seg["start"])
            end = format_ts(seg["end"])
            f.write(f"{i}\n{start} --> {end}\n{seg['text'].strip()}\n\n")

    print(f"SRT guardado en {output_path}")

def format_ts(seconds):
    h = int(seconds // 3600)
    m = int((seconds % 3600) // 60)
    s = int(seconds % 60)
    ms = int((seconds % 1) * 1000)
    return f"{h:02d}:{m:02d}:{s:02d},{ms:03d}"

transcribe_to_srt("podcast.mp3", "podcast.srt")
```

## Pruebas de rendimiento

| Método           | Modelo             | Audio de 1 h | GPU          | Velocidad aproximada |
| ---------------- | ------------------ | ------------ | ------------ | -------------------- |
| Whisper estándar | large-v3           | \~60 min     | RTX 3090     | 1×                   |
| faster-whisper   | large-v3           | \~5 min      | RTX 3090     | \~12×                |
| **WhisperX**     | **large-v3-turbo** | **\~1 min**  | **RTX 3090** | **\~60×**            |
| **WhisperX**     | **large-v3-turbo** | **\~50 seg** | **RTX 4090** | **\~70×**            |

| Tamaño de lote | Velocidad (RTX 4090) | VRAM  |
| -------------- | -------------------- | ----- |
| 4              | \~30× en tiempo real | 6 GB  |
| 8              | \~45× en tiempo real | 8 GB  |
| 16             | \~60× en tiempo real | 10 GB |
| 32             | \~70× en tiempo real | 14 GB |

## Consejos para usuarios de Clore.ai

* **Liberar VRAM entre pasos** — eliminar modelos y llamar `torch.cuda.empty_cache()` entre transcripción, alineación y diarización
* **Token de HuggingFace** — debes aceptar la licencia del modelo pyannote antes de que la diarización funcione; establece `HF_TOKEN` como una variable de entorno
* **Ajuste del tamaño de lote** — comienza con `batch_size=16`, reduce a 4–8 en tarjetas de 12 GB, aumenta a 32 en tarjetas de 24 GB
* **`int8` cómputo** — usar `compute_type="int8"` para reducir a la mitad el uso de VRAM con una pérdida mínima de calidad
* **Imagen Docker** — `pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime`
* **Caché persistente de modelos** — monta `/root/.cache/huggingface` para evitar volver a descargar modelos en cada reinicio del contenedor

## Solución de problemas

| Problema                            | Solución                                                                                                                                |
| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `CUDA fuera de memoria`             | Reducir `batch_size`, usar `compute_type="int8"`, o usar un modelo más pequeño (medium, small)                                          |
| Diarización devuelve `UNKNOWN`      | Asegúrate de que el token de HuggingFace sea válido y de que aceptaste la licencia de pyannote                                          |
| `No hay módulo llamado 'whisperx'`  | `pip install whisperx` — asegúrate de que no sea un error tipográfico (es `whisperx`, no en `whisper-x`)                                |
| Marcas de tiempo de palabras pobres | Comprueba que `whisperx.align()` se llame después de `transcribe()` — la salida cruda de Whisper carece de precisión a nivel de palabra |
| Detección de idioma incorrecta      | Forzar idioma con `--language en` o `language="en"` en la API de Python                                                                 |
| Procesamiento lento                 | Aumente `batch_size`, usar `large-v3-turbo` en lugar de `large-v3`, asegúrate de que la GPU no esté compartida                          |
