Curso

El aprendizaje por refuerzo (RL) es la parte del ecosistema del aprendizaje automático donde un agente aprende interactuando con el entorno para encontrar la estrategia óptima que le permita alcanzar sus objetivos. Es bastante distinto de los algoritmos supervisados, que requieren ingerir y procesar datos etiquetados. El aprendizaje por refuerzo no necesita datos de entrenamiento previos: aprende del propio entorno y de su sistema de recompensas para tomar mejores decisiones.
Por ejemplo, en el videojuego de Mario, si el personaje realiza una acción aleatoria (p. ej., moverse a la izquierda), en función de esa acción puede recibir una recompensa. Tras actuar, el agente (Mario) pasa a un nuevo estado, y el proceso se repite hasta que llega al final de la fase o muere.
Este episodio se repite muchas veces hasta que Mario aprende a moverse por el entorno maximizando las recompensas.

Imagen del autor
Podemos descomponer el aprendizaje por refuerzo en cinco pasos sencillos:
- El agente parte de un estado cero en un entorno.
- Toma una acción siguiendo una estrategia concreta.
- Recibe una recompensa o un castigo según esa acción.
- Aprende de los movimientos previos y va optimizando la estrategia.
- El proceso se repite hasta encontrar una estrategia óptima.
Descubre más en nuestro tutorial, una introducción al aprendizaje por refuerzo. Verás con ejemplos de código cómo funciona el aprendizaje por refuerzo.
En este tutorial, aprenderás qué es Q-learning y por qué necesitamos Deep Q-learning. Además, crearemos y entrenaremos algoritmos de Q-learning desde cero usando Numpy y Gymnasium.
Nota: si estás empezando en machine learning, te recomendamos el itinerario Machine Learning Scientist with Python para entender mejor el aprendizaje por refuerzo y Q-learning.
Conviértete en un Científico ML
¿Qué es Q-learning?
Q-learning es un algoritmo sin modelo, basado en valores y off-policy que encuentra la mejor secuencia de acciones según el estado actual del agente. La "Q" viene de quality (calidad). La calidad representa lo valiosa que es una acción para maximizar recompensas futuras.
Los algoritmos con modelo usan funciones de transición y recompensa para estimar la política óptima y construir el modelo. En cambio, los algoritmos sin modelo aprenden las consecuencias de sus acciones a partir de la experiencia, sin funciones explícitas de transición y recompensa.
El método basado en valores entrena una función de valor para aprender qué estados son más valiosos y, a partir de ahí, decidir. Por otro lado, los métodos basados en políticas entrenan directamente la política para aprender qué acción tomar en cada estado.
En los métodos off-policy, el algoritmo evalúa y actualiza una política distinta de la que usa para actuar. Por el contrario, un algoritmo on-policy evalúa y mejora la misma política con la que actúa.
Terminología clave en Q-learning
Antes de ver cómo funciona Q-learning, conviene aclarar algunos términos para entender sus fundamentos.
- States (s): la posición o situación actual del agente en el entorno.
- Action (a): un paso que da el agente en un estado concreto.
- Rewards: por cada acción, el agente recibe una recompensa o una penalización.
- Episodes: final de la fase, cuando el agente ya no puede actuar. Ocurre al lograr el objetivo o fallar.
- Q(St+1, a): valor Q óptimo esperado de realizar la acción a en un estado dado.
- Q(St, At): estimación actual de Q(St+1, a).
- Q-Table: tabla que mantiene el agente con los conjuntos de estados y acciones.
- Temporal Differences (TD): técnica para estimar el valor esperado de Q(St+1, a) usando estado y acción actuales y anteriores.
¿Cómo funciona Q-learning?
Vamos a verlo con el ejemplo del lago helado. En este entorno, el agente debe cruzar el lago desde el inicio hasta la meta sin caer en los agujeros. La mejor estrategia es llegar a la meta por el camino más corto.

Gif del autor
Q-table
El agente usa una Q-table para elegir la mejor acción posible según la recompensa esperada en cada estado del entorno. En pocas palabras, una Q-table es una estructura de datos con estados y acciones, y el algoritmo de Q-learning se encarga de actualizar sus valores.
Función Q
La función Q usa la ecuación de Bellman y toma como entrada el estado (s) y la acción (a). La ecuación simplifica el cálculo del valor de estado y del valor estado-acción.
Imagen de freecodecamp.org
Algoritmo de Q-learning

