📖 Teoría

Gated Recurrent Unit (GRU)

La alternativa elegante al LSTM: misma potencia contra el vanishing gradient, menos parámetros y más velocidad. Reset/update gates, ecuaciones formales, implementación en PyTorch y TensorFlow, y una comparación exhaustiva con LSTM para saber cuándo usar cada uno.

¿Por qué simplificar el LSTM?

El LSTM resolvió el problema del vanishing gradient de forma brillante, pero su arquitectura con 3 puertas, un cell state separado y 4 conjuntos de matrices de pesos es relativamente pesada. En 2014, Kyunghyun Cho et al. se preguntaron: ¿podemos lograr un rendimiento similar con una arquitectura más simple?

Aspecto LSTM GRU
Puertas 3 (forget, input, output) 2 (reset, update)
Estados 2 (ht + Ct) 1 (solo ht)
Matrices de pesos 4 conjuntos 3 conjuntos
Parámetros (dh=256, dx=100) ~367K ~275K (~25% menos)
Velocidad de entrenamiento Referencia ~15-25% más rápido

Esta reducción del ~25% en parámetros puede parecer modesta, pero tiene implicaciones prácticas significativas. Menos parámetros significa entrenamiento más rápido, menor consumo de memoria GPU, y menor riesgo de sobreajuste en datasets pequeños o medianos. Además, con menos operaciones por paso temporal, el GRU permite procesar secuencias más largas en el mismo tiempo — una ventaja crucial en aplicaciones de tiempo real como reconocimiento de voz en streaming o predicción de texto en teclados móviles.

💡

La idea del GRU: Combinar la forget gate y la input gate en una sola «update gate», eliminar el cell state separado, y usar directamente el hidden state con un mecanismo de actualización controlada. Menos parámetros, entrenamiento más rápido, rendimiento comparable en la mayoría de tareas.

📜 Origen del GRU

El Gated Recurrent Unit fue propuesto por Cho et al. en junio de 2014 en el paper «Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation», dentro del contexto de traducción automática neuronal. Los autores buscaban una unidad recurrente eficiente para su sistema encoder-decoder que pudiera capturar dependencias a largo plazo sin la complejidad computacional del LSTM. El resultado fue una arquitectura que demostraba que la simplificación podía lograrse sin sacrificar rendimiento.

1997 — LSTM

Hochreiter & Schmidhuber publican el LSTM, resolviendo el vanishing gradient con cell state y puertas. Arquitectura compleja pero eficaz.

2014 ★ — GRU

Cho, van Merriënboer, Gulcehre, Bahdanau et al. proponen el Gated Recurrent Unit como alternativa más simple al LSTM. Dos puertas en lugar de tres, un solo estado en lugar de dos. Rendimiento comparable con ~25% menos parámetros.

2014 — Mecanismo de atención

Bahdanau, Cho & Bengio publican el mecanismo de atención para traducción neuronal. El GRU y la atención comparten coautores y la misma motivación: eficiencia.

2016 — «LSTM: A Search Space Odyssey»

Greff et al. comparan exhaustivamente LSTM, GRU y variantes, concluyendo que el GRU rinde de forma comparable al LSTM en la mayoría de tareas.

Curiosamente, Dzmitry Bahdanau — coautor del paper del GRU — publicó en el mismo año su célebre paper sobre el mecanismo de atención, que eventualmente llevaría a los Transformers. El GRU y la atención nacieron de la misma necesidad: hacer que los modelos secuenciales fueran más eficientes y capaces. De hecho, el paper original del GRU fue uno de los primeros en utilizar una arquitectura encoder-decoder para traducción automática neuronal — un paradigma que dominaría el campo durante los siguientes años.

📚

Referencias fundamentales:

🔄 Las dos puertas del GRU

A diferencia del LSTM con sus 3 puertas + cell state, el GRU opera con solo 2 puertas y un único estado (el hidden state \(h_t\)):

🔃

Reset Gate \(r_t\)

«¿Cuánto del pasado ignorar?» Controla cuánta información del hidden state anterior se utiliza para calcular el nuevo candidato. Con \(r_t \approx 0\), el candidato se calcula casi como si no hubiera historia previa — un «reset» de la memoria.

