σμ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 AcademyRoteiros
Ctrl K
AO
+1,2 mil alunos
EntrarParticipar ↗
Roteiros
AO
+1,2 mil alunos
Entrar
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Roteiro de Python›Módulo 11 · Sistemas de produção e modelos de ponta›Tópico 11.11·~1 h 30 minEspecialista

Redes neurais para previsão

Posição no roteiroMódulo 11 de 11 · Tópico 11 de 13
11.11 Redes neurais para previsão
—Nota do lab— / 100XP total—Sequência—

Uma função com botões e um jeito de girá-los

Uma rede neural é só uma função com muitos números ajustáveis, os pesos. Uma camada multiplica as entradas por uma matriz de pesos e soma um viés; aplica-se uma não linearidade como ReLU (max(x, 0)); o resultado alimenta a camada seguinte. Empilhadas assim, essas funções conseguem aproximar relações muito complicadas. Treinar significa escolher os pesos para que as previsões casem com alvos conhecidos: meça o desajuste com uma perda (para prever um número, o erro quadrático médio), calcule como a perda muda com cada peso e empurre cada peso um pouco na direção que a reduz. Esse empurrão, repetido milhares de vezes, é a descida do 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

A descida do gradiente no modelo mais simples

Treinar uma rede usa retropropagação para obter os gradientes, mas a ideia já está completa num modelo linear y ~ x @ w + b. O erro quadrático médio tem gradiente 2/n * x.T @ err em relação a w e 2 * mean(err) em relação a b, em que err = previsão - alvo. Parta de zeros, subtraia a taxa de aprendizado vezes o gradiente e repita. Uma taxa de aprendizado grande demais passa do ponto e diverge; pequena demais se arrasta. Padronize antes as variáveis (média zero, variância unitária) para que uma só taxa sirva a todas. Bibliotecas como o PyTorch calculam os gradientes automaticamente, mas fazem exatamente este laço.

# 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 que prever retornos é difícil para uma rede

Uma rede pode ajustar quase qualquer coisa, e esse é o perigo com dados financeiros: retornos são sobretudo ruído, então um modelo flexível memoriza o ruído do período de treino e falha no seguinte. O sobreajuste aparece como um erro de treino muito abaixo do erro em dados que ele não viu. As defesas são as dos tópicos anteriores: comparar com uma referência simples (um modelo linear, ou prever zero); avaliar só em dados posteriores com validação walk-forward, nunca embaralhando o tempo; poucas variáveis e uma rede pequena; adicionar regularização (decaimento de pesos, dropout) e parada antecipada; e tirar a média de várias redes iniciadas com sementes diferentes. Se a rede não superar claramente a referência linear fora da amostra, use o modelo linear.

# 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
Comece pela referência chataAntes de qualquer rede, ajuste um modelo linear com as mesmas variáveis e guarde seu erro fora da amostra como o número a bater. Registre a semente, a janela de dados e os hiperparâmetros de cada execução, pois o treino neural é aleatório e os resultados mudam de execução para execução. Escale as variáveis usando só as estatísticas do período de treino, nunca da amostra inteira, ou você vaza o futuro. E lembre que uma pequena vantagem de previsão não vale nada se os custos de negociação forem maiores.
  • Uma camada é relu(x @ w + b); uma rede empilha camadas, e a perda (o erro quadrático médio numa previsão) mede o quanto as previsões se afastam dos alvos.
  • Treinar é descida do gradiente: no modelo linear w -= lr * 2/n * x.T @ err e b -= lr * 2 * mean(err); escale as variáveis e ajuste a taxa de aprendizado.
  • Retornos são sobretudo ruído: compare com uma referência linear, valide em walk-forward, use redes pequenas com regularização e pare quando o ganho fora da amostra sumir.

A seguir: aprendizado por reforço para trading, aprender uma política a partir de recompensas em vez de respostas rotuladas.

Lab: o que é avaliado

  • Desligar os negativos com relu25 pts
  • Rodar a rede para a frente com mlp_forward25 pts
  • Medir o erro com mse_loss25 pts
  • Aprender descendo com train_linear25 pts
Explorador · lab-140-1
▾ lab-140-1
…
bash · no navegador
learner@quantcraft:~/lab-140-1$
Python ainda não carregadoPythonLn 1, Col 1Spaces: 4UTF-8

Redefinir este tópico?

Tudo em lab-140-1 volta aos arquivos iniciais e o terminal é limpo. Sua solução atual será perdida — isso não pode ser desfeito.

Será redefinido
  • main.py → código inicial
  • Histórico do terminal e saída dos testes
  • Alterações não enviadas
Será mantido
  • Melhor nota do lab
  • Seu XP e sua sequência
  • Progresso da aula e anotações

Entre para executar o seu código.

Todas as aulas são gratuitas para ler. Crie uma conta ou entre para executar código, rodar os testes e enviar este lab.

  • IDE no navegador com terminal bash — nada para instalar
  • Correção instantânea, XP e o seu lugar no roteiro
Criar uma contaEntrar
OU
Continuar com o GitHub