σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademyRutas
Ctrl K
AO
+1,2 k estudiantes
Iniciar sesiónUnirse ↗
Rutas
AO
+1,2 k estudiantes
Iniciar sesión
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Ruta de Python›Módulo 11 · Sistemas de producción y modelos de vanguardia›Tema 11.11·~1 h 30 minExperto

Redes neuronales para pronóstico

Posición en la rutaMódulo 11 de 11 · Tema 11 de 13
11.11 Redes neuronales para pronóstico
—Puntuación del lab— / 100XP total—Racha—

Una función con mandos y una forma de girarlos

Una red neuronal es solo una función con muchos números ajustables, los pesos. Una capa multiplica las entradas por una matriz de pesos y suma un sesgo; se aplica una no linealidad como ReLU (max(x, 0)); el resultado alimenta la capa siguiente. Apiladas así, estas funciones pueden aproximar relaciones muy complicadas. Entrenar significa elegir los pesos para que las predicciones coincidan con objetivos conocidos: mide el desajuste con una pérdida (para pronosticar un número, el error cuadrático medio), calcula cómo cambia la pérdida con cada peso y empuja cada peso un poco en la dirección que la reduce. Ese empujón, repetido miles de veces, es el descenso de gradiente.

# A one-hidden-layer network: relu(x @ w1 + b1) @ w2 + b2
import numpy as np

def relu(x):
    return np.maximum(x, 0.0)

def mlp_forward(x, w1, b1, w2, b2):
    return relu(x @ w1 + b1) @ w2 + b2

x = np.array([[1.0, 2.0], [0.5, -1.0]])                       # two samples, two features
w1 = np.array([[1.0, -1.0], [0.5, 1.0]])                      # two hidden units
b1 = np.array([0.0, 0.5])
w2 = np.array([1.0, 2.0])
print(relu(np.array([-1.5, 0.0, 2.0])))                       # [0. 0. 2.]
print(mlp_forward(x, w1, b1, w2, 0.1))                        # [5.1 0.1]   a prediction for each sample; the second sample's hidden values are switched off by the relu

El descenso de gradiente en el modelo más simple

Entrenar una red usa retropropagación para obtener los gradientes, pero la idea ya está completa en un modelo lineal y ~ x @ w + b. El error cuadrático medio tiene gradiente 2/n * x.T @ err respecto a w y 2 * mean(err) respecto a b, donde err = predicción - objetivo. Parte de ceros, resta la tasa de aprendizaje por el gradiente y repite. Una tasa de aprendizaje demasiado grande se pasa y diverge; demasiado pequeña, se arrastra. Estandariza primero las variables (media cero, varianza unidad) para que una sola tasa sirva a todas. Bibliotecas como PyTorch calculan los gradientes automáticamente, pero hacen exactamente este bucle.

# Gradient descent on a line: start at zero and walk downhill on the mean squared error
import numpy as np

def mse_loss(pred, target):
    return float(np.mean((pred - target) ** 2))

def train_linear(x, y, lr, epochs):
    n, d = x.shape
    w, b = np.zeros(d), 0.0
    for _ in range(epochs):
        err = x @ w + b - y
        w = w - lr * 2 / n * x.T @ err
        b = b - lr * 2 * float(np.mean(err))
    return w, float(b)

x = np.array([[0.0], [1.0], [2.0], [3.0]])
y = np.array([1.0, 3.0, 5.0, 7.0])                          # exactly y = 2x + 1
for epochs in (0, 10, 2000):
    w, b = train_linear(x, y, 0.05, epochs)
    print(epochs, np.round(w, 3), round(b, 3), round(mse_loss(x @ w + b, y), 6))
# 0 [0.] 0.0 21.0          the zero start
# 10 [2.009] 0.958 0.00092   already close
# 2000 [2.] 1.0 0.0          the line y = 2x + 1 is found

Por qué pronosticar rendimientos es difícil para una red

Una red puede ajustar casi cualquier cosa, y ese es el peligro con datos financieros: los rendimientos son sobre todo ruido, así que un modelo flexible memoriza el ruido del periodo de entrenamiento y falla en el siguiente. El sobreajuste se ve como un error de entrenamiento muy inferior al error en datos que no ha visto. Las defensas son las de los temas anteriores: comparar con una referencia simple (un modelo lineal, o predecir cero); evaluar solo con datos posteriores usando validación walk-forward, sin barajar nunca el tiempo; pocas variables y una red pequeña; añadir regularización (decaimiento de pesos, dropout) y parada temprana; y promediar varias redes iniciadas con semillas distintas. Si la red no supera claramente a la referencia lineal fuera de muestra, usa el modelo lineal.