rt = 0 → ignorar ht−1 en el candidato
rt = 1 → usar ht−1 completamente
🔀

Update Gate \(z_t\)

«¿Cuánto actualizar?» Combina las funciones de la forget gate y la input gate del LSTM en una sola puerta. Controla el balance entre mantener el estado anterior y adoptar el nuevo candidato.

zt ≈ 1 → mantener ht−1 (no actualizar)
zt ≈ 0 → reemplazar con h̃t (candidato nuevo)

La diferencia fundamental entre las puertas del GRU y las del LSTM radica en el acoplamiento. En el LSTM, la forget gate y la input gate son independientes: pueden ambas estar abiertas simultáneamente (añadir información nueva sin borrar la vieja) o ambas cerradas. En el GRU, la update gate \(z_t\) implementa un trade-off obligatorio: si \(z_t\) es alta (actualizar mucho), automáticamente \(1 - z_t\) es baja (retener poco del pasado), y viceversa. Esta restricción simplifica la dinámica del modelo y reduce la cantidad de configuraciones posibles que la red necesita aprender.

La reset gate, por su parte, juega un papel que no tiene equivalente directo en el LSTM. Al controlar cuánto del hidden state previo se utiliza para calcular el candidato, permite a la celda «ignorar» su historia cuando detecta un cambio de contexto abrupto — por ejemplo, al cruzar un límite de oración en un texto, o al iniciar un nuevo patrón en una serie temporal.

🔑

La clave del GRU: La update gate \(z_t\) implementa un mecanismo de «coupled gate» — lo que no se actualiza se mantiene, y lo que se actualiza reemplaza. No hay decisión independiente de «qué olvidar» y «qué escribir» como en LSTM; es un único trade-off: \(h_t = z_t \odot h_{t-1} + (1-z_t) \odot \tilde{h}_t\)

🔬 Anatomía de una celda GRU

El GRU es más compacto que el LSTM. Cada paso temporal recibe \(h_{t-1}\) y \(x_t\), y produce \(h_t\) — sin cell state separado:

CELDA GRU — FLUJO DE DATOS ht−1 ht σ reset gate rt σ update gate zt tanh candidato h̃t × rt ⊙ ht−1 × zt ⊙ ht−1 × (1−zt) ⊙ h̃t + ht−1 , xt [ht−1, xt] → entrada a ambas gates y al candidato (vía rt ⊙ ht−1) Reset gate Update gate Candidato h̃t × multiplicación + interpolación final

Paso a paso: flujo de datos en una celda GRU

1
Reset Gate: Calcula \(r_t = \sigma(W_r \cdot [h_{t-1}, x_t] + b_r)\). Decide cuánto de \(h_{t-1}\) considerar al generar el candidato.
2
Update Gate: Calcula \(z_t = \sigma(W_z \cdot [h_{t-1}, x_t] + b_z)\). Determina el balance entre mantener y actualizar.
3
Candidato: Calcula \(\tilde{h}_t = \tanh(W_h \cdot [r_t \odot h_{t-1}, x_t] + b_h)\). Propone un nuevo estado usando el pasado filtrado por la reset gate.
4
Interpolación: \(h_t = z_t \odot h_{t-1} + (1 - z_t) \odot \tilde{h}_t\). Mezcla suavemente el estado anterior con el candidato nuevo.
💡

Nota elegante: Cuando \(z_t = 1\), el GRU simplemente copia \(h_{t-1}\) sin ninguna transformación — el gradiente fluye directamente a través del tiempo, como una skip connection. Cuando \(z_t = 0\), el GRU ignora completamente el pasado y usa solo el candidato nuevo. Los valores intermedios interpolan suavemente.

🔍 GRU vs LSTM: comparación visual

La mejor forma de entender las diferencias arquitectónicas entre GRU y LSTM es verlas en paralelo. Aunque ambas resuelven el mismo problema (capturar dependencias a largo plazo evitando el vanishing gradient), lo hacen con mecanismos distintos:

🧠 LSTM

forget σ input σ output σ cand tanh
Cell state Ct Hidden ht
  • 3 gates + 1 candidato
  • 2 estados (Ct + ht)
  • 4 × dh(dh + dx + 1) parámetros
  • Ct: camino lineal separado para el gradiente
  • Forget e input gates independientes

