σμ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 AcademyParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Parcours
AO
+1,2 k apprenants
Connexion
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Parcours Python›Module 11 · Systèmes de production et modèles de pointe›Sujet 11.11·~1 h 30 minExpert

Réseaux de neurones pour la prévision

Position dans le parcoursModule 11 sur 11 · Sujet 11 sur 13
11.11 Réseaux de neurones pour la prévision
—Score du lab— / 100XP total—Série—

Une fonction à boutons, et un moyen de les tourner

Un réseau de neurones n'est qu'une fonction avec beaucoup de nombres ajustables, les poids. Une couche multiplie les entrées par une matrice de poids et ajoute un biais ; une non-linéarité comme ReLU (max(x, 0)) est appliquée ; le résultat alimente la couche suivante. Empilées ainsi, ces fonctions peuvent approcher des relations très compliquées. Entraîner signifie choisir les poids pour que les prédictions collent à des cibles connues : mesurez l'écart avec une perte (pour prévoir un nombre, l'erreur quadratique moyenne), calculez comment la perte varie avec chaque poids, et poussez chaque poids un peu dans la direction qui la réduit. Cette poussée, répétée des milliers de fois, est la descente de gradient.

# 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

La descente de gradient sur le modèle le plus simple

L'entraînement d'un réseau utilise la rétropropagation pour obtenir les gradients, mais l'idée est déjà complète pour un modèle linéaire y ~ x @ w + b. L'erreur quadratique moyenne a pour gradient 2/n * x.T @ err par rapport à w et 2 * mean(err) par rapport à b, où err = prédiction - cible. Partez de zéros, soustrayez le taux d'apprentissage fois le gradient, recommencez. Un taux d'apprentissage trop grand dépasse la cible et diverge ; trop petit, il rampe. Standardisez d'abord les variables (moyenne nulle, variance unité) pour qu'un seul taux convienne à toutes. Des bibliothèques comme PyTorch calculent les gradients automatiquement, mais elles font exactement cette boucle.

# 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

Pourquoi prévoir des rendements est difficile pour un réseau

Un réseau peut ajuster presque n'importe quoi, et c'est le danger avec les données financières : les rendements sont surtout du bruit, si bien qu'un modèle flexible mémorise le bruit de la période d'entraînement et échoue sur la suivante. Le surapprentissage se voit à une erreur d'entraînement bien inférieure à l'erreur sur des données jamais vues. Les défenses sont celles des sujets précédents : comparer à une référence simple (un modèle linéaire, ou prédire zéro) ; n'évaluer que sur des données postérieures avec la validation walk-forward, sans jamais mélanger le temps ; peu de variables et un petit réseau ; ajouter de la régularisation (décroissance des poids, dropout) et un arrêt précoce ; et moyenner plusieurs réseaux lancés de graines différentes. Si le réseau ne bat pas nettement la référence linéaire hors échantillon, utilisez le modèle linéaire.

# 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
Commencez par la référence ennuyeuseAvant tout réseau, ajustez un modèle linéaire avec les mêmes variables et gardez son erreur hors échantillon comme le chiffre à battre. Journalisez la graine, la fenêtre de données et les hyperparamètres de chaque exécution, car l'entraînement neuronal est aléatoire et les résultats changent d'une exécution à l'autre. Mettez les variables à l'échelle avec les seules statistiques de la période d'entraînement, jamais de l'échantillon complet, sinon vous laissez fuir le futur. Et rappelez-vous qu'un petit avantage de prévision ne vaut rien si les coûts de transaction sont plus grands.
  • Une couche est relu(x @ w + b) ; un réseau empile des couches, et la perte (l'erreur quadratique moyenne pour une prévision) mesure l'écart des prédictions aux cibles.
  • L'entraînement est une descente de gradient : pour le modèle linéaire w -= lr * 2/n * x.T @ err et b -= lr * 2 * mean(err) ; mettez les variables à l'échelle, réglez le taux d'apprentissage.
  • Les rendements sont surtout du bruit : comparez à une référence linéaire, validez en walk-forward, utilisez de petits réseaux régularisés, et arrêtez quand le gain hors échantillon disparaît.

À suivre : l'apprentissage par renforcement pour le trading, apprendre une politique à partir de récompenses plutôt que de réponses étiquetées.

Lab : ce qui est noté

  • Éteindre les négatifs avec relu25 pts
  • Faire avancer le réseau avec mlp_forward25 pts
  • Mesurer l'erreur avec mse_loss25 pts
  • Apprendre en descendant avec train_linear25 pts
Explorateur · lab-140-1
▾ lab-140-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-140-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-140-1 revient aux fichiers de départ et le terminal est vidé. Votre solution actuelle sera perdue : cette action est irréversible.

Sera réinitialisé
  • main.py → code de départ
  • Historique du terminal et sortie des tests
  • Modifications non soumises
Sera conservé
  • Meilleur score du lab
  • Votre XP et votre série
  • Progression du cours et notes

Connectez-vous pour exécuter votre code.

Tous les cours sont gratuits à lire. Créez un compte ou connectez-vous pour exécuter du code, lancer les tests et soumettre ce lab.

  • IDE dans le navigateur avec terminal bash — rien à installer
  • Correction instantanée, XP et votre place dans le parcours
Créer un compteSe connecter
OU
Continuer avec GitHub