Secuencia a Secuencia (Seq2Seq)

S2: Traducción Automática Neuronal (NMT)

Prof. Francisco Suárez

Universidad Católica Boliviana

2026-03-31

Agenda de Hoy

  1. 🌍 Breve historia de la Traducción Automática
  2. 🧠 NMT como aplicación de Seq2Seq
  3. 📝 Preprocesamiento para NMT: tokenización y vocabularios
  4. 🔬 Experimento: un traductor Español → Inglés
  5. 📏 Evaluación: BLEU score
  6. ⚡ Trucos prácticos de los sistemas NMT reales

Objetivo

Aplicar la arquitectura Seq2Seq para construir un sistema de traducción automática neuronal, entender su pipeline completo y aprender a evaluar con BLEU.

Prerequisitos: Codificador-Decodificador, Teacher Forcing (S1)

Historia de la Traducción Automática 🌍

Del Rule-Based al Neural MT

Hito clave

En 2016, Google reemplazó su sistema estadístico por Google Neural Machine Translation (GNMT) — un sistema Seq2Seq con atención de 8 capas. La calidad mejoró más en un solo salto que en los 10 años previos.

SMT vs. NMT

Statistical MT (SMT)

  • Pipeline complejo: alineación → modelo de traducción → modelo de lenguaje → decodificación
  • Componentes entrenados por separado
  • Traduce frase por frase (phrase-based)
  • Requiere ingeniería de features manual
  • Difícil capturar dependencias largas

Neural MT (NMT)

  • Modelo end-to-end con un solo objetivo
  • Entrenamiento conjunto de todos los parámetros
  • Contexto de la oración completa
  • Representaciones aprendidas automáticamente
  • Mejor con dependencias a distancia

¿Por qué ganó NMT?

NMT produce traducciones más fluidas y coherentes porque modela la oración completa como un todo, no como fragmentos pegados.

NMT como Aplicación de Seq2Seq 🧠

Arquitectura NMT Básica

Componentes clave

  1. Vocabulario fuente \(V_{\text{src}}\) (español)
  2. Vocabulario objetivo \(V_{\text{tgt}}\) (inglés)
  3. Embeddings separados por idioma
  4. Encoder sobre idioma fuente
  5. Decoder sobre idioma objetivo

Truco de Sutskever (2014)

Invertir la secuencia de entrada mejora significativamente la traducción. ¿Por qué? Las primeras palabras del decoder están más cerca del inicio de la secuencia fuente invertida.

¿Qué necesitamos para entrenar un NMT?

Componente Descripción Ejemplo
Corpus paralelo Pares de oraciones alineadas “el gato duerme” ↔︎ “the cat sleeps”
Tokenizador fuente Segmentar texto en idioma fuente ["el", "gato", "duerme"]
Tokenizador objetivo Segmentar texto en idioma objetivo ["the", "cat", "sleeps"]
Vocabulario fuente Mapeo token → índice (español) {"el": 5, "gato": 12, ...}
Vocabulario objetivo Mapeo token → índice (inglés) {"the": 5, "cat": 8, ...}
Tokens especiales <PAD>, <SOS>, <EOS>, <UNK> Señales de control

Desafío: vocabulario abierto

En la vida real, aparecen palabras nunca vistas (<UNK>). Soluciones: tokenización por subpalabras (BPE, WordPiece, SentencePiece) → la veremos en detalle con Transformers.

Preprocesamiento para NMT 📝

Construyendo el Pipeline de Datos

Code
import torch
import torch.nn as nn
import random
import re