⚡ GRU

reset σ update σ cand tanh
Hidden ht (único)
  • 2 gates + 1 candidato
  • 1 estado (solo ht)
  • 3 × dh(dh + dx + 1) parámetros
  • ht: interpolación directa como skip connection
  • Update gate = coupled forget + input

La reducción de una gate y un estado completo no es cosmética: implica ~25% menos de parámetros y, en consecuencia, menos cómputo por paso temporal, menor riesgo de sobreajuste en datasets pequeños, y tiempos de entrenamiento más cortos. La pregunta práctica es si esa simplificación sacrifica expresividad — tema que exploraremos en la sección de comparación empírica.

📐 Ecuaciones del GRU

Las ecuaciones del GRU son notablemente más compactas que las del LSTM. Mientras que el LSTM requiere cuatro conjuntos de ecuaciones (forget, input, output gates más candidato), el GRU las condensa en tres, y la ecuación final de actualización del estado es una simple interpolación lineal. En cada paso temporal:

1. Reset Gate $$r_t = \sigma\!\bigl(W_r \cdot [h_{t-1}, x_t] + b_r\bigr)$$
2. Update Gate $$z_t = \sigma\!\bigl(W_z \cdot [h_{t-1}, x_t] + b_z\bigr)$$
3. Candidate Hidden State $$\tilde{h}_t = \tanh\!\bigl(W_h \cdot [r_t \odot h_{t-1}, x_t] + b_h\bigr)$$
4. Hidden State (interpolación) $$h_t = z_t \odot h_{t-1} + (1 - z_t) \odot \tilde{h}_t$$
🔑

Diferencia clave con LSTM: En el LSTM, la forget gate \(f_t\) y la input gate \(i_t\) son independientes — pueden ambas estar en 1 o ambas en 0. En el GRU, la update gate \(z_t\) fuerza un trade-off: \(z_t\) controla cuánto mantener y \((1-z_t)\) controla cuánto actualizar. Siempre suman 1.

📏 Dimensiones y conteo de parámetros

El GRU tiene 3 conjuntos de matrices de pesos (vs 4 del LSTM):

Componente Matriz Dimensiones Bias
Reset gate \(W_r\) \(d_h \times (d_h + d_x)\) \(b_r \in \mathbb{R}^{d_h}\)
Update gate \(W_z\) \(d_h \times (d_h + d_x)\) \(b_z \in \mathbb{R}^{d_h}\)
Candidato \(W_h\) \(d_h \times (d_h + d_x)\) \(b_h \in \mathbb{R}^{d_h}\)
Parámetros del GRU $$\text{Params}_{\text{GRU}} = 3 \cdot d_h \cdot (d_h + d_x + 1)$$

COMPARACIÓN DE PARÁMETROS (dh=256, dx=100)

RNN vanilla
91.9K (1×)
GRU
275.7K (3×)
LSTM
367.6K (4×)

El GRU tiene ~25% menos parámetros que el LSTM → entrenamiento más rápido y menor riesgo de sobreajuste.

📈 ¿Por qué el GRU también resuelve el vanishing gradient?

El gradiente del hidden state a través del tiempo:

Gradiente del GRU $$\frac{\partial h_t}{\partial h_{t-1}} = \text{diag}(z_t) + \text{términos de segundo orden}$$

Cuando la update gate \(z_t \approx 1\), tenemos \(h_t \approx h_{t-1}\) y el gradiente es aproximadamente la identidad — la información fluye sin atenuación, exactamente como en el cell state del LSTM. El mecanismo es análogo a una residual connection adaptativa: en lugar de sumar un residuo fijo, la red aprende cuánto del residuo (el candidato) añadir en cada paso temporal.

Para secuencias largas, si la update gate permanece alta en múltiples pasos, el gradiente se propaga casi sin cambios a lo largo de cientos de timesteps:

Gradiente a largo plazo $$\frac{\partial h_T}{\partial h_k} \approx \prod_{t=k+1}^{T} \text{diag}(z_t) \approx I \quad \text{si } z_t \approx 1$$
🎯

Observación: Tanto el LSTM como el GRU resuelven el vanishing gradient mediante el mismo principio: crear un camino directo (highway) para el gradiente con multiplicación por valores cercanos a 1. En el LSTM ese camino es el cell state; en el GRU es directamente el hidden state con la interpolación de la update gate. (Más detalles en Fundamentos de RNN y LSTM en profundidad.)

