📖 Teoría

Aprendizaje por Refuerzo

De los fundamentos de procesos de Markov a los agentes modernos: Q-Learning, Deep Q-Networks (DQN), Policy Gradients, Actor-Critic, PPO, SAC, RLHF para LLMs, y aplicaciones en robótica, conducción autónoma y más.

¿Qué es el Aprendizaje por Refuerzo?

El Aprendizaje por Refuerzo (Reinforcement Learning, RL) es el tercer gran paradigma del Machine Learning. A diferencia del aprendizaje supervisado —donde un modelo aprende de pares (entrada, etiqueta)— y del no supervisado —donde se buscan patrones sin etiquetas—, en RL un agente aprende a tomar decisiones secuenciales interactuando con un entorno, recibiendo recompensas (o penalizaciones) por sus acciones.

💡 Intuición: Imagina enseñar a un perro un truco nuevo. No le das instrucciones paso a paso (supervisado), ni le dejas descubrir patrones por su cuenta (no supervisado). Le das una golosina cuando hace algo bien y dices "no" cuando no. El perro explora, descubre qué acciones llevan a la golosina, y explota ese conocimiento. Eso es Reinforcement Learning.
🤖 Agente Política π(s) → a 🌍 Entorno Dinámica del mundo Acción aₜ Estado sₜ₊₁ Recompensa rₜ₊₁ El agente actúa → el entorno responde → el agente aprende → repite
🧠
Agente
El que toma decisiones. Observa el estado del entorno y elige acciones según su política \(\pi\).
🌍
Entorno
El mundo con el que interactúa el agente. Responde a las acciones con un nuevo estado y una recompensa.
Acción
Lo que el agente decide hacer en cada paso. Puede ser discreta (izquierda/derecha) o continua (ángulo de giro).
🎯
Recompensa
Señal escalar que indica lo "buena" o "mala" fue la acción. El agente busca maximizar la recompensa acumulada.
📍
Estado
Representación del entorno en un instante. Contiene toda la información relevante para tomar la siguiente decisión.
📋
Política
La "estrategia" del agente: función \(\pi(s)\) que mapea cada estado a una acción (o distribución de acciones).

Los tres paradigmas del Machine Learning

El aprendizaje por refuerzo ocupa un lugar único entre los paradigmas del ML. Mientras que en supervisado se dispone de la "respuesta correcta" y en no supervisado no hay señal de retroalimentación, en RL el agente recibe una señal escalar retardada —la recompensa— que no le dice qué debería haber hecho, solo cuánto de bien lo hizo.