# Overfitting in miniature: a flexible fit to pure noise looks great in-sample and useless afterwards
import numpy as np

rng = np.random.default_rng(5)
x_train, x_test = rng.normal(size=(30, 12)), rng.normal(size=(1000, 12))      # 12 features that mean nothing
y_train, y_test = rng.normal(0, 0.01, size=30), rng.normal(0, 0.01, size=1000)  # next-day returns: pure noise

w = np.linalg.lstsq(x_train, y_train, rcond=None)[0]                          # a model with 12 free weights fitted to 30 points
train_mse = np.mean((x_train @ w - y_train) ** 2)
test_mse = np.mean((x_test @ w - y_test) ** 2)
baseline_mse = np.mean(y_test ** 2)                                           # predict zero
print(train_mse < baseline_mse, test_mse > baseline_mse)                      # True True   better in-sample, worse than zero out of sample
# The learning rate matters: a small one settles, one that is too large overshoots and the error blows up
import numpy as np

x = np.array([[1.0], [2.0], [3.0]])
y = np.array([2.0, 4.0, 6.0])

def final_errors(lr, epochs):
    w, b, errors = np.zeros(1), 0.0, []
    for _ in range(epochs):
        err = x @ w + b - y
        errors.append(float(np.mean(err ** 2)))
        w = w - lr * 2 / len(y) * x.T @ err
        b = b - lr * 2 * float(np.mean(err))
    return errors

small, large = final_errors(0.05, 30), final_errors(0.5, 30)
print(small[-1] < small[0])                          # True   the error shrinks
print(large[-1] > 1000 * large[0])                   # True   the error explodes: the steps are too big
Empieza por la referencia aburridaAntes de cualquier red, ajusta un modelo lineal con las mismas variables y guarda su error fuera de muestra como la cifra a superar. Registra la semilla, la ventana de datos y los hiperparámetros de cada ejecución, pues el entrenamiento neuronal es aleatorio y los resultados cambian entre ejecuciones. Escala las variables usando solo las estadísticas del periodo de entrenamiento, nunca de toda la muestra, o filtrarás el futuro. Y recuerda que una pequeña ventaja de pronóstico no vale nada si los costes de operar son mayores.
  • Una capa es relu(x @ w + b); una red apila capas y la pérdida (el error cuadrático medio en un pronóstico) mide cuánto se alejan las predicciones de los objetivos.
  • Entrenar es descenso de gradiente: en el modelo lineal w -= lr * 2/n * x.T @ err y b -= lr * 2 * mean(err); escala las variables y ajusta la tasa de aprendizaje.
  • Los rendimientos son sobre todo ruido: compara con una referencia lineal, valida en walk-forward, usa redes pequeñas con regularización y para cuando desaparezca la ganancia fuera de muestra.

A continuación: aprendizaje por refuerzo para trading, aprender una política a partir de recompensas en vez de respuestas etiquetadas.

Lab: qué se califica

  • Apagar los negativos con relu25 pts
  • Ejecutar la red hacia delante con mlp_forward25 pts
  • Medir el error con mse_loss25 pts
  • Aprender descendiendo con train_linear25 pts
Explorador · lab-140-1
▾ lab-140-1
…
bash · en el navegador
learner@quantcraft:~/lab-140-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

Todo lo que hay en lab-140-1 vuelve a los archivos iniciales y se limpia el terminal. Tu solución actual se perderá: no se puede deshacer.

Se restablecerá
  • main.py → código inicial
  • Historial del terminal y salida de pruebas
  • Cambios sin enviar
Se conservará
  • Mejor puntuación del lab
  • Tu XP y tu racha
  • Progreso de la lección y notas

Inicia sesión para ejecutar tu código.

Todas las clases se pueden leer gratis. Crea una cuenta o inicia sesión para ejecutar código, lanzar las pruebas y enviar este lab.

  • IDE en el navegador con terminal bash — sin instalar nada
  • Corrección instantánea, XP y tu lugar en la ruta
Crear una cuentaIniciar sesión
O
Continuar con GitHub