Skip to content

English · Español

Lab 02 — Walkthrough de Inferencia con Whisper

Objetivo: cargar whisper-tiny.en de OpenAI (39M params) vía transformers de HuggingFace, alimentarlo con 24 clips de audio pre-grabados de formas verbales en inglés (uno por muestra (verb, tense, person) de §A13), e inspeccionar (a) la secuencia de tokens decodificada, (b) los logits por-token, © los tokens de timestamp, (d) el patrón de cross-attention encoder-decoder. Sin entrenamiento. Solo inferencia + introspección.

🇪🇸 Esta lab es inspección, no entrenamiento. Cargamos Whisper-tiny pre-entrenado, le damos clips de audio de verbos en distintas conjugaciones, y abrimos la caja: ¿qué tokens emite? ¿qué timestamps marca? ¿a qué frames de audio presta atención cuando genera cada token de texto? Es el punto donde la teoría del frontend log-mel y de cross-attention se vuelve concreta.

Tiempo estimado: 3–4 horas.

Prerequisitos: - docs/extension-track/X2-multimodal/theory/02-audio-models.md leído. - huggingface_hub, transformers, librosa (o soundfile) instalados en tu env uv. - Los 24 clips de audio commiteados en docs/extension-track/X2-multimodal/lab/data/audio/. Ver "Datos" debajo para cómo obtener o generar.


Por qué este lab usa transformers (una excepción única)

CLAUDE.md §0.4 dice: nada de librería transformers antes de la Fase 24.

X2 es un extension track, no parte del ritual core de 40-fases. Estamos post-Fase-24 en espíritu. Además:

  • Este lab es solo-inferencia. No hay mecánica de entrenamiento que revelar.
  • Re-implementar el frontend log-mel + conv stem + encoder de 4 capas + decoder de 4 capas + tokenizer BPE de Whisper desde cero es un proyecto de ~3 semanas que añade cero insight conceptual más allá de lo que theory/02-audio-models.md ya deriva.
  • El objetivo del lab es introspección de un checkpoint pre-entrenado, lo que requiere cargar ese checkpoint, lo que requiere transformers (o replicar su código de carga de checkpoints, que es el mismo compromiso).

La excepción es local a este lab. El lab 00 y el lab 01 se quedan en NumPy solo.


Datos

Necesitamos 24 clips de audio pre-grabados. Uno por muestra (verb, tense, person) para 8 verbos elegidos de §A13 (un subset; los 300 completos serían excesivos).

Subset de verbos (24 clips = 8 verbos × 3 formas cada uno, mezclando tiempos/personas)

Un conjunto representativo cubriendo los 5 tiempos y las 3 personas:

Clip # Frase Verbo Tiempo Persona
01 "I work" work present_simple 1sg
02 "you worked" work past_simple 2sg
03 "he works" work present_simple 3sg
04 "I will play" play future 1sg
05 "you played" play past_simple 2sg
06 "she has played" play past_participle 3sg
07 "to walk" walk infinitive -
08 "he walks" walk present_simple 3sg
09 "I talked" talk past_simple 1sg
10 "you will talk" talk future 2sg
11 "she listens" listen present_simple 3sg
12 "I have listened" listen past_participle 1sg
13 "you watch" watch present_simple 2sg
14 "he watched" watch past_simple 3sg
15 "I am" be present_simple 1sg
16 "you were" be past_simple 2sg
17 "she has been" be past_participle 3sg
18 "I will be" be future 1sg
19 "I go" go present_simple 1sg
20 "you went" go past_simple 2sg
21 "he has gone" go past_participle 3sg
22 "to eat" eat infinitive -
23 "I ate" eat past_simple 1sg
24 "she will eat" eat future 3sg

Cómo obtener los clips

Dos opciones:

Opción A (preferida, determinista): generar desde text-to-speech. - Usa pyttsx3 (offline, sin red) o espeak-ng desde CLI. - Una voz, pitch/rate fijos, WAV mono 16 kHz. - Guarda como data/audio/clip_<NN>.wav donde NN es el número de clip. - Commitea un script data/audio/generate.py que produce los 24 clips deterministamente desde la tabla de arriba.

Opción B (más realista): grábate tú. - Usa arecord o sox o cualquier DAW. 16 kHz, mono, ~2-3 segundos por clip. - Commitea los archivos WAV. Anota en el README que son grabados por humano y no reproducibles bit-a-bit.

Cualquiera sirve para el lab. La opción A se recomienda por reproducibilidad — el comportamiento de Whisper sobre audio TTS es su propia pregunta interesante (transcribe TTS bien; así es como en parte fue entrenado).


Lo que produces

