Algoritmos de optimización mediante descenso de gradiente: fundamentos y variantes

El descenso de gradiente es un método fundamental para optimizar funciones objetivo en modelos parametrizados, común en redes neuronales. Este artículo explora sus variantes y algoritmos avanzados que mejoran la convergencia y eficiencia.

Variantes del descenso de gradiente

El descenso de gradiente se adapta según la cantidad de datos usados para calcular gradientes, equilibrando precisión y costo computacional.

Descenso por gradiente por lotes

Calcula el gradiente de la función de costo sobre todo el conjunto de datos de entrenamiento en cada iteración:

θ = θ - η · ∇θ J(θ)

Es simple pero lento para grandes datasets y no permite actualizaciones en línea. Implementación básica:

for epoch in num_epochs:
    grad = compute_gradient(objective_function, full_dataset, parameters)
    parameters -= learning_rate * grad

Descenso estocástico de gradiente (SGD)

Actualiza parámetros por cada ejemplo indiivdual:

θ = θ - η · ∇θ J(θ; x_i; y_i)

Es rápido y permite aprendizaje en línea, pero introduce varianza alta que puede dificultar la convergencia. Ejemplo modificado:

import numpy as np
for epoch in range(num_epochs):
    shuffled_data = np.random.permutation(dataset)
    for sample in shuffled_data:
        grad_sample = evaluate_gradient(loss_function, sample, parameters)
        parameters -= learning_rate * grad_sample

Descenso de gradiente por mini-lotes

Combina lo mejor de ambos enfoques, usando pequeños lotes de datos:

θ = θ - η · ∇θ J(θ; batch)

Reduce varinaza y aprovecha optimizaciones matriciales. Tamaños comunes: 32 a 256 ejemplos. Implementación alterada:

batch_size = 64
for epoch in num_epochs:
    indices = np.arange(len(dataset))
    np.random.shuffle(indices)
    for start in range(0, len(dataset), batch_size):
        batch_indices = indices[start:start+batch_size]
        batch_data = dataset[batch_indices]
        grad_batch = calc_gradient(loss_function, batch_data, params)
        params = update_params(params, grad_batch, lr=0.01)

Desafíos

El descenso de gradiente por mini-lotes enfrenta problemas como la selección de tasa de aprendizaje, tasas fijas para todos los parámetros y dificultades para escapar de mínimos locales o puntos de silla en funciones no convexas.

Algoritmos de optimización

Momento (Momentum)

Acelera SGD en direcciones consistentes y reduce oscilaciones acumulando actualizaciones pasadas:

v_t = γ · v_{t-1} + η · ∇θ J(θ)
θ = θ - v_t

Donde γ suele ser 0.9. Similar a una bola que gana impulso al rodar cuesta abajo.

Gradiente acelerado de Nesterov (NAG)

Mejora el momento calculando el gradiente en la posición anticipada:

v_t = γ · v_{t-1} + η · ∇θ J(θ - γ · v_{t-1})
θ = θ - v_t

Permite correcciones más precisas, especialmente útil en redes recurrentes.

Adagrad

Adapta la tasa de aprendizaje por parámetro, favoreciendo actualizaciones mayores para características raras:

G_t = G_{t-1} + (∇θ J(θ))^2
θ = θ - (η / sqrt(G_t + ε)) · ∇θ J(θ)

Ideal para datos dispersos, pero acumula gradientes al cuadrado, reduciendo progresivamente la tasa de aprendizaje.

Adadelta

Extiende Adagrad usando un promedio móvil de gradientes pasados para limitar la acumulación:

E[g²]_t = γ · E[g²]_{t-1} + (1-γ) · (∇θ J(θ))^2
Δθ_t = - (sqrt(E[Δθ²]_{t-1} + ε) / sqrt(E[g²]_t + ε)) · ∇θ J(θ)
θ = θ + Δθ_t

Elimina la necesidad de establecer una tasa de aprendizaje fija.

RMSprop

Similar a Adadelta, pero con un enfoque simplificado:

E[g²]_t = 0.9 · E[g²]_{t-1} + 0.1 · (∇θ J(θ))^2
θ = θ - (η / sqrt(E[g²]_t + ε)) · ∇θ J(θ)

Propuesto por Geoff Hinton, utiliza γ=0.9 y tasa de aprendizaje típica de 0.001.

Adam (Estimación de momento adaptativo)

Combina momentos y promedios móviles para tasas adaptativas:

m_t = β₁ · m_{t-1} + (1-β₁) · ∇θ J(θ)
v_t = β₂ · v_{t-1} + (1-β₂) · (∇θ J(θ))^2
θ = θ - η · (m_t / (1-β₁^t)) / (sqrt(v_t / (1-β₂^t)) + ε)

Valores por defecto: β₁=0.9, β₂=0.999, ε=1e-8. Incluye corrección de sesgo.

AdaMax

Generaliza Adam usando normas infinitas para estabilidad:

u_t = max(β₂ · u_{t-1}, |∇θ J(θ)|)
θ = θ - (η / u_t) · m_t

Donde m_t se calcula como en Adam pero sin corrección de sesgo en u_t.

Nadam

Integra el momento de Nesterov en Adam para mejor anticipación:

θ = θ - η · (m_t / (1-β₁^t)) / (sqrt(v_t / (1-β₂^t)) + ε)

Pero con m_t modificado para usar el vector de momento actual, mejorando la responsividad.

Visualización y selección de algoritmos

Los métodos adaptativos como Adagrad, RMSprop y Adam suelen converger más rápido en superficies complejas. Adam es una opción general robusta, aunque SGD con momento puede ser preferible en algunos contextos por su simplicidad. La elección depende de la dispersión de datos y la complejidad del modelo.

Paralelización y distribución

Para escalar SGD, se proponen técnicas como Hogwild! (acceso concurrente sin bloqueos para datos dispersos), Downpour SGD (réplicas asíncronas con servidores de parámetros), y algoritmos tolerantes a retardos. TensorFlow implementa ejecución distribuida mediante grafos computacionales. El SGD con promediado elástico (EASGD) permite exploración adicional en el espacio de parámetros.

Estrategias adicionales

Mejoras complementarias incluyen mezclar datos en cada época, aprendizaje curricular para problemas graduales, normalización por lotes para estabilizar entrenamiento, parada temprana basada en validación, y añadir ruido gaussiano a gradientes para escapar de mínimos locales.

Etiquetas: gradient-descent optimization SGD momentum Adagrad

Publicado el 7-1 22:12