Phase 06 - Lesson 04
Reconocimiento de Voz (ASR) — CTC, RNN-T, Atención
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
El reconocimiento de voz es clasificación de audio en cada instante de tiempo, unida por un modelo de secuencia que conoce el inglés y el silencio. CTC, RNN-T y atención son las tres maneras de hacerlo. Elige una y entiende por qué.
Tipo: Build Lenguajes: Python Requisitos previos: Fase 6 · 02 (Espectrogramas y Mel), Fase 5 · 08 (CNNs y RNNs para Texto), Fase 5 · 10 (Atención) Tiempo: ~45 minutos
El Problema
Tienes un clip de 10 segundos a 16 kHz. Quieres una cadena: "enciende las luces de la cocina". El desafío es estructural: los fotogramas de audio no se alinean uno a uno con los caracteres. La palabra "okay" puede tomar 200 ms o 1200 ms. El silencio puntúa el enunciado. Algunos fonemas son más largos que otros. La cantidad de tokens de salida no se conoce de antemano.
Tres formulaciones resuelven esto:
- CTC (Connectionist Temporal Classification). Emite probabilidades de tokens por fotograma, incluyendo un blank especial. Colapsa repeticiones y blanks al momento de decodificar. No autorregresivo, rápido. Usado por wav2vec 2.0 y MMS.
- RNN-T (Recurrent Neural Network Transducer). Una red conjunta predice el siguiente token dado el fotograma del encoder y los tokens anteriores. Permite streaming. Usado por el ASR on-device de Google y por NVIDIA Parakeet.
- Atención encoder-decoder. El encoder comprime el audio en estados ocultos, el decoder hace cross-attention para generar tokens de forma autorregresiva. Usado por Whisper y SeamlessM4T.
En 2026, el WER SOTA en LibriSpeech test-clean es 1,4% (Parakeet-TDT-1.1B, NVIDIA) y 1,58% (Whisper-Large-v3-turbo). Las diferencias son minúsculas; las diferencias de despliegue son enormes.
El Concepto
Intuición de CTC. Deja que el encoder produzca T distribuciones por fotograma sobre V+1 tokens (V caracteres + blank). Para una cadena objetivo y de longitud U < T, cualquier alineación de fotogramas que colapse a y cuenta. La pérdida de CTC suma sobre todas esas alineaciones. Inferencia: argmax por fotograma, colapsar repeticiones, eliminar blanks.
Ventajas: no autorregresivo, permite streaming, lookahead cero. Desventaja: suposición de independencia condicional — cada predicción de fotograma es independiente de las demás, por lo que no hay modelo de lenguaje interno. Corrígelo con un LM externo mediante beam search o shallow fusion.
Intuición de RNN-T. Agrega una red predictor que incrusta el historial de tokens y un joiner que combina el estado del predictor con el fotograma del encoder en una distribución conjunta sobre V+1 (el +1 es un null / sin emisión). Modela explícitamente la dependencia condicional que CTC ignora. Permite streaming porque cada paso se condiciona únicamente sobre fotogramas pasados y tokens pasados.
Ventajas: streaming + LM interno. Desventaja: el entrenamiento es más complejo y consume más memoria (retícula de pérdida 3D); los kernels de pérdida de RNN-T forman una categoría de biblioteca por sí solos.
Atención encoder-decoder. Encoder (6-32 capas transformer) sobre fotogramas log-mel. El decoder (6-32 capas transformer) hace cross-attention sobre las salidas del encoder para generar tokens de forma autorregresiva. Sin restricción de alineación — la atención puede mirar a cualquier punto del audio. No permite streaming a menos que restrinjas la atención (Whisper-Streaming por chunks, 2024).
Ventajas: mayor calidad en ASR offline, fácil de entrenar con herramientas seq2seq estándar. Desventaja: la latencia autorregresiva es proporcional a la longitud de la salida; no puede hacer streaming sin ingeniería.
WER: el único número
Word Error Rate (Tasa de Error de Palabra) = (S + D + I) / N, donde S=sustituciones, D=eliminaciones, I=inserciones, N=cantidad de palabras de referencia. Equivale a la distancia de edición de Levenshtein a nivel de palabra. Mientras más bajo, mejor. Un WER superior al 20% es generalmente inutilizable; por debajo del 5% es paridad humana para habla leída. Cifras de 2026 en benchmarks estándar:
| Modelo | LibriSpeech test-clean | LibriSpeech test-other | Tamaño |
|---|---|---|---|
| Parakeet-TDT-1.1B | 1.40% | 2.78% | 1.1B params |
| Whisper-Large-v3-turbo | 1.58% | 3.03% | 809M |
| Canary-1B Flash | 1.48% | 2.87% | 1B |
| Seamless M4T v2 | 1.7% | 3.5% | 2.3B |
Todos estos se basan en encoder-decoder o RNN-T. Los sistemas puramente CTC (wav2vec 2.0) rondan el 1,8–2,1% en test-clean.
Constrúyelo
Paso 1: decodificación CTC voraz (greedy)
def ctc_greedy(frame_logits, blank=0, vocab=None):
# frame_logits: list of per-frame probability vectors
preds = [max(range(len(p)), key=lambda i: p[i]) for p in frame_logits]
out = []
prev = -1
for p in preds:
if p != prev and p != blank:
out.append(p)
prev = p
return "".join(vocab[i] for i in out) if vocab else out
Dos reglas: colapsar repeticiones consecutivas, descartar blanks. Ejemplo: a a _ _ a b b _ c → a a b c.
Paso 2: beam search con CTC
def ctc_beam(frame_logits, beam=8, blank=0):
import math
beams = [([], 0.0)] # (tokens, log_prob)
for p in frame_logits:
log_p = [math.log(max(pi, 1e-10)) for pi in p]
candidates = []
for seq, lp in beams:
for t, lpt in enumerate(log_p):
new = seq[:] if t == blank else (seq + [t] if not seq or seq[-1] != t else seq)
candidates.append((new, lp + lpt))
candidates.sort(key=lambda x: -x[1])
beams = candidates[:beam]
return beams[0][0]
En producción se usa beam search con árbol de prefijos y fusión con LM; este es el esqueleto conceptual.
Paso 3: WER
def wer(ref, hyp):
r, h = ref.split(), hyp.split()
dp = [[0] * (len(h) + 1) for _ in range(len(r) + 1)]
for i in range(len(r) + 1):
dp[i][0] = i
for j in range(len(h) + 1):
dp[0][j] = j
for i in range(1, len(r) + 1):
for j in range(1, len(h) + 1):
cost = 0 if r[i - 1] == h[j - 1] else 1
dp[i][j] = min(
dp[i - 1][j] + 1,
dp[i][j - 1] + 1,
dp[i - 1][j - 1] + cost,
)
return dp[len(r)][len(h)] / max(1, len(r))
Paso 4: inferencia con Whisper
import whisper
model = whisper.load_model("large-v3-turbo")
result = model.transcribe("clip.wav")
print(result["text"])
Una sola línea para el ASR general más potente de 2026. Corre en una GPU de 24 GB a ~20× tiempo real.
Paso 5: streaming con Parakeet o wav2vec 2.0
from transformers import pipeline
asr = pipeline("automatic-speech-recognition", model="nvidia/parakeet-tdt-1.1b")
for chunk in streaming_audio():
print(asr(chunk, return_timestamps=True))
El ASR en streaming necesita atención del encoder por chunks y estado de carryover; usa una biblioteca que lo soporte (NeMo para Parakeet, pipeline de transformers con chunk_length_s).
Úsalo
El stack de 2026:
| Situación | Elección |
|---|---|
| Inglés, offline, máxima calidad | Whisper-large-v3-turbo |
| Multilingüe, robusto | SeamlessM4T v2 |
| Streaming, baja latencia | Parakeet-TDT-1.1B o Riva |
| Edge, móvil, latencia <500 ms | Whisper-Tiny cuantizado o Moonshine (2024) |
| Formato largo | Whisper con chunking basado en VAD (WhisperX) |
| Dominio específico (médico, legal) | Fine-tune de wav2vec 2.0 + fusión con LM de dominio |
Trampas que aún se despliegan en 2026
- Sin VAD. Correr Whisper sobre silencio produce alucinaciones ("Thanks for watching!"). Siempre haz gate con VAD.
- WER por carácter vs palabra vs subword. Reporta el WER a nivel de palabra después de la normalización (minúsculas, puntuación eliminada).
- Deriva de identificación de idioma. La LID automática de Whisper enruta erróneamente clips ruidosos a japonés o galés; fuerza
language="en"cuando lo sepas. - Clips largos sin chunking. Whisper tiene una ventana de 30 segundos. Usa
chunk_length_s=30, stride=5para cualquier cosa más larga.
Entrégalo
Guárdalo como outputs/skill-asr-picker.md. Elige el modelo, la estrategia de decodificación, el chunking y la fusión con LM para un objetivo de despliegue dado.
Ejercicios
- Fácil. Corre
code/main.py. Decodifica de forma voraz una salida CTC hecha a mano y calcula el WER contra una referencia. - Medio. Implementa correctamente el beam search con árbol de prefijos del Paso 2 (considera la regla de fusión del blank). Compáralo con la versión voraz en un conjunto de datos sintético de 10 ejemplos.
- Difícil. Usa
whisper-large-v3-turboen LibriSpeech test-clean. Calcula el WER en los primeros 100 enunciados. Compara con las cifras publicadas.
Términos Clave
| Término | Lo que dice la gente | Lo que realmente significa |
|---|---|---|
| CTC | La pérdida del token blank | Marginal sobre todas las alineaciones fotograma-token; no AR. |
| RNN-T | La pérdida de streaming | CTC + predictor del siguiente token; maneja el orden de las palabras. |
| Atención enc-dec | Estilo Whisper | Encoder + decoder con cross-attention; mejor calidad offline. |
| WER | El número que reportas | (S+D+I)/N a nivel de palabra. |
| Blank | El vacío | Token especial en CTC que señala "ninguna emisión en este fotograma". |
| Fusión con LM | Modelo de lenguaje externo | Agrega log-probs ponderados del LM durante el beam search. |
| VAD | El gate de silencio | Detector de actividad de voz; recorta fragmentos sin habla. |
Lectura Adicional
- Graves et al. (2006). Connectionist Temporal Classification — el paper de CTC.
- Graves (2012). Sequence Transduction with RNNs — el paper de RNN-T.
- Radford et al. / OpenAI (2022). Whisper: Robust Speech Recognition via Large-Scale Weak Supervision — el paper canónico de 2022; extensión v3-turbo en 2024.
- NVIDIA NeMo — Parakeet-TDT card — líder del Open ASR Leaderboard en 2026.
- Hugging Face — Open ASR Leaderboard — benchmark en vivo con más de 25 modelos.