Un directorio experiments/X2-multimodal/lab-02-whisper-inference/ que contiene:

  • BLUEPRINT.md
  • transcribe.py — carga Whisper-tiny.en, transcribe los 24 clips, guarda salidas.
  • inspect_tokens.py — para cada clip, vuelca los tokens BPE + sus probabilidades + sus timestamps.
  • inspect_cross_attention.py — para cada clip, extrae el patrón de cross-attention del decoder.
  • plot_cross_attention.py — visualiza la cross-attention como un heatmap (pasos del decoder × posiciones del encoder).
  • transcriptions.json{clip_id: {expected, predicted, wer, tokens, token_probs, timestamps}}.
  • manifest.json.
  • README.md.
  • cross_attention_<NN>.png — un heatmap por clip.

Recap rápido de arquitectura (lee theory/02 primero)

audio waveform (T_audio at 16 kHz)
  ↓ log-mel (80 mel × 3000 frames; padded for < 30s)
  ↓ Conv1D × 2 (stride 1 then stride 2) → (1500 frames, d_model=384)
  ↓ + sinusoidal positional embedding
  ↓ encoder: 4 transformer blocks (no causal mask)
  ↓ encoder_output: (1500, 384)
  ↓ cross-attention key/value
[<|startoftranscript|> <|en|> <|transcribe|>] (or +timestamp tokens)
  ↓ decoder: 4 transformer blocks (causal self-attn + cross-attn to encoder)
  ↓ next-token logits (vocab_size = 51865)
  ↓ greedy / beam decode
  ↓ "I work" (with optional timestamp tokens interspersed)

Vas a hurgar en: - La secuencia de tokens de salida (salida del decoder). - La distribución de probabilidad en cada paso decodificado (introspección de la incertidumbre del decoder). - Los pesos de cross-attention en cada capa del decoder (a qué frames de audio miró el decoder cuando generó cada token de texto).


TODOs

Bloque A — BLUEPRINT

  • Sketch de cómo vas a cargar Whisper, obtener los hidden states del encoder, obtener la cross-attention del decoder, plotear. Referencia la API de transformers: WhisperProcessor, WhisperForConditionalGeneration, model.generate(..., output_attentions=True, return_dict_in_generate=True).
  • Lista la config real del modelo para verificar (vocab_size, d_model, n_layers).

Bloque B — transcribir

En transcribe.py:

  • Carga openai/whisper-tiny.en y su WhisperProcessor.
  • Para cada WAV en data/audio/:
  • Carga vía librosa.load(path, sr=16000).
  • Pasa por processor para obtener input_features (el log-mel).
  • Verifica input_features.shape == (1, 80, 3000).
  • Llama a model.generate(input_features, return_timestamps=True, return_dict_in_generate=True, output_scores=True, output_attentions=True).
  • Decodifica los IDs generados con processor.batch_decode(..., skip_special_tokens=False).
  • Guarda la transcripción esperada vs predicha por clip.
  • Computa word error rate (WER) por clip. (Para 24 clips simples, el WER debería ser cercano a 0% — Whisper es muy bueno con habla limpia. Cualquier error es por sí mismo un hallazgo interesante.)

Bloque C — introspección de tokens

En inspect_tokens.py:

  • Para cada clip, recorre la secuencia de tokens generada paso a paso.
  • En cada paso, loguea:
  • El ID del token y su texto decodificado.
  • Las top-5 alternativas + sus probabilidades softmax (desde outputs.scores).
  • Si este token es un token de timestamp (ID de token en el rango <|N.NN|>).
  • Hallazgos a buscar:
  • ¿Los tokens de timestamp están aproximadamente donde ocurrió el habla? (Para "I work" el primer token de texto debería seguir a un <|0.00|> y terminar con un <|1.5|> o así.)
  • En pasado vs presente: ¿cae la confianza de Whisper en el token de forma verbal (p. ej. "worked" vs "works")? ¿Son las alternativas semánticamente similares?
  • En el prefix de tarea <|en|> <|transcribe|>: ¿son estos de muy alta confianza? (deberían serlo — el modelo ha sido entrenado con estos tokens millones de veces.)

Bloque D — extracción de cross-attention

En inspect_cross_attention.py:

  • Para cada clip, extrae la cross-attention del decoder desde outputs.cross_attentions.
  • Forma por paso de generación: (n_layers, batch, n_heads, query_len, key_len) = (4, 1, 6, 1, 1500).
  • Promedia sobre heads (o escoge un único head — el head 4 a menudo es el más interpretable para Whisper-tiny; este es un hallazgo empírico conocido).
  • Ahora tienes, para cada token de texto decodificado, una distribución sobre las 1500 posiciones del encoder.

Bloque E — plotear

En plot_cross_attention.py:

  • Para cada clip, plotea un heatmap: filas = tokens de texto decodificados (p. ej. "I", " work"), cols = posiciones del encoder 0..1499. Color = peso de cross-attention.
  • Anota las etiquetas de fila con el texto decodificado.
  • El patrón esperado (comportamiento sello de Whisper): alineamiento monotónico, cuasi-diagonal. La cross-attention "escanea" de izquierda a derecha a través del audio conforme se genera texto. Esta es la base de la predicción de timestamp de Whisper.
  • Guarda como cross_attention_<NN>.png.

