Introducción: Aprender de las Recompensas
El Reinforcement Learning (RL) es un paradigma de aprendizaje fundamentalmente diferente del supervised y unsupervised learning. En lugar de aprender de datos etiquetados, un agente interactúa con un entorno, ejecuta acciones y recibe recompensas. El objetivo es aprender una policy (estrategia) que maximice la recompensa acumulada en el tiempo.
Desde el juego de ajedrez (AlphaZero) hasta la robótica (manipulación de objetos), del trading algorítmico a la conducción autónoma, el reinforcement learning está en la base de algunas de las aplicaciones de IA más impresionantes. En este artículo exploraremos los conceptos fundamentales, desde el Q-Learning clásico hasta los algoritmos de deep RL modernos como DQN y PPO.
Lo Que Aprenderás
- Markov Decision Process (MDP): estado, acción, recompensa, transición
- Q-Learning: la tabla de valores acción-estado
- Deep Q-Network (DQN): aproximar Q con redes neuronales
- Policy Gradient: optimizar directamente la policy
- Proximal Policy Optimization (PPO): estabilidad y rendimiento
- Exploration vs exploitation: equilibrar descubrimiento y aprovechamiento
- Implementación práctica con Gymnasium (OpenAI)
Markov Decision Process (MDP)
El framework formal del RL es el Markov Decision Process, definido por cuatro componentes:
- Estados (S): las posibles situaciones en las que el agente puede encontrarse (ej. posición en una cuadrícula, frame de un juego)
- Acciones (A): los movimientos disponibles en cada estado (ej. arriba, abajo, izquierda, derecha)
- Recompensas (R): el feedback numérico recibido tras cada acción (ej. +1 por ganar, -1 por perder)
- Transiciones (P): la probabilidad de pasar a un estado siguiente dada una acción. La propiedad de Markov establece que el futuro depende solo del estado actual, no de la historia
El agente busca una policy óptima que maximice la suma descontada de las recompensas futuras. El discount factor gamma (entre 0 y 1) equilibra recompensas inmediatas vs futuras: gamma cercano a 0 hace al agente miope, cercano a 1 lo hace previsor.
Q-Learning: Valores Acción-Estado
El Q-Learning es un algoritmo off-policy que aprende la función Q(s, a): el valor esperado de la recompensa acumulada eligiendo la acción a en el estado s y siguiendo la policy óptima a partir de ese momento. La regla de actualización es:
Q(s, a) = Q(s, a) + alpha * [R + gamma * max_a'(Q(s', a')) - Q(s, a)]
import numpy as np
import gymnasium as gym
class QLearningAgent:
def __init__(self, n_states, n_actions, lr=0.1, gamma=0.99, epsilon=1.0):
self.q_table = np.zeros((n_states, n_actions))
self.lr = lr
self.gamma = gamma
self.epsilon = epsilon
self.epsilon_min = 0.01
self.epsilon_decay = 0.995
def choose_action(self, state):
"""Epsilon-greedy: explora con prob epsilon, explota en caso contrario"""
if np.random.random() < self.epsilon:
return np.random.randint(self.q_table.shape[1])
return np.argmax(self.q_table[state])
def learn(self, state, action, reward, next_state, done):
"""Actualización Q-table"""
target = reward
if not done:
target += self.gamma * np.max(self.q_table[next_state])
self.q_table[state, action] += self.lr * (target - self.q_table[state, action])
# Decay epsilon
self.epsilon = max(self.epsilon_min, self.epsilon * self.epsilon_decay)
# Entrenamiento en FrozenLake
env = gym.make('FrozenLake-v1', is_slippery=False)
agent = QLearningAgent(n_states=16, n_actions=4)
for episode in range(5000):
state, _ = env.reset()
total_reward = 0
done = False
while not done:
action = agent.choose_action(state)
next_state, reward, terminated, truncated, _ = env.step(action)
done = terminated or truncated
agent.learn(state, action, reward, next_state, done)
state = next_state
total_reward += reward
if episode % 1000 == 0:
print(f"Episode {episode}, Reward: {total_reward}, "
f"Epsilon: {agent.epsilon:.3f}")
Exploration vs Exploitation
El dilema fundamental del RL: el agente debe explorar (probar acciones nuevas para descubrir mejores recompensas) y explotar (usar el conocimiento actual para maximizar la recompensa). La estrategia epsilon-greedy equilibra ambos aspectos: con probabilidad epsilon elige una acción aleatoria, en caso contrario la mejor acción conocida. Epsilon se reduce gradualmente durante el entrenamiento.
Deep Q-Network (DQN)
El Q-Learning con tablas funciona solo para espacios de estados pequeños y discretos. Para entornos complejos (imágenes, estados continuos), la Deep Q-Network (DQN) sustituye la Q-table con una red neuronal que aproxima la función Q. Dos innovaciones clave estabilizan el entrenamiento:
- Experience Replay: las transiciones se almacenan en un buffer y se muestrean aleatoriamente para el entrenamiento, rompiendo la correlación temporal entre muestras consecutivas
- Target Network: una copia separada de la red, actualizada periódicamente, calcula los Q-values objetivo, estabilizando el aprendizaje
import torch
import torch.nn as nn
from collections import deque
import random
class DQN(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, 128),
nn.ReLU(),
nn.Linear(128, action_dim)
)
def forward(self, x):
return self.net(x)
class DQNAgent:
def __init__(self, state_dim, action_dim, lr=1e-3, gamma=0.99):
self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
self.q_net = DQN(state_dim, action_dim).to(self.device)
self.target_net = DQN(state_dim, action_dim).to(self.device)
self.target_net.load_state_dict(self.q_net.state_dict())
self.optimizer = torch.optim.Adam(self.q_net.parameters(), lr=lr)
self.memory = deque(maxlen=100000)
self.gamma = gamma
self.batch_size = 64
def store(self, state, action, reward, next_state, done):
self.memory.append((state, action, reward, next_state, done))
def learn(self):
if len(self.memory) < self.batch_size:
return
batch = random.sample(self.memory, self.batch_size)
states, actions, rewards, next_states, dones = zip(*batch)
states = torch.FloatTensor(states).to(self.device)
actions = torch.LongTensor(actions).to(self.device)
rewards = torch.FloatTensor(rewards).to(self.device)
next_states = torch.FloatTensor(next_states).to(self.device)
dones = torch.FloatTensor(dones).to(self.device)
q_values = self.q_net(states).gather(1, actions.unsqueeze(1))
with torch.no_grad():
next_q = self.target_net(next_states).max(1)[0]
targets = rewards + (1 - dones) * self.gamma * next_q
loss = nn.functional.mse_loss(q_values.squeeze(), targets)
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
def update_target(self):
self.target_net.load_state_dict(self.q_net.state_dict())
Policy Gradient y Actor-Critic
Los métodos Policy Gradient optimizan directamente la policy sin pasar por la función Q. El teorema del policy gradient establece que el gradiente de la recompensa esperada respecto a los parámetros de la policy es proporcional a la recompensa ponderada por la log-probabilidad de las acciones.
Actor-Critic
La arquitectura Actor-Critic combina ambos enfoques: el Actor (policy network) elige las acciones, el Critic (value network) estima cuán bueno es el estado actual. El Critic reduce la varianza de las actualizaciones del Actor, haciendo el entrenamiento más estable.
PPO: El Estándar Industrial
Proximal Policy Optimization (PPO), desarrollado por OpenAI, es el algoritmo de RL más usado en la práctica. Su innovación clave es el clipped objective: limita cuánto la nueva policy puede alejarse de la antigua en cada actualización, previniendo cambios demasiado drásticos que desestabilizarían el entrenamiento.
PPO está en la base de muchos éxitos: InstructGPT y RLHF (alineación de LLM), OpenAI Five (Dota 2), entrenamiento de agentes robóticos y muchos otros.
Próximos Pasos en la Serie
- En el próximo artículo exploraremos el Transfer Learning Avanzado con BERT, GPT y Hugging Face
- Veremos fine-tuning, prompt engineering y RAG (Retrieval-Augmented Generation)
- Compararemos modelos open-source: Llama, Mistral, Falcon







