👨🏫 Profesor: Sergio Gevatschnaider
⏱️ Duración: 90 m
🎯 Nivel: Intermedio
Las Redes Neuronales Recurrentes (RNN) son una clase de redes neuronales artificiales donde las conexiones entre nodos forman un grafo dirigido a lo largo de una secuencia temporal. Esto les permite exhibir un comportamiento dinámico temporal y usar un estado interno (memoria) para procesar secuencias de entradas de longitud variable.
Red Neuronal Tradicional (Feedforward) - El Médico Amnésico: Imagina ir a un médico que tiene amnesia. Cada vez que lo visitas, no recuerda nada de tu historial. Si le dices "tengo tos", te recetará un jarabe. Si al día siguiente vuelves y dices "ahora tengo fiebre", te dará un antipirético, ignorando por completo que ayer tenías tos. No puede diagnosticar una neumonía porque no puede conectar los síntomas a lo largo del tiempo.
Red Neuronal Recurrente (RNN) - El Médico con Bloc de Notas: Ahora imagina un médico que anota todo en un bloc. Cuando llegas con tos, lo anota. Al día siguiente, cuando llegas con fiebre, lee su nota sobre la tos y dice: "Interesante, tos ayer y fiebre hoy... esto podría ser algo más serio". ¡Ha usado el contexto previo (su memoria) para hacer un diagnóstico más inteligente! Ese "bloc de notas" es el estado oculto de una RNN.
La magia de una RNN reside en un concepto simple pero poderoso: un bucle. La red procesa un elemento de la secuencia y pasa una "memoria" de lo que vio al siguiente paso. Esta "memoria" se llama estado oculto (hidden state).
A la izquierda, la RNN en su forma compacta (plegada). A la derecha, la misma red desplegada a través del tiempo, mostrando cómo la información fluye.
¿Cómo se calcula exactamente el nuevo estado oculto h_t
? Es una combinación de la memoria anterior h_{t-1}
y la nueva información x_t
.
ht = tanh(Whh * ht-1 + Wxh * xt + bh)
h_{t-1}
: El estado oculto (memoria) del paso anterior.x_t
: La entrada actual.W_{hh}
: La matriz de pesos para la memoria anterior.W_{xh}
: La matriz de pesos para la entrada actual.b_h
: El sesgo (bias) del estado oculto.tanh
: Una función de activación que mantiene los valores entre -1 y 1, evitando que la memoria "explote".A menudo, queremos una salida en cada paso de tiempo (ej. traducir una palabra en cada paso). Esta se calcula a partir del estado oculto actual:
y_t
: La salida predicha en el paso t.W_{hy}
: La matriz de pesos que transforma la memoria en una salida.b_y
: El sesgo de la salida.Es CRUCIAL entender que las matrices de pesos (W_{hh}
, W_{xh}
, W_{hy}
) y los sesgos son los mismos en cada paso de tiempo. Esto es lo que permite a la RNN generalizar su conocimiento a través de la secuencia. No aprende una regla para la primera palabra y otra para la tercera; aprende una única regla sobre cómo actualizar su memoria basándose en la nueva información, sin importar en qué punto de la secuencia se encuentre.
Las RNN simples son maravillosas, pero tienen un problema fundamental cuando las secuencias son muy largas: el problema de la desaparición y explosión del gradiente.
Desaparición del Gradiente: Imagina el juego del teléfono. Una persona susurra un mensaje a la siguiente, y así sucesivamente. Para cuando el mensaje llega al final de una larga fila, es probable que se haya debilitado, distorsionado o perdido por completo. En una RNN, la "señal" del gradiente de error de los primeros pasos se debilita a medida que se propaga hacia atrás en el tiempo, haciendo imposible que la red aprenda dependencias a largo plazo.
Explosión del Gradiente: Ahora imagina un micrófono demasiado cerca de un altavoz. El sonido entra en el micrófono, se amplifica, sale por el altavoz, vuelve a entrar en el micrófono, se amplifica aún más... y en segundos tienes un eco ensordecedor. En una RNN, si los gradientes son demasiado grandes, pueden crecer exponencialmente a medida que se propagan hacia atrás, desestabilizando el entrenamiento por completo.
Durante el entrenamiento (llamado Backpropagation Through Time), los gradientes se multiplican repetidamente por la matriz de pesos recurrentes W_{hh}
. Si los valores en esta matriz son consistentemente pequeños (< 1), el gradiente se encoge hasta desaparecer. Si son grandes (> 1), el gradiente crece hasta explotar.
Vamos a construir una RNN simple que aprende a predecir el siguiente carácter en la frase "hola mundo".
🐍 RNN para generar texto con TensorFlow/Keras
import tensorflow as tf
from tensorflow.keras import layers, models
import numpy as np
# --- 1. PREPARACIÓN DE DATOS ---
text = "hola mundo"
chars = sorted(list(set(text)))
char_to_idx = {c: i for i, c in enumerate(chars)}
idx_to_char = {i: c for i, c in enumerate(chars)}
vocab_size = len(chars)
# Crear secuencias de entrada (X) y salida (y)
seq_length = 3
X_data, y_data = [], []
for i in range(len(text) - seq_length):
X_data.append([char_to_idx[c] for c in text[i:i+seq_length]])
y_data.append(char_to_idx[text[i+seq_length]])
# Convertir a formato numpy y one-hot encoding
X = tf.keras.utils.to_categorical(X_data, num_classes=vocab_size)
y = tf.keras.utils.to_categorical(y_data, num_classes=vocab_size)
print(f"Número de secuencias de entrenamiento: {len(X)}")
# --- 2. CONSTRUCCIÓN DEL MODELO RNN ---
model = models.Sequential([
# La capa RNN. `input_shape` es (longitud_secuencia, tamaño_vocabulario)
layers.SimpleRNN(units=25, input_shape=(seq_length, vocab_size)),
# Capa de salida para predecir el próximo carácter
layers.Dense(units=vocab_size, activation='softmax')
])
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
model.summary()
# --- 3. ENTRENAMIENTO ---
print("\n--- Entrenando el modelo... ---")
model.fit(X, y, epochs=100, verbose=0)
print("✅ Entrenamiento completado.")
# --- 4. GENERACIÓN DE TEXTO ---
def predict_next_char(seed_text):
x_pred = np.zeros((1, seq_length, vocab_size))
for t, char in enumerate(seed_text):
x_pred[0, t, char_to_idx[char]] = 1.
prediction = model.predict(x_pred, verbose=0)[0]
next_idx = np.argmax(prediction)
return idx_to_char[next_idx]
seed = "ola"
prediction = predict_next_char(seed)
print(f"\nPredicción: Si la entrada es '{seed}', el siguiente carácter es '{prediction}'") # Debería predecir ' '
Para resolver el problema de los gradientes, se crearon arquitecturas más sofisticadas. No reemplazan el concepto de RNN, sino que proponen celdas recurrentes mucho más inteligentes.
Innovación: Introduce un "carril expreso" llamado estado de la celda y un sistema de compuertas (gates) que regulan el flujo de información.
Innovación: Una versión simplificada de la LSTM que combina las compuertas de olvido y entrada en una única compuerta de actualización. También tiene una compuerta de reseteo. Beneficio: Es computacionalmente más eficiente que la LSTM y ofrece un rendimiento similar en muchas tareas. Una gran alternativa.