Característica Supervisado No supervisado Refuerzo
DatosPares (x, y) etiquetadosSolo x, sin etiquetasInteracciones (s, a, r, s')
SeñalError directo (label)NingunaRecompensa escalar retardada
ObjetivoMinimizar error de predicciónDescubrir estructuraMaximizar recompensa acumulada
FeedbackInmediato y completoNo hayRetardado y parcial
DatosDataset estático (i.i.d.)Dataset estáticoGenerados por el agente (no i.i.d.)
ExploraciónNo necesariaNo necesariaFundamental (explore vs exploit)
EjemploClasificar imágenesClustering, PCAJugar al ajedrez, control robótico
⚠️ El dilema exploración vs explotación: El agente debe equilibrar explorar (probar acciones nuevas para descubrir mejores recompensas) con explotar (usar el conocimiento actual para maximizar la recompensa). Este tradeoff es exclusivo del RL y no existe en supervisado ni en no supervisado.

Casos de uso del Aprendizaje por Refuerzo

El RL brilla en problemas donde se necesita tomar decisiones secuenciales y donde no existe un dataset etiquetado de "decisiones óptimas". Estos son los dominios más importantes:

🎮
Juegos
El campo de pruebas original. Atari (DQN, 2013), Go (AlphaGo, 2016), StarCraft II (AlphaStar, 2019), Dota 2 (OpenAI Five), ajedrez. Mnih, V. et al. (2015). "Human-level control through deep reinforcement learning". Nature.
🤖
Robótica
Manipulación de objetos, locomoción, control de robots humanoides. Sim-to-real transfer: entrenar en simulación y transferir al robot real. Levine, S. et al. (2016). "End-to-End Training of Deep Visuomotor Policies".
🚗
Conducción autónoma
Planificación de trayectorias, toma de decisiones en intersecciones, adaptación a condiciones de tráfico dinámicas.
💬
LLMs (RLHF)
Alineamiento de modelos de lenguaje con preferencias humanas usando PPO. ChatGPT, Claude, Gemini usan RLHF/DPO para ser más útiles y seguros. Ouyang, L. et al. (2022). "Training language models to follow instructions with human feedback".
📈
Finanzas
Trading algorítmico, gestión de portfolios, pricing dinámico. El mercado es el entorno; la recompensa es el beneficio.
🏥
Salud
Tratamiento personalizado, dosificación de fármacos, planificación quirúrgica. Decisiones secuenciales con consecuencias a largo plazo.
Energía y recursos
Optimización de redes eléctricas, control de HVAC en data centers (Google DeepMind: -40% energía de cooling), gestión de tráfico.
🧬
Descubrimiento científico
Diseño de moléculas y fármacos, optimización de experimentos, control de reactores de fusión (DeepMind + EPFL, 2022).

Historia y hitos del Aprendizaje por Refuerzo

1957 Ecuaciones de Bellman 1989 Q-Learning Watkins 1992 TD-Gammon Backgammon 1998 Sutton & Barto 📖 2013 DQN Atari · DeepMind 2016 AlphaGo vence a Lee Sedol 2017 PPO & AlphaZero 2022 RLHF ChatGPT De las ecuaciones de Bellman al alineamiento de LLMs con RLHF: 65 años de evolución
  • Sutton & Barto (2018): "Reinforcement Learning: An Introduction" (2ª ed.) — Libro gratuito online. La biblia del RL.
  • David Silver (2015): Curso de RL de DeepMind/UCL — Lectures
  • Spinning Up (OpenAI): Guía educativa de RL profundo con implementaciones.
  • Gymnasium (Farama): Entornos estándar de RL (antes OpenAI Gym).
  • Stable Baselines3: Implementaciones fiables de algoritmos RL en PyTorch.

Taxonomía del Aprendizaje por Refuerzo

El campo del RL es amplio. Esta taxonomía te ayuda a situar cada algoritmo en su contexto:

Reinforcement Learning Model-Free Model-Based Value-Based Policy-Based Q-Learning DQN, Double DQN Dueling DQN REINFORCE A2C / A3C PPO, SAC, TRPO Actor-Critic = Value + Policy Dyna-Q World Models AlphaZero / MuZero Dreamer v3 Model-Free: aprende por prueba y error · Model-Based: aprende un modelo del entorno y planifica

🔍 ¿Model-Free o Model-Based?

  • Model-Free: el agente no intenta aprender cómo funciona el entorno. Aprende directamente una política o función de valor por prueba y error. Más simple, más robusto, pero necesita muchas interacciones.
  • Model-Based: el agente aprende un modelo del entorno (cómo transiciona entre estados) y lo usa para planificar. Más eficiente en datos, pero más complejo y propenso a errores del modelo.
  • Value-Based: aprende el "valor" de cada estado o acción, y actúa de forma greedy (elige la acción con mayor valor).
  • Policy-Based: aprende directamente la política \(\pi(a|s)\) sin calcular valores explícitamente.
  • Actor-Critic: lo mejor de ambos mundos — un actor (política) y un critic (valor) que se entrenan juntos.

Procesos estocásticos y Cadenas de Markov

Para formalizar el RL necesitamos herramientas de la teoría de la probabilidad. Todo comienza con los procesos estocásticos: secuencias de variables aleatorias que evolucionan en el tiempo.

📐 Proceso estocástico

Un proceso estocástico es una colección de variables aleatorias \(\{X_t\}_{t \geq 0}\) indexadas por el tiempo \(t\). Cada \(X_t\) toma valores en un espacio de estados \(\mathcal{S}\). El proceso describe cómo el sistema evoluciona de forma probabilística a lo largo del tiempo.

Un caso especial extremadamente importante es cuando el futuro solo depende del presente, no del pasado. Esta es la propiedad de Markov:

🔑 Propiedad de Markov: Un proceso estocástico tiene la propiedad de Markov si y solo si:
$$P(X_{t+1} | X_t, X_{t-1}, \ldots, X_0) = P(X_{t+1} | X_t)$$
El estado presente contiene toda la información necesaria para predecir el futuro. El pasado es irrelevante dado el presente. Esto se conoce como "el futuro es independiente del pasado dado el presente".

Cadena de Markov

Una Cadena de Markov es un proceso estocástico con la propiedad de Markov, un espacio de estados discreto \(\mathcal{S} = \{s_1, s_2, \ldots, s_n\}\), y transiciones en tiempo discreto. Se define completamente por:

  • Un conjunto de estados \(\mathcal{S}\)
  • Una matriz de transición \(\mathbf{P}\) donde \(P_{ij} = P(X_{t+1} = s_j \mid X_t = s_i)\)
  • Una distribución inicial \(\mu_0\) sobre los estados
Ejemplo: Cadena de Markov del clima ☀️ Soleado 🌧️ Lluvioso 0.8 0.6 0.2 0.4 Matriz de transición P: P = [0.8 0.2] [0.4 0.6]

Proceso de Decisión de Markov (MDP)

El MDP extiende la Cadena de Markov añadiendo acciones y recompensas. Es el marco matemático fundamental de todo el RL. Un MDP es una tupla \(\langle \mathcal{S}, \mathcal{A}, P, R, \gamma \rangle\):

SímboloNombreDescripción
\(\mathcal{S}\)Espacio de estadosConjunto de todos los estados posibles del entorno.
\(\mathcal{A}\)Espacio de accionesConjunto de todas las acciones que el agente puede tomar.
\(P(s'|s,a)\)Función de transiciónProbabilidad de llegar al estado \(s'\) al tomar la acción \(a\) en el estado \(s\).
\(R(s,a,s')\)Función de recompensaRecompensa inmediata al transicionar de \(s\) a \(s'\) con la acción \(a\).
\(\gamma \in [0,1)\)Factor de descuentoCuánto importan las recompensas futuras vs las inmediatas.
💡 Intuición del factor de descuento \(\gamma\): Con \(\gamma = 0\), el agente es miope — solo le importa la recompensa inmediata. Con \(\gamma \to 1\), el agente es previsor — valora mucho las recompensas futuras. Valores típicos: \(\gamma = 0.99\) o \(\gamma = 0.95\). Un euro hoy vale más que un euro mañana: eso es exactamente lo que modela \(\gamma\).

La dinámica del MDP

En cada paso de tiempo \(t\):

1
El agente observa el estado \(s_t \in \mathcal{S}\).
2
El agente elige una acción \(a_t \in \mathcal{A}\) según su política \(\pi(a_t | s_t)\).
3
El entorno transiciona al nuevo estado \(s_{t+1} \sim P(\cdot | s_t, a_t)\).
4
El agente recibe la recompensa \(r_{t+1} = R(s_t, a_t, s_{t+1})\).
5
Se repite hasta un estado terminal o para siempre (tarea continua).

Ejemplo: GridWorld

Un clásico ejemplo de MDP es el GridWorld: una cuadrícula donde un agente se mueve con acciones {↑, ↓, ←, →}, busca alcanzar la meta (+1) evitando trampas (−1).

🏆 +1 🧱 💀 −1 🤖 Start S = {16 celdas} A = {↑,↓,←,→} γ = 0.9 🏆 Meta: r = +1 💀 Trampa: r = −1 🧱 Muro: no pasa Otros: r = −0.04

Política, retorno y objetivo del RL

Política \(\pi\)

La política es la estrategia del agente. Define qué acción tomar en cada estado. Puede ser:

  • Determinista: \(\pi(s) = a\) — mapea cada estado a exactamente una acción.
  • Estocástica: \(\pi(a|s) = P(A_t = a \mid S_t = s)\) — da una distribución de probabilidad sobre acciones.

Retorno \(G_t\)

El retorno es la recompensa total acumulada (descontada) desde el paso \(t\) en adelante:

$$G_t = r_{t+1} + \gamma \, r_{t+2} + \gamma^2 \, r_{t+3} + \cdots = \sum_{k=0}^{\infty} \gamma^k \, r_{t+k+1}$$

El factor de descuento \(\gamma\) asegura que la serie converge (para recompensas acotadas) y modela que las recompensas cercanas son más valiosas que las lejanas.

🎯 Objetivo del RL

Encontrar la política \(\pi^*\) que maximice el retorno esperado:

$$\pi^* = \arg\max_\pi \, \mathbb{E}_\pi \left[ G_t \right] = \arg\max_\pi \, \mathbb{E}_\pi \left[ \sum_{k=0}^{\infty} \gamma^k \, r_{t+k+1} \right]$$

El agente no solo quiere recompensas altas ahora, sino a lo largo de toda su vida. Este es el principio de optimización a largo plazo que distingue al RL de la optimización greedy.

🎛️ Widget: Explorador del factor de descuento \(\gamma\)

Visualiza cómo \(\gamma\) afecta al peso de las recompensas futuras. Con recompensa constante \(r=1\) en cada paso:

0.90

Funciones de valor: \(V(s)\) y \(Q(s,a)\)

Las funciones de valor son el concepto central del RL. Responden a la pregunta: "¿cuánta recompensa acumulada puedo esperar desde aquí?"

State-Value Function \(V^\pi(s)\)

Definición: el valor de un estado \(s\) bajo la política \(\pi\) es el retorno esperado partiendo de \(s\) y siguiendo \(\pi\):
$$V^\pi(s) = \mathbb{E}_\pi [G_t \mid S_t = s] = \mathbb{E}_\pi \left[\sum_{k=0}^{\infty} \gamma^k \, r_{t+k+1} \;\middle|\; S_t = s\right]$$
Nos dice lo bueno que es estar en el estado \(s\) si seguimos la política \(\pi\).

Action-Value Function \(Q^\pi(s,a)\)

Definición: el valor de una acción \(a\) en el estado \(s\) bajo la política \(\pi\) es el retorno esperado partiendo de \(s\), tomando la acción \(a\), y después siguiendo \(\pi\):
$$Q^\pi(s, a) = \mathbb{E}_\pi [G_t \mid S_t = s, A_t = a] = \mathbb{E}_\pi \left[\sum_{k=0}^{\infty} \gamma^k \, r_{t+k+1} \;\middle|\; S_t = s, A_t = a\right]$$
Nos dice lo bueno que es tomar la acción \(a\) en el estado \(s\).

🔗 Relación entre \(V\) y \(Q\)

Las funciones \(V\) y \(Q\) están íntimamente relacionadas:

$$V^\pi(s) = \sum_{a \in \mathcal{A}} \pi(a|s) \, Q^\pi(s, a)$$

Es decir, el valor de un estado es el promedio ponderado del valor de cada acción, ponderado por la probabilidad de tomar esa acción según la política.

Y la política óptima simplemente elige la acción con mayor Q-value:

$$\pi^*(s) = \arg\max_a \, Q^*(s, a)$$

Las ecuaciones de Bellman

Richard Bellman (1957) descubrió la relación recursiva fundamental de las funciones de valor. Las ecuaciones de Bellman expresan que el valor de un estado se puede descomponer en la recompensa inmediata más el valor descontado del siguiente estado.

Ecuación de Bellman para \(V^\pi\)

$$V^\pi(s) = \sum_{a} \pi(a|s) \sum_{s'} P(s'|s,a) \Big[ R(s,a,s') + \gamma \, V^\pi(s') \Big]$$

"El valor de un estado = recompensa esperada + γ × valor esperado del siguiente estado". Esta es una ecuación recursiva: \(V\) se define en términos de sí misma.

Ecuación de Bellman para \(Q^\pi\)

$$Q^\pi(s, a) = \sum_{s'} P(s'|s,a) \Big[ R(s,a,s') + \gamma \sum_{a'} \pi(a'|s') \, Q^\pi(s', a') \Big]$$

Ecuaciones de optimalidad de Bellman

Para la política óptima \(\pi^*\), las ecuaciones se simplifican porque la política greedy siempre elige la mejor acción:

$$V^*(s) = \max_a \sum_{s'} P(s'|s,a) \Big[ R(s,a,s') + \gamma \, V^*(s') \Big]$$
$$Q^*(s, a) = \sum_{s'} P(s'|s,a) \Big[ R(s,a,s') + \gamma \max_{a'} Q^*(s', a') \Big]$$

La ecuación de optimalidad de Bellman para \(Q^*\) es la base del Q-Learning. El \(\max_{a'}\) reemplaza la media ponderada por la política porque la política óptima siempre elige la mejor acción.

📝 Resumen de ecuaciones clave

EcuaciónFórmulaQué nos dice
Bellman \(V^\pi\) \(V^\pi(s) = \mathbb{E}_\pi[r + \gamma V^\pi(s')]\) Valor de un estado bajo política \(\pi\)
Bellman \(Q^\pi\) \(Q^\pi(s,a) = \mathbb{E}[r + \gamma \mathbb{E}_{a'}[Q^\pi(s',a')]]\) Valor de una acción bajo política \(\pi\)
Optimalidad \(V^*\) \(V^*(s) = \max_a \mathbb{E}[r + \gamma V^*(s')]\) Valor óptimo de un estado
Optimalidad \(Q^*\) \(Q^*(s,a) = \mathbb{E}[r + \gamma \max_{a'} Q^*(s',a')]\) Base del Q-Learning

Episodios, trayectorias y horizonte

📍
Trayectoria (τ)
Secuencia de transiciones: \(\tau = (s_0, a_0, r_1, s_1, a_1, r_2, \ldots)\). Una "partida" completa del agente.
🔄
Episodio
Una trayectoria que termina en un estado terminal. Ejemplo: una partida de ajedrez, un intento de un juego.
♾️
Tarea continua
Nunca termina. Ejemplo: control de un robot o un sistema de trading que funciona indefinidamente.
📏
Horizonte
Finito: el episodio tiene un número fijo de pasos. Infinito: tarea continua, se necesita \(\gamma < 1\).

Consideremos un episodio con recompensas \(r_1 = +1, r_2 = -1, r_3 = +5, r_4 = 0\) y \(\gamma = 0.9\):

$$G_0 = 1 + 0.9(-1) + 0.9^2(5) + 0.9^3(0) = 1 - 0.9 + 4.05 + 0 = 4.15$$
$$G_1 = -1 + 0.9(5) + 0.9^2(0) = -1 + 4.5 = 3.5$$

Observa cómo \(G_t\) cambia en cada paso porque las recompensas futuras son diferentes.

Q-Learning: el algoritmo fundamental

Q-Learning (Watkins, 1989) es el algoritmo más influyente de RL. Es un método off-policy y model-free que aprende la función de valor-acción óptima \(Q^*(s,a)\) directamente, sin necesidad de conocer el modelo del entorno.

🔑 La regla de actualización de Q-Learning:
$$Q(s_t, a_t) \leftarrow Q(s_t, a_t) + \alpha \Big[ \underbrace{r_{t+1} + \gamma \max_{a'} Q(s_{t+1}, a')}_{\text{TD target}} - \underbrace{Q(s_t, a_t)}_{\text{estimación actual}} \Big]$$

Donde \(\alpha \in (0,1]\) es la tasa de aprendizaje y el término entre corchetes es el error de diferencia temporal (TD error): la diferencia entre lo que el agente observó y lo que esperaba.

📖 Interpretación intuitiva

La regla dice: "ajusta tu estimación de \(Q(s,a)\) hacia la recompensa inmediata + mejor valor futuro estimado". Es como corregir tus predicciones cada vez que tienes nueva información.

  • \(\alpha\) grande: aprende rápido pero olvida rápido (inestable).
  • \(\alpha\) pequeño: aprende lento pero estable.
  • TD error = 0: la estimación es perfecta, no hay que ajustar.
  • TD error > 0: la realidad fue mejor de lo esperado → aumentar Q.
  • TD error < 0: la realidad fue peor de lo esperado → disminuir Q.

Propiedades del Q-Learning

🔓
Off-policy
Aprende la política óptima \(Q^*\) mientras sigue una política exploratoria (e.g., ε-greedy). Separa exploración de aprendizaje.
🎲
Model-free
No necesita conocer \(P(s'|s,a)\) ni \(R(s,a,s')\). Aprende solo con experiencias \((s,a,r,s')\).
Convergencia
Converge a \(Q^*\) bajo condiciones suaves: todos los pares \((s,a)\) visitados infinitas veces y \(\alpha\) decreciente.

Exploración: la estrategia ε-greedy

Si el agente siempre elige la acción con mayor Q-value (greedy), nunca descubrirá acciones potencialmente mejores. La estrategia ε-greedy resuelve el dilema exploración-explotación:

ε-greedy:
$$a_t = \begin{cases} \text{acción aleatoria} & \text{con probabilidad } \varepsilon \\ \arg\max_a Q(s_t, a) & \text{con probabilidad } 1 - \varepsilon \end{cases}$$

Con \(\varepsilon = 0.1\), el agente explora (acción aleatoria) el 10% del tiempo y explota (mejor acción conocida) el 90%. Normalmente se usa ε-decay: empezar con \(\varepsilon = 1.0\) (exploración pura) y decrecer gradualmente a \(\varepsilon_{\min} = 0.01\).

📉 Widget: Visualizador de ε-decay

Observa cómo \(\varepsilon\) decrece a lo largo de los episodios.

Q-Learning tabular

En la versión más simple, los valores \(Q(s,a)\) se almacenan en una tabla (Q-table) con una fila por cada estado y una columna por cada acción. El algoritmo completo es:

1
Inicializar \(Q(s,a) = 0\) para todos los pares \((s,a)\).
2
Para cada episodio: observar estado inicial \(s_0\).
3
Elegir acción \(a_t\) usando ε-greedy sobre \(Q(s_t, \cdot)\).
4
Ejecutar \(a_t\), observar \(r_{t+1}\) y \(s_{t+1}\).
5
Actualizar: \(Q(s_t, a_t) \leftarrow Q(s_t, a_t) + \alpha [r_{t+1} + \gamma \max_{a'} Q(s_{t+1}, a') - Q(s_t, a_t)]\)
6
\(s_t \leftarrow s_{t+1}\). Si no es terminal, volver al paso 3.
import numpy as np
import gymnasium as gym

# ═══════════════════════════════════════════════════
# Q-Learning tabular — FrozenLake
# ═══════════════════════════════════════════════════

env = gym.make("FrozenLake-v1", is_slippery=False)

# Hiperparámetros
alpha = 0.1          # Tasa de aprendizaje
gamma = 0.99         # Factor de descuento
epsilon = 1.0        # Exploración inicial
eps_min = 0.01       # Exploración mínima
eps_decay = 0.995    # Decay por episodio
n_episodes = 10000

# Inicializar Q-table: |S| × |A| = 16 × 4
n_states = env.observation_space.n     # 16
n_actions = env.action_space.n         # 4
Q = np.zeros((n_states, n_actions))

rewards_history = []

for episode in range(n_episodes):
    state, _ = env.reset()
    total_reward = 0
    done = False

    while not done:
        # ε-greedy
        if np.random.random() < epsilon:
            action = env.action_space.sample()   # Explorar
        else:
            action = np.argmax(Q[state])          # Explotar

        next_state, reward, terminated, truncated, _ = env.step(action)
        done = terminated or truncated

        # Actualización Q-Learning
        td_target = reward + gamma * np.max(Q[next_state]) * (1 - terminated)
        td_error = td_target - Q[state, action]
        Q[state, action] += alpha * td_error

        state = next_state
        total_reward += reward

    # Decay epsilon
    epsilon = max(eps_min, epsilon * eps_decay)
    rewards_history.append(total_reward)

# Mostrar Q-table final
print("Q-table aprendida:")
print(Q.round(2))
print(f"\nÉxito últimos 100 eps: {np.mean(rewards_history[-100:]):.0%}")

🗺️ Widget: Visualizador de Q-Table (GridWorld 4×4)

Simula Q-Learning en un GridWorld simple. Observa cómo la Q-table converge.

Limitaciones del Q-Learning tabular

El Q-Learning tabular funciona bien en entornos pequeños, pero se vuelve impracticable a medida que el espacio de estados crece:

EntornoEstadosAccionesEntradas Q-table¿Tabular viable?
FrozenLake 4×416464✅ Trivial
Taxi-v350063,000✅ Fácil
Blackjack~70021,400✅ Fácil
Ajedrez~10⁴⁷~35~10⁴⁸❌ Imposible
Atari (píxeles)~10⁶⁸⁰⁰⁰18~10⁶⁸⁰⁰¹❌ Imposible
Robótica (continuo)∞ (ℝⁿ)∞ (ℝᵐ)❌ Imposible

🚫 Los tres problemas del Q-Learning tabular

  • Maldición de la dimensionalidad: la Q-table crece exponencialmente con la dimensión del estado. Un juego de Atari con frames 210×160×3 tiene un espacio de estados astronómico.
  • Sin generalización: aprender \(Q(s_1, a)\) no dice nada sobre \(Q(s_2, a)\), incluso si \(s_1\) y \(s_2\) son casi idénticos. Cada entrada es independiente.
  • Estados continuos: con estados como posición \((x, y) \in \mathbb{R}^2\) o velocidades, no se puede indexar una tabla discreta.
💡 La solución: aproximar \(Q(s,a)\) con una red neuronal. En lugar de una tabla, usamos una red neuronal \(Q_\theta(s,a)\) que toma un estado como entrada y produce Q-values para cada acción. La red generaliza: estados similares producen valores similares. Esto es el Deep Q-Network (DQN).

SARSA: la alternativa on-policy

SARSA (State-Action-Reward-State-Action) es similar a Q-Learning, pero es on-policy: actualiza usando la acción que realmente tomará el agente (no la mejor posible).

Actualización SARSA:
$$Q(s_t, a_t) \leftarrow Q(s_t, a_t) + \alpha \Big[ r_{t+1} + \gamma \, Q(s_{t+1}, \underbrace{a_{t+1}}_{\text{acción real}}) - Q(s_t, a_t) \Big]$$

La diferencia con Q-Learning: usa \(Q(s_{t+1}, a_{t+1})\) en lugar de \(\max_{a'} Q(s_{t+1}, a')\). El agente actualiza usando la acción que realmente tomó, no la mejor posible.

PropiedadQ-LearningSARSA
TipoOff-policyOn-policy
Target\(r + \gamma \max_{a'} Q(s', a')\)\(r + \gamma Q(s', a')\) donde \(a'\) es la acción real
Política aprendidaÓptima \(Q^*\) (greedy)Q-value de la política seguida (ε-greedy)
ExploraciónSeparada del aprendizajeIntegrada en el aprendizaje
SeguridadMás arriesgado (asume acciones óptimas)Más conservador (cuenta el riesgo de explorar)
ConvergenciaA \(\pi^*\) (greedy óptima)A la Q-value de la política ε-greedy

En CliffWalking, el agente debe ir de un punto a otro bordeando un acantilado. Caer al acantilado da recompensa −100. El camino óptimo es bordear el acantilado (más rápido pero arriesgado con ε-greedy). El camino seguro es ir por arriba.

  • Q-Learning aprende el camino óptimo (bordeando el acantilado) porque su target usa \(\max\) y asume que seguirá la política greedy.
  • SARSA aprende el camino seguro (por arriba) porque sabe que con ε-greedy podría explorar y caer al acantilado.

SARSA es "más realista" porque tiene en cuenta que la política exploratoria puede causar errores.

De la Q-Table a la red neuronal (DQN)

La idea fundamental del Deep Q-Network (Mnih et al., 2013/2015) es reemplazar la Q-table por una red neuronal \(Q_\theta(s, a)\) que aproxima la función \(Q^*\). Esto permite manejar espacios de estados enormes (como píxeles) y generalizar a estados nunca vistos.

Arquitectura de un DQN Estado s Vectores de features, píxeles, observaciones... feature 1 feature 2 feature n dim = |S| Hidden 1 Linear(n, 128) + ReLU 128 neurons Hidden 2 Linear(128, 64) + ReLU 64 neurons Q-Values Linear(64, |A|) Sin activación Q(s, a₀) = 2.3 Q(s, a₁) = 7.1 ★ Q(s, a₂) = 1.8 Q(s, a₃) = 4.5 a₁ argmax Input: estado → Red neuronal → Output: Q-value para CADA acción → argmax → acción elegida

🏗️ Diseño de la red DQN

  • Input: el estado \(s\), codificado como un vector numérico (features del entorno, píxeles aplanados, frame stacking…).
  • Capas ocultas: Linear → ReLU (típicamente 2-3 capas de 64-512 neuronas). Para imágenes, CNN como extractor de features.
  • Output: un valor \(Q(s, a_i)\) por cada acción posible. Capa linear sin activación (los Q-values pueden ser negativos).
  • Dimensión output: \(|\mathcal{A}|\) — el número de acciones discretas posibles.
  • Acción elegida: \(\arg\max_a Q_\theta(s, a)\) (en explotación).

Codificación del estado

La forma en que se codifica el estado es crítica para el rendimiento del DQN. El estado debe contener toda la información relevante para tomar una decisión.

EntornoEstado crudoCodificación para DQNDimensión
CartPolePosición, velocidad, ángulo, vel. angularVector \([x, \dot{x}, \theta, \dot{\theta}]\)4
SnakePosición cabeza, comida, dirección, cuerpoVector de distancias/booleanos: [peligro_frente, peligro_der, ...]11-15
Atari BreakoutFrame RGB 210×160×34 frames grises 84×84 apilados4×84×84
Pong DRLPosiciones de palas y pelotaVector \([y_{pad}, y_{ball}, x_{ball}, v_{x}, v_{y}]\)5-8
TradingHistórico de preciosVentana de n precios normalizados + indicadores~50-200

Para juegos de Atari, el DQN original usa 4 frames consecutivos (en escala de grises, redimensionados a 84×84) como input. Esto permite a la red "ver" la velocidad y dirección de los objetos (un solo frame no tiene información de movimiento). La red usa capas convolucionales como feature extractor:

  • Conv2d(4, 32, kernel=8, stride=4) → ReLU
  • Conv2d(32, 64, kernel=4, stride=2) → ReLU
  • Conv2d(64, 64, kernel=3, stride=1) → ReLU
  • Flatten → Linear(3136, 512) → ReLU
  • Linear(512, |A|) → Q-values

Diseño de recompensas (Reward Shaping)

El diseño de la función de recompensa es una de las partes más críticas (y artesanales) del RL. Una mala recompensa lleva a comportamientos inesperados o a que el agente no aprenda.

EntornoRecompensa simpleRecompensa mejorada (shaped)
Snake +1 por comer, −1 por morir +10 comer, −10 morir, −0.01/paso, +0.1 acercarse a comida, −0.1 alejarse
Pong +1 punto, −1 punto rival Suficiente con reward esparsa; la pelota da feedback natural
Robótica (reach) +1 si toca el target −distancia al target (reward densa), +10 si lo toca
CartPole +1 por cada paso que sobrevive Funciona bien con reward esparsa
⚠️ Trampas comunes en el diseño de recompensas:
  • Reward hacking: el agente encuentra una forma de obtener recompensa que no era la intención (e.g., dar vueltas en círculos para acumular "bonos por moverse").
  • Reward esparsa: si la recompensa solo llega al final (e.g., ganar/perder), el agente tarda mucho en aprender porque la señal es muy débil.
  • Reward demasiado densa: si se da demasiada señal intermedia, el agente puede optimizar los "bonos" en lugar del objetivo real.
  • Escala incorrecta: si la recompensa por morir (−1) es mucho menor que la de comer (+10), el agente puede arriesgarse demasiado.

Experience Replay

En supervisado, los datos son i.i.d. (independientes e idénticamente distribuidos). En RL, las experiencias consecutivas \((s_t, a_t, r_{t+1}, s_{t+1})\) están altamente correlacionadas (el siguiente estado depende del anterior). Esto desestabiliza el entrenamiento de la red neuronal.

💾 Experience Replay Buffer

La solución es almacenar las experiencias en un buffer circular (replay buffer) y muestrear mini-batches aleatorios para entrenar. Esto rompe la correlación temporal y reutiliza experiencias pasadas.

1
El agente interactúa con el entorno y almacena \((s, a, r, s', done)\) en el buffer.
2
El buffer tiene capacidad máxima \(N\) (típicamente 100K–1M transiciones). Cuando se llena, las más antiguas se eliminan.
3
Para entrenar, se muestrea un mini-batch aleatorio de \(B\) transiciones (típicamente 32-256).
4
La red se entrena con ese batch (SGD/Adam). El muestreo aleatorio rompe la correlación.
Replay Buffer (capacidad N = 100K) (s,a,r,s')₁ (s,a,r,s')₂ (s,a,r,s')₃ ··· (s,a,r,s')ₙ ← nueva exp. muestreo aleatorio Mini-batch (B=32) 🧠 Q-Network SGD update

Target Network y Soft Update

Un segundo problema de inestabilidad: al entrenar la red, el target \(r + \gamma \max_{a'} Q_\theta(s', a')\) también cambia porque depende de los mismos pesos \(\theta\) que estamos actualizando. Es como perseguir un objetivo que se mueve — inestable.

🎯 Solución: Target Network \(Q_{\theta^-}\)

Se mantiene una segunda red (target network) con pesos \(\theta^-\) que se actualizan lentamente. El target se calcula con esta red "congelada":

$$y_i = r_i + \gamma \max_{a'} Q_{\theta^-}(s'_i, a') \cdot (1 - \text{done}_i)$$

Los pesos de la target network \(\theta^-\) se actualizan periódicamente mediante soft update o hard copy.

Soft Update (Polyak averaging)

$$\theta^- \leftarrow \tau \, \theta + (1 - \tau) \, \theta^-$$

Con \(\tau\) pequeño (típicamente \(\tau = 0.005\)), la target network se mueve muy lentamente hacia la red principal. Esto estabiliza el entrenamiento porque el target cambia gradualmente.

Hard Update

Alternativa: copiar \(\theta^- \leftarrow \theta\) cada \(C\) pasos (e.g., \(C=1000\)). El DQN original usaba hard update cada 10,000 frames.

🔬 Widget: Simulador de entrenamiento DQN

Visualiza cómo los componentes del DQN interactúan durante el entrenamiento.

La función de pérdida del DQN

El DQN se entrena como un problema de regresión: queremos que la predicción \(Q_\theta(s, a)\) se acerque al TD target.

Loss del DQN (Huber Loss / MSE):
$$\mathcal{L}(\theta) = \frac{1}{B} \sum_{i=1}^{B} \ell\Big( \underbrace{Q_\theta(s_i, a_i)}_{\text{predicción}} - \underbrace{\big(r_i + \gamma \max_{a'} Q_{\theta^-}(s'_i, a') \cdot (1 - d_i)\big)}_{\text{TD target (detached)}} \Big)$$

Donde \(\ell\) es MSE o Huber loss, y \(d_i\) es 1 si \(s'_i\) es terminal. El TD target se calcula con la target network y se detiene el gradiente (no se propaga backprop al target).

⚙️ ¿Por qué Huber Loss en lugar de MSE?

La Huber Loss (smooth L1 loss) es más robusta a outliers que MSE. En RL, los TD errors pueden ser muy grandes al inicio del entrenamiento. Huber Loss se comporta como MSE para errores pequeños y como MAE para errores grandes, evitando gradientes explosivos.

$$\ell_\delta(x) = \begin{cases} \frac{1}{2}x^2 & \text{si } |x| \leq \delta \\ \delta(|x| - \frac{1}{2}\delta) & \text{si } |x| > \delta \end{cases}$$

El algoritmo DQN completo

1
Inicializar Q-network \(Q_\theta\) con pesos aleatorios.
2
Inicializar target network \(Q_{\theta^-}\) con \(\theta^- = \theta\).
3
Inicializar replay buffer \(\mathcal{D}\) vacío (capacidad \(N\)).
4
Para cada episodio: reset entorno, obtener \(s_0\).
5
Elegir acción \(a_t\) con ε-greedy sobre \(Q_\theta(s_t, \cdot)\).
6
Ejecutar \(a_t\), observar \(r_{t+1}, s_{t+1}, done\).
7
Almacenar \((s_t, a_t, r_{t+1}, s_{t+1}, done)\) en \(\mathcal{D}\).
8
Muestrear mini-batch de \(B\) transiciones de \(\mathcal{D}\).
9
Calcular TD targets: \(y_i = r_i + \gamma \max_{a'} Q_{\theta^-}(s'_i, a') \cdot (1 - d_i)\).
10
Calcular loss: \(\mathcal{L} = \frac{1}{B} \sum_i (Q_\theta(s_i, a_i) - y_i)^2\).
11
Backprop + optimizer step para actualizar \(\theta\).
12
Soft update: \(\theta^- \leftarrow \tau\theta + (1-\tau)\theta^-\).
13
Decay ε. Repetir desde paso 5 hasta terminal.
import torch
import torch.nn as nn
import torch.optim as optim
import numpy as np
import gymnasium as gym
from collections import deque
import random

# ═══════════════════════════════════════════════════
# 1. Red neuronal Q-Network
# ═══════════════════════════════════════════════════
class QNetwork(nn.Module):
    def __init__(self, state_dim, action_dim, hidden=128):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(state_dim, hidden),
            nn.ReLU(),
            nn.Linear(hidden, hidden),
            nn.ReLU(),
            nn.Linear(hidden, action_dim)  # Sin activación
        )

    def forward(self, x):
        return self.net(x)

# ═══════════════════════════════════════════════════
# 2. Replay Buffer
# ═══════════════════════════════════════════════════
class ReplayBuffer:
    def __init__(self, capacity=100_000):
        self.buffer = deque(maxlen=capacity)

    def push(self, state, action, reward, next_state, done):
        self.buffer.append((state, action, reward, next_state, done))

    def sample(self, batch_size):
        batch = random.sample(self.buffer, batch_size)
        states, actions, rewards, next_states, dones = zip(*batch)
        return (
            torch.FloatTensor(np.array(states)),
            torch.LongTensor(actions),
            torch.FloatTensor(rewards),
            torch.FloatTensor(np.array(next_states)),
            torch.FloatTensor(dones),
        )

    def __len__(self):
        return len(self.buffer)

# ═══════════════════════════════════════════════════
# 3. Agente DQN
# ═══════════════════════════════════════════════════
class DQNAgent:
    def __init__(self, state_dim, action_dim):
        self.action_dim = action_dim
        self.gamma = 0.99
        self.tau = 0.005
        self.epsilon = 1.0
        self.eps_min = 0.01
        self.eps_decay = 0.995
        self.batch_size = 64

        # Q-network y target network
        self.q_net = QNetwork(state_dim, action_dim)
        self.target_net = QNetwork(state_dim, action_dim)
        self.target_net.load_state_dict(self.q_net.state_dict())

        self.optimizer = optim.Adam(self.q_net.parameters(), lr=1e-3)
        self.buffer = ReplayBuffer()

    def select_action(self, state):
        if random.random() < self.epsilon:
            return random.randrange(self.action_dim)
        with torch.no_grad():
            q_values = self.q_net(torch.FloatTensor(state).unsqueeze(0))
            return q_values.argmax(dim=1).item()

    def train_step(self):
        if len(self.buffer) < self.batch_size:
            return None

        states, actions, rewards, next_states, dones = \
            self.buffer.sample(self.batch_size)

        # Q-values actuales para las acciones tomadas
        q_values = self.q_net(states).gather(1, actions.unsqueeze(1)).squeeze()

        # TD target con target network (sin gradiente)
        with torch.no_grad():
            next_q = self.target_net(next_states).max(dim=1)[0]
            targets = rewards + self.gamma * next_q * (1 - dones)

        # Loss y backprop
        loss = nn.SmoothL1Loss()(q_values, targets)  # Huber Loss
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()

        # Soft update de target network
        for p, tp in zip(self.q_net.parameters(),
                         self.target_net.parameters()):
            tp.data.copy_(self.tau * p.data + (1 - self.tau) * tp.data)

        return loss.item()

    def decay_epsilon(self):
        self.epsilon = max(self.eps_min, self.epsilon * self.eps_decay)

# ═══════════════════════════════════════════════════
# 4. Training loop
# ═══════════════════════════════════════════════════
env = gym.make("CartPole-v1")
agent = DQNAgent(state_dim=4, action_dim=2)

for episode in range(500):
    state, _ = env.reset()
    total_reward = 0

    while True:
        action = agent.select_action(state)
        next_state, reward, term, trunc, _ = env.step(action)
        done = term or trunc

        agent.buffer.push(state, action, reward, next_state, float(done))
        loss = agent.train_step()

        state = next_state
        total_reward += reward

        if done:
            break

    agent.decay_epsilon()

    if (episode + 1) % 50 == 0:
        print(f"Ep {episode+1} | Reward: {total_reward:.0f} | "
              f"ε: {agent.epsilon:.3f} | Buffer: {len(agent.buffer)}")
import tensorflow as tf
import numpy as np
import gymnasium as gym
from collections import deque
import random

# ═══════════════════════════════════════════════════
# DQN en TensorFlow/Keras — CartPole
# ═══════════════════════════════════════════════════

def build_q_network(state_dim, action_dim, hidden=128):
    return tf.keras.Sequential([
        tf.keras.layers.Dense(hidden, activation='relu', input_shape=(state_dim,)),
        tf.keras.layers.Dense(hidden, activation='relu'),
        tf.keras.layers.Dense(action_dim)  # Sin activación
    ])

# Crear redes
q_net = build_q_network(4, 2)
target_net = build_q_network(4, 2)
target_net.set_weights(q_net.get_weights())

optimizer = tf.keras.optimizers.Adam(1e-3)
buffer = deque(maxlen=100_000)
gamma, tau, batch_size = 0.99, 0.005, 64
epsilon, eps_min, eps_decay = 1.0, 0.01, 0.995

@tf.function
def train_step(states, actions, rewards, next_states, dones):
    # TD target
    next_q = tf.reduce_max(target_net(next_states), axis=1)
    targets = rewards + gamma * next_q * (1.0 - dones)

    with tf.GradientTape() as tape:
        q_values = q_net(states)
        indices = tf.stack([tf.range(batch_size), actions], axis=1)
        q_selected = tf.gather_nd(q_values, indices)
        loss = tf.keras.losses.Huber()(targets, q_selected)

    grads = tape.gradient(loss, q_net.trainable_variables)
    optimizer.apply_gradients(zip(grads, q_net.trainable_variables))

    # Soft update
    for w, tw in zip(q_net.weights, target_net.weights):
        tw.assign(tau * w + (1 - tau) * tw)

    return loss

env = gym.make("CartPole-v1")
for episode in range(500):
    state, _ = env.reset()
    total_reward = 0

    while True:
        if random.random() < epsilon:
            action = env.action_space.sample()
        else:
            q = q_net(tf.expand_dims(state, 0))
            action = int(tf.argmax(q, axis=1))

        ns, r, term, trunc, _ = env.step(action)
        buffer.append((state, action, r, ns, float(term or trunc)))
        state = ns
        total_reward += r

        if len(buffer) >= batch_size:
            batch = random.sample(buffer, batch_size)
            s, a, rw, ns2, d = map(np.array, zip(*batch))
            train_step(
                tf.constant(s, dtype=tf.float32),
                tf.constant(a, dtype=tf.int32),
                tf.constant(rw, dtype=tf.float32),
                tf.constant(ns2, dtype=tf.float32),
                tf.constant(d, dtype=tf.float32),
            )

        if term or trunc:
            break

    epsilon = max(eps_min, epsilon * eps_decay)

Variantes y mejoras del DQN

VarianteProblema que resuelveIdea clave
Double DQN Sobreestimación de Q-values Selecciona acción con \(Q_\theta\), evalúa con \(Q_{\theta^-}\): \(y = r + \gamma Q_{\theta^-}(s', \arg\max_{a'} Q_\theta(s', a'))\)
Dueling DQN No todos los estados necesitan evaluar acciones Separa en \(V(s)\) + Advantage \(A(s,a)\): \(Q(s,a) = V(s) + A(s,a) - \bar{A}\)
Prioritized Replay No todas las experiencias son igual de informativas Muestrea experiencias con mayor TD error con más frecuencia.
Noisy Nets ε-greedy es ineficiente para explorar Añade ruido aprendible a los pesos de la red para explorar.
Rainbow Combinar todas las mejoras Double + Dueling + Prioritized + Noisy + Multi-step + Distributional.

Rainbow (Hessel et al., 2018) combina 6 mejoras independientes y demuestra que juntas superan a cada una por separado. Es considerado el estado del arte de los métodos DQN.

  • Double DQN: reduce sobreestimación.
  • Prioritized Replay: muestrea experiencias útiles.
  • Dueling architecture: separa valor de ventaja.
  • Multi-step returns: usa n-step returns en lugar de 1-step.
  • Distributional RL: predice la distribución del retorno, no solo la media.
  • Noisy Nets: exploración parametrizada.

Hessel, M. et al. (2018). "Rainbow: Combining Improvements in Deep Reinforcement Learning". AAAI.

Policy Gradient Methods

Los métodos value-based (como DQN) aprenden \(Q(s,a)\) y derivan la política indirectamente (\(\arg\max\)). Los métodos policy-based aprenden la política \(\pi_\theta(a|s)\) directamente, parametrizada por una red neuronal con pesos \(\theta\).

🎯 ¿Por qué policy gradients?

  • Acciones continuas: DQN solo funciona con acciones discretas (\(\arg\max\) no tiene sentido en \(\mathbb{R}^n\)). Policy gradients manejan acciones continuas naturalmente.
  • Políticas estocásticas: a veces la política óptima es aleatoria (e.g., piedra-papel-tijeras). Policy gradients lo modelan directamente.
  • Convergencia: cambios suaves en \(\theta\) → cambios suaves en la política (no saltos bruscos como en value-based).

El teorema del Policy Gradient

Teorema (Sutton et al., 1999):
$$\nabla_\theta J(\theta) = \mathbb{E}_{\pi_\theta} \left[ \sum_{t=0}^{T} \nabla_\theta \log \pi_\theta(a_t | s_t) \cdot G_t \right]$$

La dirección de mejora de la política es: aumentar la probabilidad de acciones que llevaron a retornos altos y disminuir la de acciones con retornos bajos. Es como decir: "haz más de lo que funcionó y menos de lo que no".

REINFORCE (Monte Carlo Policy Gradient)

El algoritmo más simple de policy gradient. Recoge un episodio completo, calcula los retornos \(G_t\) para cada paso, y actualiza:

$$\theta \leftarrow \theta + \alpha \sum_{t} \nabla_\theta \log \pi_\theta(a_t | s_t) \cdot (G_t - b)$$

Donde \(b\) es un baseline (típicamente \(V(s_t)\)) que reduce la varianza del estimador sin sesgar el gradiente.

import torch
import torch.nn as nn
import torch.optim as optim
import gymnasium as gym

class PolicyNetwork(nn.Module):
    def __init__(self, state_dim, action_dim):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(state_dim, 128), nn.ReLU(),
            nn.Linear(128, action_dim),
            nn.Softmax(dim=-1)  # Distribución sobre acciones
        )

    def forward(self, x):
        return self.net(x)

env = gym.make("CartPole-v1")
policy = PolicyNetwork(4, 2)
optimizer = optim.Adam(policy.parameters(), lr=1e-3)

for episode in range(1000):
    states, actions, rewards = [], [], []
    state, _ = env.reset()

    # 1. Recoger episodio completo
    while True:
        probs = policy(torch.FloatTensor(state))
        dist = torch.distributions.Categorical(probs)
        action = dist.sample()

        next_state, reward, term, trunc, _ = env.step(action.item())
        states.append(state)
        actions.append(action)
        rewards.append(reward)

        state = next_state
        if term or trunc:
            break

    # 2. Calcular retornos descontados
    G, returns = 0, []
    for r in reversed(rewards):
        G = r + 0.99 * G
        returns.insert(0, G)
    returns = torch.FloatTensor(returns)
    returns = (returns - returns.mean()) / (returns.std() + 1e-8)  # Baseline

    # 3. Policy gradient update
    loss = 0
    for s, a, G_t in zip(states, actions, returns):
        probs = policy(torch.FloatTensor(s))
        log_prob = torch.log(probs[a])
        loss -= log_prob * G_t  # Negativo porque optimizer minimiza

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

Actor-Critic: lo mejor de ambos mundos

Los métodos Actor-Critic combinan value-based y policy-based: un actor (red de política \(\pi_\theta\)) decide qué hacer, y un critic (red de valor \(V_\phi\)) evalúa cuán buena fue esa decisión.

Estado s observación 🎭 Actor π_θ(a|s) → distribución acciones 🧑‍⚖️ Critic V_φ(s) → valor del estado Acción a muestreada de π Advantage A = r + γV(s') − V(s) El Critic guía al Actor: "la acción fue mejor/peor de lo esperado"
Advantage Function:
$$A(s_t, a_t) = \underbrace{r_{t+1} + \gamma V_\phi(s_{t+1})}_{\text{TD target}} - \underbrace{V_\phi(s_t)}_{\text{baseline}}$$

El advantage mide cuánto mejor fue la acción \(a_t\) comparada con el promedio. Si \(A > 0\): la acción fue mejor de lo esperado → aumentar su probabilidad. Si \(A < 0\): peor de lo esperado → disminuir su probabilidad.

VarianteAñoIdea claveUso
A2C2016Advantage Actor-Critic síncrono. Múltiples workers en paralelo.Entornos simples, educativo
A3C2016Asíncrono: cada worker actualiza gradientes independientemente.Atari, históricamente importante
GAE2016Generalized Advantage Estimation: combina n-step returns con λ.Reduce varianza del advantage

PPO — Proximal Policy Optimization

PPO (Schulman et al., 2017) es el algoritmo de RL más usado en la práctica. Es un actor-critic que resuelve el problema de inestabilidad de los policy gradients limitando cuánto puede cambiar la política en cada paso.

Objetivo de PPO (clipped):
$$\mathcal{L}^{CLIP}(\theta) = \mathbb{E}_t \left[ \min\left( r_t(\theta) \hat{A}_t, \; \text{clip}\big(r_t(\theta), 1-\epsilon, 1+\epsilon\big) \hat{A}_t \right) \right]$$

Donde \(r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)}\) es el ratio de probabilidades y \(\epsilon = 0.2\) es el clip range. El clipping limita los cambios bruscos en la política, asegurando estabilidad sin necesitar la complejidad de TRPO.

🏆
El estándar
PPO es el algoritmo por defecto en RL moderno. Simple, estable, y funciona bien en la mayoría de entornos.
💬
RLHF
ChatGPT y otros LLMs usan PPO para alinear el modelo con preferencias humanas (RLHF).
🤖
Robótica
PPO es popular en robótica por su estabilidad con acciones continuas y sim-to-real transfer.

Schulman, J. et al. (2017). "Proximal Policy Optimization Algorithms". arXiv:1707.06347.

Otros algoritmos importantes

SAC (Haarnoja et al., 2018) maximiza no solo la recompensa sino también la entropía de la política, incentivando la exploración:

$$J(\pi) = \sum_t \mathbb{E}\left[ r(s_t, a_t) + \alpha \, \mathcal{H}(\pi(\cdot | s_t)) \right]$$
  • Off-policy: usa replay buffer (más eficiente en datos que PPO).
  • Acciones continuas: diseñado para espacios de acción continuos.
  • Robusto: la entropía previene convergencia prematura.
  • Uso: robótica, locomoción, control continuo.

Haarnoja, T. et al. (2018). "Soft Actor-Critic: Off-Policy Maximum Entropy Deep RL". ICML.

DDPG (Lillicrap et al., 2016) extiende DQN a acciones continuas usando una red actor que produce directamente la acción (sin softmax ni muestreo):

  • Actor: \(\mu_\theta(s) \to a \in \mathbb{R}^n\) (acción determinista).
  • Critic: \(Q_\phi(s, a) \to \mathbb{R}\) (evalúa par estado-acción).
  • Off-policy con replay buffer y target networks para ambas redes.
  • Exploración con ruido (Ornstein-Uhlenbeck o Gaussiano).

Lillicrap, T. et al. (2016). "Continuous control with deep reinforcement learning". ICLR.

AlphaGo (Silver et al., 2016) y AlphaZero (2017) combinan RL con Monte Carlo Tree Search:

  • Red neuronal: recibe el tablero → predice política \(\pi(a|s)\) + valor \(V(s)\).
  • MCTS: usa la red para guiar la búsqueda del árbol de jugadas.
  • Self-play: el agente juega contra sí mismo y mejora iterativamente.
  • AlphaZero: aprende desde cero (sin datos humanos) → superhuman en Go, ajedrez y shogi.
  • MuZero: ni siquiera necesita conocer las reglas del juego — aprende el modelo del entorno.

Silver, D. et al. (2017). "Mastering the Game of Go without Human Knowledge". Nature.

Comparativa de algoritmos de RL

AlgoritmoTipoOn/Off policyAccionesUso principal
Q-LearningValueOffDiscretasEducativo, entornos tabulares
DQNValueOffDiscretasAtari, juegos discretos
REINFORCEPolicyOnAmbasEducativo, simple
A2C/A3CActor-CriticOnAmbasParalelizable, general
PPOActor-CriticOnAmbasLLMs (RLHF), robótica, general
SACActor-CriticOffContinuasRobótica, control
DDPGActor-CriticOffContinuasControl continuo
TD3Actor-CriticOffContinuasMejora de DDPG
AlphaZeroModel-based + MCTSOn (self-play)DiscretasJuegos de mesa

RLHF — RL para alinear modelos de lenguaje

Reinforcement Learning from Human Feedback (RLHF) es la técnica que hizo posible ChatGPT. Usa RL (específicamente PPO) para alinear un LLM con las preferencias humanas.

1
Pre-training: entrenar un LLM base con next-token prediction en texto masivo (GPT base).
2
SFT (Supervised Fine-Tuning): fine-tune con datos curados de instrucción-respuesta (humanos escriben respuestas ideales).
3
Reward Model: humanos comparan pares de respuestas y eligen la mejor. Se entrena un modelo de recompensa que predice la preferencia humana.
4
RL (PPO): el LLM genera respuestas (acciones), el reward model las evalúa (recompensa), y PPO optimiza la política del LLM para maximizar la recompensa.
1. Pre-training LLM base next-token pred. 2. SFT Fine-tune con instrucciones 3. Reward Model Humanos comparan resp. A vs B → RM 4. PPO / RLHF LLM genera resp. RM da reward → PPO RL loop: generar → evaluar → optimizar Alternativa moderna: DPO (Direct Preference Optimization) — sin RL explícito, optimiza preferencias directamente

💬 El RL en el contexto de LLMs

Concepto RLEn RLHF para LLMs
AgenteEl LLM (genera tokens)
EntornoEl prompt + contexto de conversación
EstadoPrompt + tokens generados hasta ahora
AcciónSiguiente token a generar
Política\(\pi_\theta(token | prompt, tokens_{prev})\) = distribución sobre vocabulario
RecompensaScore del Reward Model al final de la respuesta completa
EpisodioGenerar una respuesta completa a un prompt

Ouyang, L. et al. (2022). "Training language models to follow instructions with human feedback". NeurIPS.

RL en sistemas modernos

🚗
Conducción autónoma
Waymo, Tesla, Wayve usan RL para planificación de trayectorias, toma de decisiones en intersecciones, y adaptación a condiciones impredecibles. Sim-to-real con simuladores como CARLA. El estado incluye sensores LiDAR, cámaras, radar, posición GPS.
🤖
Robótica
Boston Dynamics, Google DeepMind usan RL para locomoción (caminar, correr, saltar) y manipulación (agarrar objetos). SAC y PPO son los algoritmos más usados. Sim-to-real transfer es clave.
🧪
Descubrimiento de fármacos
El agente construye moléculas átomo a átomo. La recompensa es la afinidad de unión, solubilidad, toxicidad. Usado en Insilico Medicine, Recursion Pharma.
Control de fusión nuclear
DeepMind + EPFL (2022): RL para controlar el plasma en un tokamak (reactor de fusión). 19 bobinas magnéticas como acciones, forma del plasma como estado.
📡
Redes y sistemas
Google: RL para optimizar el compilador de chips (TPU placement), gestión de tráfico de red, scheduling de jobs en data centers, recomendaciones en YouTube.
🧮
Matemáticas
AlphaTensor (2022): RL descubrió nuevos algoritmos de multiplicación de matrices más rápidos que los conocidos desde 1969 (algoritmo de Strassen).

Retos abiertos y futuro del RL

RetoDescripciónEnfoques actuales
Sample efficiencyRL necesita millones de interacciones. Inviable en el mundo real.Model-based RL, offline RL, sim-to-real.
Reward designDiseñar la recompensa correcta es difícil y propenso a hacking.RLHF, reward learning, inverse RL.
GeneralizaciónEl agente aprende para un entorno específico. No generaliza.Meta-RL, foundation models para RL, procedural generation.
SeguridadEl agente puede descubrir comportamientos peligrosos.Safe RL, constrained RL, alignment research.
Multi-agentMúltiples agentes interactuando. Equilibrios, cooperación, competición.MARL, game theory + RL, emergent communication.
Long-horizonTareas con miles de pasos y reward muy esparsa.Hierarchical RL, intrinsic motivation, curriculum learning.

🧩 Widget: Selector de algoritmo de RL

Recomienda el algoritmo más adecuado según tu problema.

Papers y recursos fundamentales

📚 Papers esenciales de RL