# --- Corpus paralelo español → inglés (simplificado) ---
raw_pairs = [
    ("el gato duerme", "the cat sleeps"),
    ("el perro corre", "the dog runs"),
    ("el pájaro vuela", "the bird flies"),
    ("el gato come", "the cat eats"),
    ("el perro duerme", "the dog sleeps"),
    ("un gato grande corre", "a big cat runs"),
    ("un perro pequeño duerme", "a small dog sleeps"),
    ("el pájaro pequeño canta", "the small bird sings"),
    ("un gato pequeño come", "a small cat eats"),
    ("el perro grande corre rápido", "the big dog runs fast"),
    ("el gato negro duerme mucho", "the black cat sleeps a lot"),
    ("un pájaro bonito vuela alto", "a beautiful bird flies high"),
    ("el perro blanco come mucho", "the white dog eats a lot"),
    ("un gato negro corre rápido", "a black cat runs fast"),
    ("el pájaro grande vuela lejos", "the big bird flies far"),
    ("un perro bonito duerme aquí", "a beautiful dog sleeps here"),
    ("el gato blanco come pescado", "the white cat eats fish"),
    ("un pájaro negro canta fuerte", "a black bird sings loud"),
    ("el perro negro corre lejos", "the black dog runs far"),
    ("un gato bonito duerme mucho", "a beautiful cat sleeps a lot"),
    ("el gato duerme aquí", "the cat sleeps here"),
    ("un perro come pescado", "a dog eats fish"),
    ("el pájaro canta fuerte", "the bird sings loud"),
    ("un gato corre rápido", "a cat runs fast"),
    ("el perro grande duerme mucho", "the big dog sleeps a lot"),
    ("un pájaro blanco vuela alto", "a white bird flies high"),
    ("el gato grande come mucho", "the big cat eats a lot"),
    ("un perro negro duerme aquí", "a black dog sleeps here"),
    ("el pájaro bonito canta fuerte", "the beautiful bird sings loud"),
    ("un gato blanco corre lejos", "a white cat runs far"),
]

print(f"Total pares: {len(raw_pairs)}")
print(f"Ejemplo: '{raw_pairs[0][0]}' → '{raw_pairs[0][1]}'")
Total pares: 30
Ejemplo: 'el gato duerme' → 'the cat sleeps'

Vocabularios y Codificación

Code
# Tokens especiales
PAD, SOS, EOS, UNK = 0, 1, 2, 3
SPECIAL_TOKENS = {"<PAD>": PAD, "<SOS>": SOS, "<EOS>": EOS, "<UNK>": UNK}

class Vocabulary:
    def __init__(self):
        self.token2idx = dict(SPECIAL_TOKENS)
        self.idx2token = {v: k for k, v in SPECIAL_TOKENS.items()}
        self.n_tokens = len(SPECIAL_TOKENS)
    
    def add_sentence(self, sentence):
        for token in sentence.lower().split():
            if token not in self.token2idx:
                self.token2idx[token] = self.n_tokens
                self.idx2token[self.n_tokens] = token
                self.n_tokens += 1
    
    def encode(self, sentence, add_eos=True):
        indices = [self.token2idx.get(t, UNK) for t in sentence.lower().split()]
        if add_eos:
            indices.append(EOS)
        return indices
    
    def decode(self, indices):
        return " ".join(self.idx2token.get(i, "<UNK>") for i in indices 
                       if i not in (PAD, SOS, EOS))

# Construir vocabularios separados
src_vocab = Vocabulary()
tgt_vocab = Vocabulary()

for src, tgt in raw_pairs:
    src_vocab.add_sentence(src)
    tgt_vocab.add_sentence(tgt)

print(f"Vocabulario español: {src_vocab.n_tokens} tokens")
print(f"Vocabulario inglés:  {tgt_vocab.n_tokens} tokens")
print(f"\nEspañol: {list(src_vocab.token2idx.items())[:10]}...")
print(f"Inglés:  {list(tgt_vocab.token2idx.items())[:10]}...")
Vocabulario español: 26 tokens
Vocabulario inglés:  26 tokens

Español: [('<PAD>', 0), ('<SOS>', 1), ('<EOS>', 2), ('<UNK>', 3), ('el', 4), ('gato', 5), ('duerme', 6), ('perro', 7), ('corre', 8), ('pájaro', 9)]...
Inglés:  [('<PAD>', 0), ('<SOS>', 1), ('<EOS>', 2), ('<UNK>', 3), ('the', 4), ('cat', 5), ('sleeps', 6), ('dog', 7), ('runs', 8), ('bird', 9)]...

Preparación de los Datos

Code
def prepare_batch(pairs, src_vocab, tgt_vocab):
    """Codifica pares y crea tensores con padding"""
    src_encoded = [src_vocab.encode(s) for s, _ in pairs]
    tgt_encoded = [tgt_vocab.encode(t) for _, t in pairs]
    
    # Padding
    max_src = max(len(s) for s in src_encoded)
    max_tgt = max(len(t) for t in tgt_encoded)
    
    src_padded = [s + [PAD] * (max_src - len(s)) for s in src_encoded]
    tgt_padded = [t + [PAD] * (max_tgt - len(t)) for t in tgt_encoded]
    
    return torch.tensor(src_padded), torch.tensor(tgt_padded)