Bloque F — análisis

En README.md:

  • ¿Se transcribieron correctamente los 24 clips (WER ≈ 0)?
  • ¿Qué clips tuvieron la confianza más baja del decoder en el token verbal? ¿Cuáles eran las top-5 alternativas en ese paso?
  • ¿Fueron precisos los timestamps? Compara contra la duración real del clip.
  • ¿Muestra la cross-attention alineamiento monotónico? Escoge el ejemplo más limpio y el más sucio para discutir.
  • Conecta con el tutor de gramática: si quisieras marcar errores de pronunciación (p. ej. el usuario dice "he work" en vez de "he works"), ¿cómo usarías las probabilidades por-token de Whisper? (Pista: mira la probabilidad de works vs work en la posición del token verbal cuando el audio es "he work".)

Bloque G — objetivos stretch

  • Test adversarial. Graba 5 clips con errores de gramática deliberados ("he work", "you was", "I has"). ¿Whisper los transcribe como se dijeron (preservando el error), o los "corrige" a inglés gramatical? Esta es una propiedad conocida de los modelos grandes de habla: a menudo "arreglan" pequeños errores de gramática silenciosamente. Reporta lo que encuentres.
  • Especialización de heads de cross-attention. Para un clip, plotea cada uno de los 6 heads de atención por separado. ¿Hay heads especializados (p. ej. uno para detección de frontera, uno para duración de vocal)? Esto es exploratorio y los hallazgos varían.
  • Comparación greedy vs beam decode. Genera el mismo clip con num_beams=1 vs num_beams=5. ¿Son las salidas idénticas, o beam search encuentra una transcripción diferente?

Criterios de aceptación

  1. BLUEPRINT.md aprobado.
  2. Los 24 clips transcritos; WER reportado por clip.
  3. Probabilidades por-token volcadas para al menos 5 clips representativos.
  4. Heatmaps de cross-attention commiteados para los 24 clips.
  5. La discusión en README.md incluye el hallazgo de alineamiento monotónico y al menos una observación sorprendente o inesperada.
  6. manifest.json incluye hash del checkpoint de Whisper, versión de transformers, versión de librosa, seed (si es relevante para cualquier decode estocástico), wall-clock de la corrida completa de inferencia.

Lo que este lab intencionadamente NO es

  • No es entrenamiento de Whisper. No estamos re-entrenando. El modelo se usa tal cual.
  • No es una comparación de Whisper con otro ASR. Nada de wav2vec, nada de Conformer-RNN-T. Un modelo, inspección profunda.
  • No es streaming. Whisper es batch. Los 24 clips son < 30 s.
  • No es multilingüe. whisper-tiny.en es solo-inglés. El whisper-tiny multilingüe (74 M, sin .en) tiene un vocabulario y comportamiento diferentes.

Lo que habrás aprendido

  • El frontend log-mel es concreto: un clip de 30 s → matriz log-mel (80, 3000). El processor hace esto por ti, pero ahora sabes qué hay en input_features.
  • La cross-attention es interpretable en Whisper. El patrón "mira el audio para el siguiente token de texto" del decoder es visible y aproximadamente monotónico. Esta es la base de la lógica de timestamps de Whisper.
  • Un modelo de 39 M params es suficiente para habla limpia en un idioma conocido. La mayoría de tareas de "transcripción" no necesitan escala GPT; necesitan 39 M entrenados sobre suficiente audio.
  • El modelo tiene priors de language-model integrados. A veces "arreglará" errores de gramática silenciosamente. Esto es genial para ASR-como-producto, malo para tutor-de-gramática-como-producto. El tutor de gramática necesita un modelo que transcriba exactamente lo que se dijo, no una versión suavizada.

Ese último punto es el hallazgo relevante-para-entrevista: cuando elijas un ASR para un tutor de gramática, específicamente quieres un modelo con menos prior de language-model que Whisper — quizá wav2vec2 fine-tuneado con pérdida CTC, que no tiene el bias autoregresivo de language-model. El modelo correcto depende de si quieres una transcripción fluida o una literal.


Cross-references

  • theory/02-audio-models.md — la derivación log-mel y la arquitectura de Whisper.
  • theory/03-fusion-strategies.md §"unified-token" — nota que Whisper no es unified-token; es un encoder de audio dedicado + decoder de texto. La capacidad de audio de GPT-4o es unified-token.
  • docs/phase-15-attention/theory/03-multi-head.md — atención multi-head. La cross-attention de Whisper es la variante "el encoder son keys/values, el decoder son queries" que discutimos ahí.
  • HIRING_PATH.md — línea "audio gap" — cerrado al completar este lab + la teoría.