Implementación de Arquitecturas de Redes Neuronales y Técnicas de Optimización

Redes Completamente Conectadas (Modularidad)

El diseño de redes neuronales modernas se basa en la programación modular. Cada capa se define con dos funciones principales: una para la propagación hacia adelente (forward) y otra para la propagación hacia atrás (backward).

  • Forward: Recibe la entrada y devuelve la salida junto con un "cache" que almacena información necesaria para el gradiente.
  • Backward: Recibe el gradiente de la capa superior (upstream) y calcula los gradientes respecto a los pesos y a la entrada utilizando la regla de la cadena.
# Ejemplo de una capa ReLU simplificada
def relu_forward(entrada):
    salida = np.maximum(0, entrada)
    cache = entrada
    return salida, cache

def relu_backward(grad_salida, cache):
    entrada = cache
    # El gradiente es 1 para valores > 0, de lo contrario es 0
    grad_entrada = grad_salida * (entrada > 0)
    return grad_entrada

Algoritmos de Optimización Avanzados

Más allá del descenso de gradiente estocástico (SGD) básico, existen métodos que adaptan la tasa de aprendizaje o utilizan el momento para acelerar la convergencia.

Momentum SGD

Acumula un promedio de los gradientes pasados para suavizar las actualizaciones y superar mínimos locales o zonas de baja pendiente.

# v representa la velocidad acumulada
v = coeficiente_momento * v - tasa_aprendizaje * grad_pesos
pesos += v

RMSProp

Divide la tasa de aprendizaje por una media móvil de las magnitudes de los gradientes recientes para normalizar el paso de actualización.

cache_grad = decaimiento * cache_grad + (1 - decaimiento) * (grad_pesos**2)
pesos -= tasa_aprendizaje * grad_pesos / (np.sqrt(cache_grad) + eps)

Adam (Adaptive Moment Estimation)

Combina las ideas de Momentum y RMSProp, incluyendo una corrección de sesgo para las estimaciones iniciales.

m = beta1 * m + (1 - beta1) * grad_pesos  # Primer momento (media)
v = beta2 * v + (1 - beta2) * (grad_pesos**2) # Segundo momento (varianza)

# Corrección de sesgo
m_corregido = m / (1 - beta1**t)
v_corregido = v / (1 - beta2**t)

actualizacion = tasa_aprendizaje * m_corregido / (np.sqrt(v_corregido) + eps)
pesos -= actualizacion

Funciones de Pérdida: SVM y Softmax

Multiclass SVM (Hinge Loss)

Busca que la puntuación de la clase correcta sea mayor que las incorrectas por un margen determinado.

def svm_loss(puntuaciones, etiquetas):
    num_ejemplos = puntuaciones.shape[0]
    puntuacion_correcta = puntuaciones[np.arange(num_ejemplos), etiquetas][:, np.newaxis]
    margenes = np.maximum(0, puntuaciones - puntuacion_correcta + 1.0)
    margenes[np.arange(num_ejemplos), etiquetas] = 0
    
    loss = np.sum(margenes) / num_ejemplos
    
    # Cálculo del gradiente
    d_puntuaciones = (margenes > 0).astype(float)
    conteo_margen = np.sum(d_puntuaciones, axis=1)
    d_puntuaciones[np.arange(num_ejemplos), etiquetas] -= conteo_margen
    d_puntuaciones /= num_ejemplos
    
    return loss, d_puntuaciones

Softmax (Entropía Cruzada)

Interpreta las puntuaciones como probabilidades logarítmicas no normalizadas.

def softmax_loss(x, y):
    # Truco numérico restando el máximo
    logits_estables = x - np.max(x, axis=1, keepdims=True)
    exp_sum = np.sum(np.exp(logits_estables), axis=1, keepdims=True)
    log_probabilidades = logits_estables - np.log(exp_sum)
    probabilidades = np.exp(log_probabilidades)
    
    n = x.shape[0]
    loss = -np.sum(log_probabilidades[np.arange(n), y]) / n
    
    dx = probabilidades.copy()
    dx[np.arange(n), y] -= 1
    dx /= n
    return loss, dx

Batch Normalization

Esta técnica mitiga el problema del desvanecimiento del gradiente al normalizar las activaciones de una capa para que tengan media cero y varianza unitaria dentro de un mini-batch. Posteriormente, se escalan y desplazan con parámetros aprendibles ($\gamma$ y $\beta$).

Durante la inferencia, se utilizan promedios móviles calculados durante el entrenamiento para asegurar que la predición sea consistente independientemente del tamaño del batch.

Dropout

Es una técnica de regularización potente. En Inverted Dropout, durante el entrenamiento se desactivan neuronas aleatoriamente con probabilidad $p$ y se escalan las activaciones restantes por $1/(1-p)$ para mantener constante el valor esperado de la salida. Esto elimina la necesidad de modificar nada durante la fase de prueba.

Redes Neuronales Convolucionales (CNN)

A diferencia de las redes densas, las CNN preservan la estructura espacial de los datos. Sus componentes clave son:

  • Convolución: Aplica filtros (kernels) que se deslizan sobre la imagen. El tamaño de salida depende del stride (paso) y el padding (relleno).
  • Max Pooling: Reduce la resolución espacial seleccionando el valor máximo en ventanas locales, lo que otorga cierta invarianza a traslaciones pequeñas.
  • Spatial Batch Normalization: A diferencia de la normalización estándar, aquí se calculan estadísticas por cada canal (C) a través de todas las posiciones espaciales (H, W) y ejemplos (N).
# Cálculo de dimensiones de salida
alto_salida = (alto_entrada + 2 * padding - alto_kernel) // paso + 1
ancho_salida = (ancho_entrada + 2 * padding - ancho_kernel) // paso + 1

Implementación en Frameworks

PyTorch: Estilo Secuencial

PyTorch permite definir modelos de forma imperativa. Es fundamental utilizar model.train() y model.eval() para alternar el comportamiento de capas como Dropout y BatchNorm.

import torch.nn as nn

red_cnn = nn.Sequential(
    nn.Conv2d(3, 32, kernel_size=3, padding=1),
    nn.ReLU(),
    nn.BatchNorm2d(32),
    nn.MaxPool2d(2),
    nn.Flatten(),
    nn.Linear(32 * 16 * 16, 10)
)

TensorFlow (v1): Grafos y Sesiones

En versiones clásicas de TensorFlow, se definen placeholders para los datos y se gestiona la ejecución mediante una sesión (tf.Session).

import tensorflow.compat.v1 as tf
tf.disable_eager_execution()

X = tf.placeholder(tf.float32, [None, 32, 32, 3])
etiquetas = tf.placeholder(tf.int64, [None])
entrenando = tf.placeholder(tf.bool)

def construir_modelo(datos, es_entrenamiento):
    capa_conv = tf.layers.conv2d(datos, 64, 3, activation=tf.nn.relu)
    bn = tf.layers.batch_normalization(capa_conv, training=es_entrenamiento)
    pool = tf.layers.max_pooling2d(bn, 2, 2)
    return tf.layers.dense(tf.layers.flatten(pool), 10)

logits = construir_modelo(X, entrenando)
error = tf.losses.sparse_softmax_cross_entropy(labels=etiquetas, logits=logits)
paso_entrenamiento = tf.train.AdamOptimizer(1e-3).minimize(error)

Etiquetas: Deep Learning Computer Vision PyTorch TensorFlow CNN

Publicado el 7-23 23:41