# Dividir datos
random.seed(42)
shuffled = list(raw_pairs)
random.shuffle(shuffled)
train_pairs = shuffled[:24]
test_pairs = shuffled[24:]

train_src, train_tgt = prepare_batch(train_pairs, src_vocab, tgt_vocab)
test_src, test_tgt = prepare_batch(test_pairs, src_vocab, tgt_vocab)

print(f"Train: {train_src.shape[0]} pares, src_len={train_src.shape[1]}, tgt_len={train_tgt.shape[1]}")
print(f"Test:  {test_src.shape[0]} pares")
print(f"\nEjemplo codificado:")
print(f"  src: {train_src[0].tolist()} → '{src_vocab.decode(train_src[0].tolist())}'")
print(f"  tgt: {train_tgt[0].tolist()} → '{tgt_vocab.decode(train_tgt[0].tolist())}'")
Train: 24 pares, src_len=6, tgt_len=7
Test:  6 pares

Ejemplo codificado:
  src: [12, 5, 19, 6, 18, 2] → 'un gato bonito duerme mucho'
  tgt: [12, 19, 5, 6, 12, 18, 2] → 'a beautiful cat sleeps a lot'

Experimento: Traductor ES → EN 🔬

Modelo NMT Seq2Seq

Code
import torch.optim as optim

class Encoder(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim, n_layers=1):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=PAD)
        self.rnn = nn.GRU(embed_dim, hidden_dim, num_layers=n_layers, batch_first=True)
    
    def forward(self, src):
        embedded = self.embedding(src)
        outputs, hidden = self.rnn(embedded)
        return hidden

class Decoder(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim, n_layers=1):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=PAD)
        self.rnn = nn.GRU(embed_dim, hidden_dim, num_layers=n_layers, batch_first=True)
        self.fc_out = nn.Linear(hidden_dim, vocab_size)
    
    def forward(self, input_token, hidden):
        embedded = self.embedding(input_token)
        output, hidden = self.rnn(embedded, hidden)
        prediction = self.fc_out(output.squeeze(1))
        return prediction, hidden

class NMTModel(nn.Module):
    def __init__(self, encoder, decoder, sos_idx=SOS):
        super().__init__()
        self.encoder = encoder
        self.decoder = decoder
        self.sos_idx = sos_idx
    
    def forward(self, src, trg_len, trg=None, teacher_forcing_ratio=0.5):
        batch_size = src.size(0)
        vocab_size = self.decoder.fc_out.out_features
        
        hidden = self.encoder(src)
        outputs = torch.zeros(batch_size, trg_len, vocab_size)
        input_token = torch.full((batch_size, 1), self.sos_idx, dtype=torch.long)
        
        for t in range(trg_len):
            prediction, hidden = self.decoder(input_token, hidden)
            outputs[:, t] = prediction
            if trg is not None and random.random() < teacher_forcing_ratio:
                input_token = trg[:, t:t+1]
            else:
                input_token = prediction.argmax(1, keepdim=True)
        
        return outputs

# Hiperparámetros
EMBED_DIM = 64
HIDDEN_DIM = 128

encoder = Encoder(src_vocab.n_tokens, EMBED_DIM, HIDDEN_DIM)
decoder = Decoder(tgt_vocab.n_tokens, EMBED_DIM, HIDDEN_DIM)
model = NMTModel(encoder, decoder)

total_params = sum(p.numel() for p in model.parameters())
print(f"Parámetros totales: {total_params:,}")
Parámetros totales: 155,674

Entrenamiento del Traductor

Code
optimizer = optim.Adam(model.parameters(), lr=0.005)
criterion = nn.CrossEntropyLoss(ignore_index=PAD)

# Aumentar datos repitiendo el corpus (simular más datos)
N_EPOCHS = 200
losses = []

torch.manual_seed(42)
random.seed(42)

for epoch in range(N_EPOCHS):
    model.train()
    
    # Teacher forcing decrece con el tiempo (scheduled sampling)
    tf_ratio = max(0.1, 1.0 - epoch / N_EPOCHS)
    
    output = model(train_src, train_tgt.size(1), train_tgt, teacher_forcing_ratio=tf_ratio)
    loss = criterion(output.reshape(-1, tgt_vocab.n_tokens), train_tgt.reshape(-1))
    
    optimizer.zero_grad()
    loss.backward()
    torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
    optimizer.step()
    
    losses.append(loss.item())
    if (epoch + 1) % 50 == 0:
        print(f"Época {epoch+1:3d}/{N_EPOCHS} | Loss: {loss.item():.4f} | TF ratio: {tf_ratio:.2f}")