Imagen del autor
Inicializar la Q-table
Primero inicializamos la Q-table. La construiremos con columnas según el número de acciones y filas según el número de estados.
En nuestro ejemplo, el personaje puede moverse arriba, abajo, izquierda y derecha. Tenemos cuatro acciones posibles y cuatro estados (inicio, inactivo, camino erróneo y final). También puedes considerar el camino erróneo como caer en un agujero. Inicializaremos la Q-table con valores a 0.

Imagen del autor
Elegir una acción
El segundo paso es simple. Al principio, el agente tomará una acción aleatoria (abajo o derecha) y, en la segunda pasada, usará la Q-table actualizada para seleccionar la acción.
Ejecutar la acción
Elegir y ejecutar acciones se repetirá muchas veces hasta que termine el bucle de entrenamiento. La primera acción y estado se seleccionan usando la Q-table. En nuestro caso, todos los valores empiezan en cero.
Después, el agente se moverá hacia abajo y actualizará la Q-table con la ecuación de Bellman. Con cada movimiento, iremos actualizando los valores de la Q-table y usándolos para decidir el mejor curso de acción.
Al principio, el agente está en modo exploración y elige acciones aleatorias para conocer el entorno. La estrategia epsilon-greedy es una forma sencilla de equilibrar exploración y explotación. Epsilon representa la probabilidad de explorar; cuando disminuye, aumenta la explotación.
Al inicio, epsilon es alto, es decir, el agente explora. A medida que explora el entorno, epsilon disminuye y el agente empieza a explotar lo aprendido. Con cada iteración durante la exploración, el agente gana confianza al estimar los valores Q.

Imagen del autor
En el lago helado, el agente desconoce el entorno, así que comienza con una acción aleatoria (moverse abajo). Como se ve en la imagen, la Q-table se actualiza con la ecuación de Bellman.
Medir las recompensas
Tras actuar, medimos el resultado y la recompensa.
- La recompensa por llegar a la meta es +1
- La recompensa por tomar un camino erróneo (caer en un agujero) es 0
- La recompensa por estar inactivo o moverte por el lago helado también es 0.
Actualizar la Q-table
Actualizaremos la función Q(St, At) usando la ecuación. Emplea los valores Q estimados en el episodio anterior, la tasa de aprendizaje y el error de diferencias temporales (TD). El error TD se calcula con la recompensa inmediata, la recompensa futura máxima esperada con descuento y la estimación previa del valor Q.
El proceso se repite muchas veces hasta actualizar la Q-table y maximizar la función de valor Q.