🎛️ Explorador interactivo del GRU

Ajusta los valores de las gates y observa cómo el GRU interpola entre mantener el estado anterior y adoptar el candidato:

0.50
0.70
0.60
-0.30

🔥 GRU en PyTorch

La API de PyTorch para GRU es prácticamente idéntica a la de LSTM. La principal diferencia es que nn.GRU retorna (output, h_n) en vez de (output, (h_n, c_n)) — no hay cell state. Esto simplifica notablemente el código: no necesitas desempaquetar tuplas al recibir la salida, la función forward es más limpia, y la inicialización del estado oculto requiere una sola variable en vez de dos.

El primer ejemplo muestra un clasificador bidireccional completo usando nn.GRU de alto nivel. Observa que la única diferencia con un LSTM equivalente está en la línea de desempaquetado de la salida (línea 25) y en el constructor (línea 11). Todo lo demás — embeddings, dropout, capa de clasificación — es idéntico.

import torch
import torch.nn as nn

class GRUClassifier(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes,
                 num_layers=2, dropout=0.3, bidirectional=True):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
        self.gru = nn.GRU(
            input_size=embed_dim,
            hidden_size=hidden_dim,
            num_layers=num_layers,
            batch_first=True,
            dropout=dropout,
            bidirectional=bidirectional
        )
        direction_factor = 2 if bidirectional else 1
        self.fc = nn.Linear(hidden_dim * direction_factor, num_classes)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        embedded = self.dropout(self.embedding(x))

        # GRU retorna (output, h_n) — sin cell state
        output, h_n = self.gru(embedded)

        if self.gru.bidirectional:
            h_final = torch.cat([h_n[-2], h_n[-1]], dim=1)
        else:
            h_final = h_n[-1]

        return self.fc(self.dropout(h_final))

# Comparación directa: misma tarea, GRU vs LSTM
gru_model = GRUClassifier(vocab_size=10000, embed_dim=128,
                           hidden_dim=256, num_classes=5)
x = torch.randint(0, 10000, (32, 50))
logits = gru_model(x)

# Contar parámetros
gru_params = sum(p.numel() for p in gru_model.parameters())
print(f"GRU params: {gru_params:,}")

El segundo ejemplo muestra nn.GRUCell, la versión paso a paso que te da control total sobre el bucle temporal. Esto es útil cuando necesitas lógica condicional dentro del bucle (por ejemplo, teacher forcing, atención, o beam search) o cuando implementas variantes del GRU como el GRU-D para datos con tiempos irregulares. Compara con el equivalente LSTMCell, donde necesitarías gestionar dos variables de estado en cada paso.

import torch
import torch.nn as nn

class ManualGRU(nn.Module):
    """GRU paso a paso con GRUCell."""
    def __init__(self, input_dim, hidden_dim):
        super().__init__()
        self.hidden_dim = hidden_dim
        self.cell = nn.GRUCell(input_dim, hidden_dim)

    def forward(self, x):
        batch, seq_len, _ = x.shape
        h = torch.zeros(batch, self.hidden_dim, device=x.device)

        outputs = []
        for t in range(seq_len):
            h = self.cell(x[:, t, :], h)  # Solo h, sin cell state
            outputs.append(h)

        return torch.stack(outputs, dim=1), h

# Ventaja del GRU: inicialización más simple
model = ManualGRU(input_dim=64, hidden_dim=128)
x = torch.randn(1, 20, 64)
outputs, h_final = model(x)
print(f"Hidden state final: norma={h_final.norm():.4f}")
# No necesitas gestionar cell state — una variable menos

🟧 GRU en TensorFlow/Keras

En Keras, la transición de LSTM a GRU es igualmente directa: basta con reemplazar tf.keras.layers.LSTM por tf.keras.layers.GRU. La ventaja adicional en TensorFlow es la compatibilidad automática con cuDNN: si usas reset_after=True (el valor por defecto desde TF 2.x) y no activas recurrent_dropout, Keras delega el cálculo completo a la implementación optimizada de NVIDIA, consiguiendo speed-ups de 3× a 10×.