Época  50/200 | Loss: 0.0105 | TF ratio: 0.76
Época 100/200 | Loss: 0.0022 | TF ratio: 0.51
Época 150/200 | Loss: 0.0013 | TF ratio: 0.26
Época 200/200 | Loss: 0.0009 | TF ratio: 0.10

Curva de Aprendizaje

Probando el Traductor

Code
def translate(model, sentence, src_vocab, tgt_vocab, max_len=15):
    """Traduce una oración usando greedy decoding"""
    model.eval()
    with torch.no_grad():
        src = torch.tensor([src_vocab.encode(sentence)])
        hidden = model.encoder(src)
        
        input_token = torch.tensor([[SOS]])
        translated = []
        
        for _ in range(max_len):
            pred, hidden = model.decoder(input_token, hidden)
            token_idx = pred.argmax(1).item()
            if token_idx == EOS:
                break
            translated.append(tgt_vocab.idx2token.get(token_idx, "<UNK>"))
            input_token = torch.tensor([[token_idx]])
    
    return " ".join(translated)

# Traducir los datos de prueba
print("=" * 65)
print(f"{'Español':>30s}{'Traducción':<30s}")
print("=" * 65)

for src_sent, tgt_sent in test_pairs:
    translation = translate(model, src_sent, src_vocab, tgt_vocab)
    match = "✅" if translation == tgt_sent else "❌"
    print(f"{src_sent:>30s}{translation:<25s} {match}")

print("\n--- Oraciones del conjunto de entrenamiento ---")
for src_sent, tgt_sent in train_pairs[:6]:
    translation = translate(model, src_sent, src_vocab, tgt_vocab)
    match = "✅" if translation == tgt_sent else "❌"
    print(f"{src_sent:>30s}{translation:<25s} {match}")
=================================================================
                       Español  →  Traducción                    
=================================================================
       el pájaro pequeño canta  →  the beautiful bird sings loud ❌
          un gato pequeño come  →  a white cat runs far      ❌
          un gato corre rápido  →  a black cat runs fast     ❌
                el gato duerme  →  the black cat sleeps a lot ❌
                  el gato come  →  the white cat eats fish   ❌
           el gato duerme aquí  →  the beautiful dog sleeps a lot ❌

--- Oraciones del conjunto de entrenamiento ---
   un gato bonito duerme mucho  →  a beautiful cat sleeps a lot ✅
  el pájaro grande vuela lejos  →  the big bird flies far    ✅
    el gato negro duerme mucho  →  the black cat sleeps a lot ✅
     el gato grande come mucho  →  the big cat eats a lot    ✅
        el pájaro canta fuerte  →  the beautiful bird sings loud ❌
       un perro pequeño duerme  →  a black dog sleeps here   ❌

Evaluación: BLEU Score 📏

¿Cómo evaluamos traducciones?

El problema de la evaluación

  • No hay una única traducción correcta
  • Evaluación humana es costosa y lenta
  • Necesitamos métricas automáticas

BLEU (Bilingual Evaluation Understudy)

Papineni et al. (2002) — la métrica estándar durante 20+ años:

Mide cuántos n-gramas de la traducción candidata aparecen en la referencia.

Idea intuitiva

Si mi traducción comparte muchas frases (1-gramas, 2-gramas, 3-gramas, 4-gramas) con la referencia humana, probablemente es buena.

Ejemplo:

  • Referencia: “the cat sleeps on the mat”
  • Candidato A: “the cat sleeps on the mat” → BLEU alto
  • Candidato B: “the the the the the the” → BLEU bajo (con penalización)

Cálculo de BLEU Paso a Paso

\[\text{BLEU} = \text{BP} \cdot \exp\left(\sum_{n=1}^{N} w_n \log p_n\right)\]

1. Precisión de n-gramas (\(p_n\))

\[p_n = \frac{\text{n-gramas comunes (con clipping)}}{\text{n-gramas en candidato}}\]

Clipping: cada n-grama de la referencia se puede contar máximo tantas veces como aparece en la referencia.

2. Penalización por brevedad (BP)

\[\text{BP} = \begin{cases} 1 & \text{si } c > r \\ e^{1-r/c} & \text{si } c \leq r \end{cases}\]

donde \(c\) = longitud del candidato, \(r\) = longitud de la referencia.

3. Pesos (\(w_n\))

  • Típicamente uniformes: \(w_n = 1/N\)
  • \(N = 4\) (BLEU-4 es el estándar)