Imagen del autor | Visualización de la ecuación por Thomas Simonini
Al principio, el agente explora el entorno para actualizar la Q-table. Cuando la Q-table ya está informada, el agente pasa a explotar y empieza a tomar mejores decisiones.
Imagen del autor
En el caso del lago helado, el agente aprenderá a tomar el camino más corto hasta la meta evitando los agujeros.
Tutorial de Q-learning en Python
En esta sección, construiremos nuestro modelo de Q-learning desde cero usando el entorno Gymnasium, Pygame y Numpy. El tutorial en Python es una versión adaptada del Notebook de Thomas Simonini. Incluye inicializar el entorno y la Q-table, definir la política greedy, fijar hiperparámetros, crear y ejecutar el bucle de entrenamiento y la evaluación, y visualizar resultados.
Si tienes problemas al crear y ejecutar tu bucle de entrenamiento, puedes consultar el código fuente con la salida.
Puesta en marcha
Configurar una pantalla virtual
Primero instalaremos las dependencias para generar un vídeo de repetición (gif). Necesitamos una pantalla virtual (pyvirtualdisplay) para renderizar el entorno y grabar los fotogramas.
Nota: usando %%capture suprimimos la salida de la celda de Jupyter.
%%capture
!pip install pyglet==1.5.1
!apt install python-opengl
!apt install ffmpeg
!apt install xvfb
!pip3 install pyvirtualdisplay
# Virtual display
from pyvirtualdisplay import Display
virtual_display = Display(visible=0, size=(1400, 900))
virtual_display.start()
Instalar dependencias
Ahora instalaremos las dependencias que nos ayudarán a crear, ejecutar y evaluar el bucle de entrenamiento.
- gymnasium: para inicializar el entorno FrozenLake-v1.
- pygame: para la interfaz de FrozenLake-v1.
- numpy: para crear y manejar la Q-table.
%%capture
!pip install gymnasium
!pip install pygame
!pip install numpy
!pip install imageio imageio_ffmpeg
Importar paquetes
Importamos ahora las librerías necesarias.
- Imageio se usa para crear la animación.
- tqdm se usa para las barras de progreso.
import numpy as np
import gymnasium as gym
import random
import imageio
from tqdm.notebook import trange
Entorno Frozen Lake de Gymnasium
Vamos a crear un entorno 4x4 sin deslizamiento usando la librería Frozen Lake de Gymnasium.
- Hay dos versiones de la cuadrícula: "4x4" y "8x8".
- Si
is_slippery=True, el agente puede no moverse en la dirección deseada por la naturaleza resbaladiza del lago.
Tras inicializar el entorno, haremos un análisis del mismo.
env = gym.make("FrozenLake-v1",map_name="4x4",is_slippery=False)
print("Observation Space", env.observation_space)
print("Sample observation", env.observation_space.sample()) # display a random observation
Hay 16 espacios únicos en el entorno que se muestran en posiciones aleatorias.
Observation Space Discrete(16)
Sample observation 15
Vamos a ver cuántas acciones hay y a mostrar una acción aleatoria.
Espacio de acciones:
- 0: moverse a la izquierda
- 1: moverse hacia abajo
- 2: moverse a la derecha
- 3: moverse hacia arriba
Función de recompensa:
- Alcanzar la meta: +1
- Caer en un agujero: 0
- Permanecer sobre el lago helado: 0
print("Action Space Shape", env.action_space.n)
print("Action Space Sample", env.action_space.sample())
Action Space Shape 4
Action Space Sample 1
Crear e inicializar la Q-table
La Q-table tiene las acciones como columnas y los estados como filas. Podemos usar Gymnasium para obtener el espacio de acciones y el de estados y, con ello, crear la Q-table.
state_space = env.observation_space.n
print("There are ", state_space, " possible states")
action_space = env.action_space.n
print("There are ", action_space, " possible actions")
There are 16 possible states
There are 4 possible actions
Para inicializar la Q-table, crearemos un array de Numpy con los espacios de estados y acciones. Será una matriz de 16 x 4.
def initialize_q_table(state_space, action_space):
Qtable = np.zeros((state_space, action_space))
return Qtable
Qtable_frozenlake = initialize_q_table(state_space, action_space)
Política epsilon-greedy
En la sección anterior vimos la estrategia epsilon-greedy, que gestiona el equilibrio entre exploración y explotación. Con probabilidad 1 - ɛ explotamos, y con probabilidad ɛ exploramos.
En epsilon_greedy_policy haremos lo siguiente:
- Generar un número aleatorio entre 0 y 1.
- Si el número es mayor que epsilon, explotamos: el agente toma la acción con mayor valor dado un estado.
- En caso contrario, exploramos (acción aleatoria).
def epsilon_greedy_policy(Qtable, state, epsilon):
random_int = random.uniform(0,1)
if random_int > epsilon:
action = np.argmax(Qtable[state])
else:
action = env.action_space.sample()
return action
Definir la política greedy
Q-learning es un algoritmo off-policy, es decir, la política con la que actúa y la que se usa para actualizar la función son distintas.
En este ejemplo, la política epsilon-greedy es la política de actuación, y la política greedy es la de actualización.
La política greedy también será la final cuando el agente haya sido entrenado. Selecciona el mayor valor estado-acción de la Q-table.
def greedy_policy(Qtable, state):
action = np.argmax(Qtable[state])
return action
Hiperparámetros del modelo
Estos hiperparámetros se usan en el bucle de entrenamiento; ajustarlos bien te dará mejores resultados.
El agente necesita explorar suficiente espacio de estados para aprender buenas aproximaciones de valor; por eso usamos una disminución progresiva de epsilon. Si la tasa de decaimiento es alta, el agente puede quedarse atascado por no haber explorado lo bastante.
- Hay 10.000 episodios de entrenamiento y 100 de evaluación.
- La tasa de aprendizaje es 0,7.
- Usamos "FrozenLake-v1" con 99 pasos máximos por episodio.
- El gamma (factor de descuento) es 0,95.
- eval_seed: semilla de evaluación para el entorno.
- La probabilidad epsilon de exploración inicial es 1,0 y la mínima será 0,05.
- La tasa de decaimiento exponencial de epsilon es 0,0005.
# Training parameters
n_training_episodes = 10000
learning_rate = 0.7
# Evaluation parameters
n_eval_episodes = 100
# Environment parameters
env_id = "FrozenLake-v1"
max_steps = 99
gamma = 0.95
eval_seed = []
# Exploration parameters
max_epsilon = 1.0
min_epsilon = 0.05
decay_rate = 0.0005
Entrenamiento del modelo
En el bucle de entrenamiento vamos a:
- Crear un bucle de episodios de entrenamiento.
- Reducir epsilon: con cada episodio necesitamos menos exploración y más explotación.
- Reiniciar el entorno.
- Crear un bucle anidado para el número máximo de pasos.
- Elegir la acción con la política epsilon-greedy.
- Tomar la acción (At) y observar la recompensa esperada (Rt+1) y el estado (St+1).
- Tomar la acción (a) y observar el nuevo estado (s') y la recompensa (r).
- Actualizar la función Q con la fórmula.
- Si
done = True, terminar el episodio y salir del bucle. - Cambiar el estado actual por el nuevo estado.
- Al finalizar todos los episodios de entrenamiento, la función devuelve la Q-table actualizada.
def train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable):
for episode in trange(n_training_episodes):
epsilon = min_epsilon + (max_epsilon - min_epsilon)*np.exp(-decay_rate*episode)
# Reset the environment
state = env.reset()
step = 0
done = False
# repeat
for step in range(max_steps):
action = epsilon_greedy_policy(Qtable, state, epsilon)
new_state, reward, done, info = env.step(action)
Qtable[state][action] = Qtable[state][action] + learning_rate * (reward + gamma * np.max(Qtable[new_state]) - Qtable[state][action])
# If done, finish the episode
if done:
break
# Our state is the new state
state = new_state
return Qtable
Tardamos 3 segundos en completar 10.000 episodios de entrenamiento.
Qtable_frozenlake = train(n_training_episodes, min_epsilon, max_epsilon, decay_rate, env, max_steps, Qtable_frozenlake)