import tensorflow as tf

def build_gru_model(vocab_size=10000, embed_dim=128, hidden_dim=128,
                    max_len=200, num_classes=2):
    model = tf.keras.Sequential([
        tf.keras.layers.Embedding(vocab_size, embed_dim,
                                  input_length=max_len),
        tf.keras.layers.SpatialDropout1D(0.2),

        # GRU bidireccional
        tf.keras.layers.Bidirectional(
            tf.keras.layers.GRU(hidden_dim, return_sequences=True,
                                dropout=0.2, recurrent_dropout=0.2)
        ),
        tf.keras.layers.Bidirectional(
            tf.keras.layers.GRU(hidden_dim // 2,
                                dropout=0.2, recurrent_dropout=0.2)
        ),

        tf.keras.layers.Dense(32, activation='relu'),
        tf.keras.layers.Dropout(0.3),
        tf.keras.layers.Dense(num_classes, activation='softmax')
    ])

    model.compile(optimizer='adam',
                  loss='sparse_categorical_crossentropy',
                  metrics=['accuracy'])
    return model

model = build_gru_model()
model.summary()
# Comparar el total de parámetros con un LSTM equivalente:
# GRU tendrá ~25% menos parámetros en las capas recurrentes
import tensorflow as tf
import numpy as np