Rango

  • BLEU ∈ [0, 1] (o [0, 100] como porcentaje)
  • BLEU > 0.30 = traducción razonable
  • BLEU > 0.50 = traducción buena
  • BLEU = 1.0 = idéntica a la referencia

Implementación de BLEU

Code
from collections import Counter
import math

def compute_ngrams(tokens, n):
    """Extrae n-gramas de una lista de tokens"""
    return [tuple(tokens[i:i+n]) for i in range(len(tokens)-n+1)]

def bleu_score(candidate, reference, max_n=4):
    """Calcula BLEU score entre candidato y referencia (strings)"""
    cand_tokens = candidate.lower().split()
    ref_tokens = reference.lower().split()
    
    c = len(cand_tokens)
    r = len(ref_tokens)
    
    # Penalización por brevedad
    if c == 0:
        return 0.0
    bp = math.exp(1 - r/c) if c <= r else 1.0
    
    # Precisión de n-gramas con clipping
    log_precisions = []
    for n in range(1, max_n + 1):
        cand_ngrams = compute_ngrams(cand_tokens, n)
        ref_ngrams = compute_ngrams(ref_tokens, n)
        
        if not cand_ngrams:
            log_precisions.append(float('-inf'))
            continue
        
        ref_counts = Counter(ref_ngrams)
        clipped = sum(min(count, ref_counts[ng]) 
                      for ng, count in Counter(cand_ngrams).items())
        precision = clipped / len(cand_ngrams)
        
        log_precisions.append(math.log(precision) if precision > 0 else float('-inf'))
    
    # Promedio geométrico ponderado
    avg_log = sum(lp / max_n for lp in log_precisions)
    
    return bp * math.exp(avg_log) if avg_log > float('-inf') else 0.0

BLEU en Acción

Code
# Ejemplos ilustrativos
examples = [
    ("the cat sleeps", "the cat sleeps", "Perfecta"),
    ("the cat sleeps", "a cat is sleeping", "Sinónimos"),
    ("cat sleeps the", "the cat sleeps", "Orden incorrecto"),
    ("the", "the cat sleeps", "Muy corta"),
    ("the cat sleeps on the mat today", "the cat sleeps", "Muy larga"),
]

print("=" * 75)
print(f"{'Candidato':>30s} | {'Referencia':<20s} | {'BLEU':>6s} | Nota")
print("=" * 75)
for cand, ref, note in examples:
    score = bleu_score(cand, ref, max_n=2)  # BLEU-2 para oraciones cortas
    bar = "█" * int(score * 20)
    print(f"{cand:>30s} | {ref:<20s} | {score:.3f} | {note} {bar}")
===========================================================================
                     Candidato | Referencia           |   BLEU | Nota
===========================================================================
                the cat sleeps | the cat sleeps       | 1.000 | Perfecta ████████████████████
                the cat sleeps | a cat is sleeping    | 0.000 | Sinónimos 
                cat sleeps the | the cat sleeps       | 0.707 | Orden incorrecto ██████████████
                           the | the cat sleeps       | 0.000 | Muy corta 
the cat sleeps on the mat today | the cat sleeps       | 0.378 | Muy larga ███████

Evaluando Nuestro Traductor con BLEU

Code
# BLEU sobre datos de prueba
print("Evaluación BLEU del traductor ES → EN")
print("=" * 65)

bleu_scores = []
for src_sent, ref_sent in test_pairs:
    hyp = translate(model, src_sent, src_vocab, tgt_vocab)
    score = bleu_score(hyp, ref_sent, max_n=2)  # BLEU-2 (oraciones cortas)
    bleu_scores.append(score)
    print(f"  src: {src_sent}")
    print(f"  ref: {ref_sent}")
    print(f"  hyp: {hyp}")
    print(f"  BLEU-2: {score:.3f}")
    print()

avg_bleu = sum(bleu_scores) / len(bleu_scores) if bleu_scores else 0
print(f"BLEU-2 promedio (test): {avg_bleu:.3f}")
Evaluación BLEU del traductor ES → EN
=================================================================
  src: el pájaro pequeño canta
  ref: the small bird sings
  hyp: the beautiful bird sings loud
  BLEU-2: 0.387

  src: un gato pequeño come
  ref: a small cat eats
  hyp: a white cat runs far
  BLEU-2: 0.000

  src: un gato corre rápido
  ref: a cat runs fast
  hyp: a black cat runs fast
  BLEU-2: 0.632

  src: el gato duerme
  ref: the cat sleeps
  hyp: the black cat sleeps a lot
  BLEU-2: 0.316

  src: el gato come
  ref: the cat eats
  hyp: the white cat eats fish
  BLEU-2: 0.387

  src: el gato duerme aquí
  ref: the cat sleeps here
  hyp: the beautiful dog sleeps a lot
  BLEU-2: 0.000