Como se aprecia, la Q-table entrenada ya tiene valores y el agente los usará para navegar por el entorno y alcanzar la meta.
Qtable_frozenlake
array([[0.73509189, 0.77378094, 0.77378094, 0.73509189],
[0.73509189, 0. , 0.81450625, 0.77378094],
[0.77378094, 0.857375 , 0.77378094, 0.81450625],
[0.81450625, 0. , 0.77378094, 0.77378094],
[0.77378094, 0.81450625, 0. , 0.73509189],
[0. , 0. , 0. , 0. ],
[0. , 0.9025 , 0. , 0.81450625],
[0. , 0. , 0. , 0. ],
[0.81450625, 0. , 0.857375 , 0.77378094],
[0.81450625, 0.9025 , 0.9025 , 0. ],
[0.857375 , 0.95 , 0. , 0.857375 ],
[0. , 0. , 0. , 0. ],
[0. , 0. , 0. , 0. ],
[0. , 0.9025 , 0.95 , 0.857375 ],
[0.9025 , 0.95 , 1. , 0.9025 ],
[0. , 0. , 0. , 0. ]])
Evaluación
La función evaluate_agent se ejecuta durante n_eval_episodes episodios y devuelve la media y la desviación estándar de la recompensa.
- En el bucle, primero comprobamos si hay semilla de evaluación. Si no, reiniciamos sin semilla.
- El bucle interno corre hasta max_steps.
- El agente toma, usando la Q-table, la acción con la mayor recompensa futura esperada en el estado dado.
- Calculamos la recompensa.
- Cambiamos el estado.
- Si termina (el agente cae en un agujero o llega a la meta), salimos del bucle.
- Guardamos los resultados.
- Al final, calculamos media y desviación estándar.
def evaluate_agent(env, max_steps, n_eval_episodes, Q, seed):
episode_rewards = []
for episode in range(n_eval_episodes):
if seed:
state = env.reset(seed=seed[episode])
else:
state = env.reset()
step = 0
done = False
total_rewards_ep = 0
for step in range(max_steps):
# Take the action (index) that have the maximum reward
action = np.argmax(Q[state][:])
new_state, reward, done, info = env.step(action)
total_rewards_ep += reward
if done:
break
state = new_state
episode_rewards.append(total_rewards_ep)
mean_reward = np.mean(episode_rewards)
std_reward = np.std(episode_rewards)
return mean_reward, std_reward
Como ves, obtuvimos la puntuación perfecta con desviación estándar cero. Significa que nuestro agente alcanzó la meta en los 100 episodios.
# Evaluate our Agent
mean_reward, std_reward = evaluate_agent(env, max_steps, n_eval_episodes, Qtable_frozenlake, eval_seed)
print(f"Mean_reward={mean_reward:.2f} +/- {std_reward:.2f}")
Mean_reward=1.00 +/- 0.00
Visualizar el resultado
Hasta ahora hemos estado trabajando con números. Para la demo, vamos a crear un gif animado del agente desde el inicio hasta que alcanza la meta.
- Primero creamos el estado reiniciando el entorno con un entero aleatorio entre 0 y 500.
- Renderizamos el entorno con rdb_array para crear un array de imagen.
- Añadimos
imgal arrayimages. - En el bucle, daremos pasos usando la Q-table y renderizaremos la imagen en cada paso.
- Al final, usamos este array e imageio para crear un gif de un fotograma por segundo.
def record_video(env, Qtable, out_directory, fps=1):
images = []
done = False
state = env.reset(seed=random.randint(0,500))
img = env.render(mode='rgb_array')
images.append(img)
while not done:
# Take the action (index) that have the maximum expected future reward given that state
action = np.argmax(Qtable[state][:])
state, reward, done, info = env.step(action) # We directly put next_state = state for recording logic
img = env.render(mode='rgb_array')
images.append(img)
imageio.mimsave(out_directory, [np.array(img) for i, img in enumerate(images)], fps=fps)
Si estás en un cuaderno de Jupyter, puedes mostrar el gif con la función Image de IPython.display.
video_path="/content/replay.gif"
video_fps=1
record_video(env, Qtable_frozenlake, video_path, video_fps)
from IPython.display import Image
Image('./replay.gif')
Ahora puedes compartir estos resultados con tu equipo y compañeros de clase o publicarlos en redes sociales.
Obtén una certificación superior en IA
Preguntas frecuentes sobre Q-learning
¿Cuál es la desventaja de Q-learning?
El proceso de aprendizaje en Q-learning es costoso para el agente, sobre todo al principio. ¿Por qué? Para converger a la política óptima, cada par estado-acción debe visitarse con frecuencia.
¿Por qué se llama Q-learning?
En Q-learning, la "Q" significa quality (calidad). Representa lo útil que es una acción para obtener recompensas futuras y se usa para crear un mapa de estados y acciones que maximice las recompensas esperadas.
¿Por qué Q-learning es off-policy?
En Q-learning, la política que se actualiza es distinta de la política de comportamiento (la que usa para actuar); por eso se llama un algoritmo off-policy.
¿Q-learning siempre converge?
Sí. Durante el entrenamiento, el algoritmo converge siempre a la política óptima.
¿Por qué necesitamos deep Q-learning?
Q-learning es un algoritmo sencillo pensado para entornos pequeños y discretos. En un entorno grande, necesitaríamos una Q-table enorme de estados y acciones que consumiría mucha memoria y cómputo para entrenar. Deep Q-learning, en cambio, sustituye la Q-table por una red neuronal para manejar entornos grandes con acciones y estados continuos.


