Eine Funktion mit Reglern und ein Weg, sie zu drehen
Ein neuronales Netz ist nur eine Funktion mit vielen einstellbaren Zahlen, den Gewichten. Eine Schicht multipliziert die Eingaben mit einer Gewichtsmatrix und addiert einen Bias; eine Nichtlinearität wie ReLU (max(x, 0)) wird angewandt; das Ergebnis speist die nächste Schicht. So gestapelt können solche Funktionen sehr komplizierte Zusammenhänge annähern. Training heißt, die Gewichte so zu wählen, dass die Vorhersagen bekannten Zielen entsprechen: Messen Sie die Abweichung mit einem Loss (für die Prognose einer Zahl der mittlere quadratische Fehler), berechnen Sie, wie sich der Loss mit jedem Gewicht ändert, und schieben Sie jedes Gewicht ein Stück in die Richtung, die ihn senkt. Dieser Schubs, tausendfach wiederholt, ist der Gradientenabstieg.
# 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 reluGradientenabstieg am einfachsten Modell
Das Training eines Netzes nutzt Backpropagation für die Gradienten, doch die Idee ist schon für ein lineares Modell y ~ x @ w + b vollständig. Der mittlere quadratische Fehler hat den Gradienten 2/n * x.T @ err bezüglich w und 2 * mean(err) bezüglich b, mit err = Vorhersage - Ziel. Starten Sie bei Nullen, ziehen Sie die Lernrate mal Gradient ab, wiederholen Sie. Eine zu große Lernrate schießt über das Ziel hinaus und divergiert, eine zu kleine kriecht. Standardisieren Sie die Merkmale zuerst (Mittelwert null, Varianz eins), damit eine Lernrate für alle passt. Bibliotheken wie PyTorch berechnen die Gradienten automatisch, tun aber genau diese Schleife.
# 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 foundWarum Renditen für ein Netz schwer vorherzusagen sind
Ein Netz kann fast alles anpassen, und das ist die Gefahr bei Finanzdaten: Renditen sind überwiegend Rauschen, ein flexibles Modell merkt sich also das Rauschen der Trainingsperiode und scheitert in der nächsten. Overfitting zeigt sich als Trainingsfehler weit unter dem Fehler auf ungesehenen Daten. Die Abwehr entspricht den früheren Themen: gegen eine einfache Basislinie vergleichen (ein lineares Modell oder Vorhersage null); nur auf späteren Daten mit Walk-forward-Validierung auswerten und die Zeit nie mischen; wenige Merkmale und ein kleines Netz; Regularisierung (Weight Decay, Dropout) und frühes Stoppen ergänzen; und mehrere mit verschiedenen Seeds gestartete Netze mitteln. Schlägt das Netz die lineare Basislinie außerhalb der Stichprobe nicht klar, nehmen Sie das lineare Modell.
# 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- Eine Schicht ist
relu(x @ w + b); ein Netz stapelt Schichten, und der Loss (bei einer Prognose der mittlere quadratische Fehler) misst, wie weit Vorhersagen von den Zielen entfernt sind. - Training ist Gradientenabstieg: beim linearen Modell
w -= lr * 2/n * x.T @ errundb -= lr * 2 * mean(err); Merkmale skalieren, Lernrate abstimmen. - Renditen sind überwiegend Rauschen: Vergleichen Sie mit einer linearen Basislinie, validieren Sie walk-forward, nutzen Sie kleine regularisierte Netze und hören Sie auf, wenn der Gewinn außerhalb der Stichprobe verschwindet.
Als Nächstes: Reinforcement Learning für den Handel, eine Strategie aus Belohnungen statt aus beschrifteten Antworten lernen.
Lab: Was bewertet wird
- Negative mit
reluabschalten25 Pkt. - Das Netz mit
mlp_forwardvorwärts rechnen25 Pkt. - Den Fehler mit
mse_lossmessen25 Pkt. - Mit
train_lineardurch Abstieg lernen25 Pkt.