BLEU-2 promedio (test): 0.287

Trucos de los Sistemas NMT Reales ⚡

Técnicas que Mejoran la Traducción

Arquitectura

Técnica Efecto
Encoder bidireccional Captura contexto en ambas direcciones
Stacked layers (2-8) Mayor capacidad de representación
Residual connections Permite entrenar redes más profundas
Dropout (0.1-0.3) Regularización

Decodificación

Técnica Efecto
Beam search (\(k\)=4-10) Mejores traducciones que greedy
Length normalization Evita sesgo hacia traducciones cortas
Coverage penalty Evita repeticiones y omisiones

Datos y Entrenamiento

Técnica Efecto
Subword tokenization (BPE) Vocabulario abierto, maneja OOV
Back-translation Datos sintéticos para aumentar corpus
Label smoothing Mejor generalización
Scheduled sampling Reduce exposure bias
Reversing source Mejora alineación (Sutskever 2014)

Escala real (GNMT, 2016)

  • 8 capas LSTM encoder + 8 decoder
  • 36M pares de oraciones
  • Entrenamiento: ~6 días en 96 GPUs
  • BLEU en WMT14 EN→FR: 41.16

Limitaciones del Seq2Seq Vanilla

Problemas identificados

  1. Cuello de botella de información: toda la oración → un vector fijo
  2. Degradación con longitud: calidad cae con oraciones > 20 tokens
  3. No hay alineación explícita: decoder no sabe qué parte de la entrada mirar

La pregunta clave

¿Qué pasaría si el decoder pudiera consultar diferentes partes de la entrada en cada paso, en lugar de depender de un solo vector?

→ Esto es exactamente lo que hace la Atención (S3)

Resumen

Lo Que Aprendimos Hoy

Conceptos

  • La traducción automática neuronal (NMT) aplica Seq2Seq a pares de idiomas
  • Requiere corpus paralelo y vocabularios separados
  • BLEU score mide similitud de n-gramas con referencia humana
  • Técnicas como beam search, subword tokenization y scheduled sampling mejoran resultados
  • El cuello de botella del vector fijo limita la calidad

Práctica

  • Pipeline: tokenización → vocabularios → padding → Seq2Seq
  • Entrenamos un traductor ES→EN funcional
  • Evaluamos con BLEU-2 (adaptado a oraciones cortas)
  • bleu_score(): precisión de n-gramas + penalización por brevedad
  • Identificamos las limitaciones → motivación para Atención

Fórmulas Clave

Concepto Fórmula
Precisión n-grama \(p_n = \frac{\sum \min(\text{count}_{cand}, \text{count}_{ref})}{\sum \text{count}_{cand}}\)
Brevity Penalty \(\text{BP} = \min(1, e^{1-r/c})\)
BLEU \(\text{BLEU} = \text{BP} \cdot \exp\left(\sum_{n=1}^{N} \frac{1}{N} \log p_n\right)\)
NMT Loss \(\mathcal{L} = -\sum_{t=1}^{T_y} \log P(y_t^* \mid y_{<t}, \mathbf{c})\)
Scheduled Sampling \(\text{TF ratio} = \max(\epsilon, 1 - \text{epoch}/E)\)

Para la Próxima Sesión 📚

Semana 7, S3: El Mecanismo de Atención

  • El cuello de botella del vector de contexto fijo
  • Atención de Bahdanau (aditiva) vs. Luong (multiplicativa)
  • Pesos de atención: ¿qué partes de la entrada son relevantes?
  • Visualización de mapas de atención
  • De la atención al Transformer (preview Semana 8)

Lectura:

  • Bahdanau et al. (2015): Neural Machine Translation by Jointly Learning to Align and Translate
  • Luong et al. (2015): Effective Approaches to Attention-based Neural Machine Translation
  • Jurafsky & Martin, Cap. 10.4: Attention

¿Preguntas? 🙋

¡Gracias!

📧 fsuarez@ucb.edu.bo

🔗 Materiales: github.com/fjsuarez/ucb-nlp