Arquitectura del Sistema y Preprocesamiento
El aprendizaje por refuerzo profundo permite que los agentes artificiales aprendan políticas de control óptimas mediante la interacción directa con el entorno. En este contexto, el juego Flappy Bird representa un problema clásico de control continuo con un espacio de estados visual y recompensas dispersas. A continuación, se detalla la arquitectura y el flujo de trabajo para entrenar una Red Q Profunda (DQN) que resuelva este entorno.
El entorno del juego devuelve marcos de imagen en formato RGB de alta resolución, los cuales contienen información redundante. Para optimizar el cómputo y extraer las características espaciales relevantes (como la posición de los tubos y el pájaro), se aplica una tubería de preprocesamiento de visión por computadora.
import cv2
import numpy as np
def transformar_frame_visual(frame_rgb):
# Convertir a escala de grises para eliminar redundancia de color
frame_gris = cv2.cvtColor(frame_rgb, cv2.COLOR_RGB2GRAY)
# Recortar la región de interés (ROI) eliminando el marcador de puntuación
roi = frame_gris[20:380, 0:288]
# Redimensionar a una matriz cuadrada para la entrada de la red convolucional
frame_escalado = cv2.resize(roi, (84, 84), interpolation=cv2.INTER_AREA)
# Aplicar umbralización adaptativa para resaltar bordes y objetos
_, mascara_binaria = cv2.threshold(frame_escalado, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# Normalizar los valores de píxeles entre 0 y 1
return mascara_binaria / 255.0
Diseño de la Red Neuronal Convolucional
El aproximador de la función Q utiliza una arquitectura de red neuronal convolucional (CNN). La entrada consiste en un apilamiento de 4 frames consecutivos para capturar la dinámica temporal (velocidad de caída y posición relativa). Se utiliza la API de Keras en TensorFlow 2.x para definir el modelo de manera más limpia y moderna.
import tensorflow as tf
from tensorflow.keras import layers, models
def construir_modelo_dqn(filas=84, columnas=84, canales=4, num_acciones=2):
entradas = layers.Input(shape=(filas, columnas, canales))
# Capas convolucionales para extracción de características espaciales
x = layers.Conv2D(32, kernel_size=(8, 8), strides=(4, 4), activation='relu')(entradas)
x = layers.Conv2D(64, kernel_size=(4, 4), strides=(2, 2), activation='relu')(x)
x = layers.Conv2D(64, kernel_size=(3, 3), strides=(1, 1), activation='relu')(x)
# Aplanamiento y capas densas para la toma de decisiones
x = layers.Flatten()(x)
x = layers.Dense(512, activation='relu')(x)
# Capa de salida con valores Q para cada acción discreta
salidas_q = layers.Dense(num_acciones, activation='linear')(x)
modelo = models.Model(inputs=entradas, outputs=salidas_q)
# Compilación con optimizador Adam y error cuadrático medio
modelo.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.00025), loss='mse')
return modelo
Bucle de Entrenamiento y Estrategia de Exploración
El entrenamiento requiere un balance entre exploración y explotación. Se implementa una política épsilon-greedy con decaimiento exponencial para reducir la exploración aleatoria a medida que el agente converge hacia una política óptima. Además, se utiliza un búfer de repetición de experiencia (Experience Replay) para romper la correlación entre muestras consecutivas.
import random
class ControladorExploracion:
def __init__(self, epsilon_inicial=1.0, epsilon_minimo=0.05, decaimiento=0.995):
self.epsilon = epsilon_inicial
self.epsilon_minimo = epsilon_minimo
self.decaimiento = decaimiento
def seleccionar_accion(self, valores_q):
if np.random.rand() <= self.epsilon:
return np.random.randint(0, 2) # Exploración aleatoria
# Explotación: elegir la acción con el mayor valor Q predicho
return np.argmax(valores_q[0])
def actualizar_epsilon(self):
if self.epsilon > self.epsilon_minimo:
self.epsilon *= self.decaimiento
Optimización del Proceso de Aprendizaje
Para garantizar la estabilidad del algoritmo DQN, se deben considerar varias técnicas de optimización durante el entrenamiento:
- Red Objetivo (Target Network): Se mantiene una copia de la red principal cuyos pesos se actualizan periódicamente (por ejemplo, cada 10,000 pasos) para calcular los valores Q objetivo, evitando la oscilación o divergencia del entrenamiento.
- Factor de Descuento (Gamma): Un valor de 0.99 permite que el agente priorice recompensas a largo plazo (sobrevivir más tiempo) en lugar de recompensas inmediatas.
- Muestreo por Prioridad (Prioritized Experience Replay): En lugar de un muestreo uniforme, las transiciones con un error de diferencia temporal (TD-error) más alto se muestrean con mayor frecuencia, enfocando el aprendizaje en las experiencias más informativas.
Diagnóstico de Problemas Comunes
Durante la implementación, pueden surgir diversos problemas de convergencia y rendimiento:
- Falta de convergencia: Si la pérdida (loss) no disminuye o el agente no mejora, verifique la normalización de las entradas. Asegúrese de que los píxeles estén escalados correctamente y considere reducir la tasa de aprendizaje inicial.
- Sobreajuste (Overfitting): Aunque el sobreajuste es menos común en entornos deterministas, puede ocurrir si el búfer de experiencia es demasiado pequeño. Aumente la capacidad del búfer (por ejemplo, a 100,000 transiciones) para mantener la diversidad de los datos.
- Cuellos de botella de hardware: El procesamiento de imágenes y el entrenamiento de la CNN son intensivos. Utilice aceleración por GPU y aumente el tamaño del lote (batch size) a 64 o 128 si la memoria VRAM lo permite, para maximizar el rendimiento del entrenamiento.