def build_gru_time_series(seq_len=60, n_features=3, hidden_dim=64):
    """GRU para predicción multivariante de series temporales."""
    inputs = tf.keras.Input(shape=(seq_len, n_features))

    x = tf.keras.layers.GRU(hidden_dim, return_sequences=True)(inputs)
    x = tf.keras.layers.Dropout(0.2)(x)
    x = tf.keras.layers.GRU(hidden_dim // 2)(x)
    x = tf.keras.layers.Dense(16, activation='relu')(x)
    outputs = tf.keras.layers.Dense(1)(x)

    model = tf.keras.Model(inputs, outputs)
    model.compile(optimizer='adam', loss='mse')
    return model

# Ejemplo: 3 features (temperatura, humedad, presión)
model = build_gru_time_series()
X = np.random.randn(1000, 60, 3).astype(np.float32)
y = np.random.randn(1000).astype(np.float32)
model.fit(X, y, epochs=5, batch_size=32, verbose=0)
print(f"GRU time series model params: {model.count_params():,}")
💡

Consejo práctico: En TensorFlow, el GRU con reset_after=True (por defecto desde TF 2.x) es compatible con la implementación cuDNN optimizada por GPU, lo que lo hace significativamente más rápido. El LSTM también tiene optimización cuDNN, pero el GRU se beneficia más por tener menos operaciones.

⚖️ LSTM vs GRU: comparación sistemática

¿Cuándo usar LSTM y cuándo GRU? No hay una respuesta universal. La investigación empírica (Chung et al. 2014, Jozefowicz et al. 2015, Greff et al. 2016) muestra que depende de la tarea y los datos. Aquí tienes una guía práctica:

Criterio LSTM GRU Ganador
Parámetros 4 × dh(dh+dx+1) 3 × dh(dh+dx+1) GRU (~25% menos)
Velocidad Referencia ~15-25% más rápido GRU
Memoria GPU 2 estados (ht + Ct) 1 estado (ht) GRU
Dependencias muy largas Cell state separado Interpolación directa LSTM (ligeramente)
Datasets pequeños Puede overfittear Menos parámetros = menos overfit GRU
Datasets grandes Mayor capacidad Puede ser insuficiente LSTM
Tareas de NLP complejas Más flexible (gates indep.) Suficiente en muchos casos ~Empate
Series temporales Bueno Igual de bueno, más rápido GRU (eficiencia)
Interpretabilidad Cell state analizable Hidden state directo ~Empate
🧪

Regla general:

  • Empieza con GRU si no tienes razones específicas para usar LSTM. Es más rápido de entrenar y suele dar resultados comparables.
  • Usa LSTM si necesitas máxima expresividad (datasets grandes, dependencias muy largas) o si el rendimiento del GRU no es suficiente.
  • Prueba ambos — la diferencia en rendimiento suele ser pequeña, y el mejor modelo depende del problema concreto.

📊 Simulador comparativo: RNN vs LSTM vs GRU

Observa cómo los tres modelos propagan un gradiente a lo largo de una secuencia. Ajusta los parámetros y compara el comportamiento:

30
0.90
0.90

🌍 Aplicaciones destacadas del GRU

🤖
Chatbots y diálogo
Modelos de conversación con GRU encoder-decoder. Rápidos de entrenar, ideales para producción ligera.
pregunta respuesta
📊
Detección de anomalías
En series temporales de sensores IoT, logs de servidores, tráfico de red. La eficiencia del GRU permite despliegue en edge.
serie temporal normal/anomalía
🎮
Aprendizaje por refuerzo
En entornos parcialmente observables, GRU como memoria del agente. Ligero y eficiente para RL online.
observaciones acción
🩺
Registros clínicos
Predicción de riesgo a partir de secuencias de visitas médicas. GRU-D maneja datos con tiempos irregulares.
historial riesgo
📱
Modelos en dispositivo
Teclados predictivos, reconocimiento de gestos, modelos on-device donde cada KB de RAM cuenta.
secuencia predicción
🧬
Secuencias biológicas
Análisis de ADN, predicción de estructura secundaria de proteínas. GRU bidireccional para contexto completo.
secuencia bio estructura

🔮 RNN, LSTM, GRU y Transformers: el panorama completo

Para cerrar el módulo de redes recurrentes, veamos cómo encajan las tres arquitecturas en el panorama general del deep learning para secuencias:

EVOLUCIÓN DEL PROCESAMIENTO DE SECUENCIAS

RNN Vanilla

~1986

Elman & Jordan introducen la recurrencia: ht = f(ht−1, xt). El concepto fundamental de redes con memoria, pero limitado por el vanishing gradient en secuencias largas.

Recurrencia • Memoria implícita • Vanishing gradient

LSTM

1997

Hochreiter & Schmidhuber resuelven el vanishing gradient con gates y un cell state separado (Ct). Forget, input y output gates controlan el flujo de información de forma independiente.

+ Gates + Cell State • Memoria a largo plazo

GRU

2014

Cho et al. simplifican el LSTM: 2 gates (reset + update) en vez de 3, sin cell state separado. Rendimiento comparable con ~25% menos parámetros y entrenamiento más rápido.

+ Simplificación • Eficiencia ≈ rendimiento

Transformers

2017

Vaswani et al. eliminan la recurrencia por completo. Self-attention permite procesar toda la secuencia en paralelo, escalando a billones de parámetros. Heredan conceptos de gates (skip connections) del LSTM/GRU.

Sin recurrencia • Self-attention paralelo

📌 RESUMEN DEL MÓDULO RNN

  • RNN Vanilla → concepto de recurrencia, limitado por vanishing gradient
  • LSTM → resuelve vanishing gradient con gates y cell state separado
  • GRU → simplificación del LSTM, rendimiento similar con menos parámetros
🎓

Lo que has aprendido en el módulo RNN:

  1. Fundamentos: Datos secuenciales, recurrencia, hidden state, BPTT, vanishing/exploding gradients.
  2. LSTM: Cell state, forget/input/output gates, cómo resuelve el vanishing gradient, la «neurona sintiente».
  3. GRU: Simplificación elegante, reset/update gates, cuándo usar cada arquitectura.

Estas ideas son fundamentales para entender por qué los Transformers funcionan como funcionan — los mecanismos de atención y las skip connections heredan conceptos directos del LSTM y GRU.

📚

Referencias y lecturas recomendadas

  • Cho et al. (2014). Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation. arXiv:1406.1078 — Paper original del GRU.
  • Chung et al. (2014). Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling. arXiv:1412.3555 — Comparación empírica GRU vs LSTM.
  • Jozefowicz et al. (2015). An Empirical Exploration of Recurrent Network Architectures. ICML 2015 — Estudio exhaustivo de variantes de RNN.
  • Greff et al. (2016). LSTM: A Search Space Odyssey. arXiv:1503.04069 — Análisis de ablación de componentes LSTM/GRU.
  • Vaswani et al. (2017). Attention Is All You Need. arXiv:1706.03762 — Los Transformers que sucedieron a LSTM/GRU.

📖 Ver también: Fundamentos de RNN | LSTM en profundidad

🏭 Casos de uso