2. La sigmoide y la función de error que vas a minimizar
Nivel 11: Redes Neuronales para Trading
La función de activación que aplica cada unidad oculta suele ser la sigmoide. En palabras: toma un número cualquiera, lo pasa por una exponencial negativa y devuelve un valor estrictamente entre 0 y 1. En código plano:
import numpy as np
import matplotlib.pyplot as plt
def sigmoide(v, s=1.0):
# 1 dividido entre 1 mas la exponencial de menos s por v
return 1 / (1 + np.exp(-s * v))
v = np.linspace(-10, 10, 200)
plt.plot(v, sigmoide(v, 1), color='C3', label='s = 1')
plt.plot(v, sigmoide(v, 0.5), '--', color='C0', label='s = 0.5')
plt.plot(v, sigmoide(v, 10), '--', color='C4', label='s = 10')
plt.xlabel('v')
plt.ylabel('sigmoide')
plt.legend()
plt.show()El parámetro de escala s controla la velocidad de activación. Un s grande equivale a una activación dura en v igual a cero: la curva se parece a un escalón, la unidad está apagada o encendida y casi nada intermedio. Un s pequeño la aplana hasta hacerla casi lineal en el centro. Y si escribes sigmoide de s por (v menos v0), lo que haces es desplazar el umbral de activación de cero a v0 — que es exactamente el papel del término de sesgo dentro de la unidad.
Esto tiene una consecuencia práctica enorme, que te va a morder en cuanto entrenes: saturación. Con valores de entrada muy grandes o muy pequeños, la sigmoide se aplana y su pendiente tiende a cero. Una unidad saturada casi no propaga gradiente y deja de aprender. Por eso las entradas se estandarizan antes de entrenar y por eso los pesos se inicializan pequeños.
Los parámetros. Al conjunto completo de pesos lo llamamos theta. Está formado por los pesos y sesgos de la capa oculta — hay M por (p más 1) de ellos, siendo p el número de entradas y M el número de unidades ocultas — y por los pesos y sesgos de la capa de salida, que son K por (M más 1). Cuenta esos números antes de entrenar: son la medida honesta de cuántos grados de libertad le estás dando al modelo. Con 8 señales técnicas, 32 unidades ocultas y una salida son 32 por 9 más 33, es decir 321 parámetros, para ajustar quizá 1000 días de bolsa. Si eso te suena a sobreajuste, te suena bien.
La función de error. Para regresión usamos la suma de errores al cuadrado: sumas, sobre todas las observaciones y todas las salidas, el cuadrado de la diferencia entre el valor real y el predicho. Para clasificación puedes usar el error cuadrático o, mejor, la entropía cruzada (deviance): menos la suma, sobre observaciones y clases, del objetivo real multiplicado por el logaritmo de la probabilidad predicha. El clasificador resultante asigna a cada observación la clase con la probabilidad predicha más alta.
Hay un detalle elegante que conviene retener: con activación softmax en la salida y entropía cruzada como error, la red neuronal es exactamente una regresión logística lineal sobre las unidades ocultas, y todos los parámetros se estiman por máxima verosimilitud. Dicho de otro modo: la red no inventa un paradigma nuevo, aprende una representación no lineal de los datos y luego hace encima el modelo lineal de toda la vida.
def error_cuadratico(y, f):
return np.sum((y - f) ** 2)
def entropia_cruzada(y, f, eps=1e-12):
# y son etiquetas 0-1, f son probabilidades predichas
return -np.sum(y * np.log(f + eps))