Problem
Adam führt pro Gewicht zwei gleitende Mittelwerte, des Gradienten (m) und des quadrierten Gradienten (v), und gibt damit jedem Gewicht eine eigene Schrittweite.
Beginne mit m = 0 und v = 0 für jedes Gewicht. Für jeden Schritt t = 1, 2, 3, … mit Gradient g:
- m ← beta1 × m + (1 - beta1) × g
- v ← beta2 × v + (1 - beta2) × g²
- m̂ = m / (1 - beta1ᵗ) und v̂ = v / (1 - beta2ᵗ) (Bias-Korrektur)
- w ← w - learning_rate × m̂ / (√v̂ + epsilon)
Implementiere adam_steps(gradients, weights, learning_rate, beta1, beta2, epsilon) und wende pro Zeile von gradients ein Update an, in Reihenfolge.
Eingabe
gradients: Eine Zeile pro Schritt; jede Zeile hat einen Gradienten pro Gewicht.
weights: Die Startgewichte.
learning_rate, beta1, beta2, epsilon: Die Hyperparameter von Adam (üblich sind 0.001, 0.9, 0.999 und 0.00000001).
Ausgabe
Gib die Gewichte nach allen Schritten zurück.
Einschränkungen
0 ≤ Schritte ≤ 1.000, 1 ≤ Gewichte ≤ 100
0 < learning_rate ≤ 1, 0 ≤ beta1, beta2 < 1, 0 < epsilon ≤ 0.001
-100 ≤ jeder Wert ≤ 100
Beispiele
Eingabe:
beta1 = 0.9
beta2 = 0.999
epsilon = 1.0e-08
weights = [1.0]
gradients = [
[2.0]
]
learning_rate = 0.1
Erwartete Ausgabe:
expected = [0.9]
Eingabe:
double beta1 = 0.9;
double beta2 = 0.999;
double epsilon = 1.0e-08;
std::vector<double> weights = {1.0};
std::vector<std::vector<double>> gradients = {
{2.0}
};
double learning_rate = 0.1;
Erwartete Ausgabe:
std::vector<double> expected = {0.9};
Eingabe:
double beta1 = 0.9;
double beta2 = 0.999;
double epsilon = 1.0e-08;
double[] weights = new double[] {1.0};
double[][] gradients = new double[][] {
new double[] {2.0}
};
double learning_rate = 0.1;
Erwartete Ausgabe:
double[] expected = new double[] {0.9};
Erläuterung
m = 0.1 × 2 = 0.2 und v = 0.001 × 4 = 0.004. Die Bias-Korrektur ergibt m̂ = 0.2 / 0.1 = 2 und v̂ = 0.004 / 0.001 = 4, also ist der Schritt 0.1 × 2 / √4 = 0.1: w = 0.9. Im ersten Schritt bewegt Adam jedes Gewicht ungefähr um die Lernrate, unabhängig von der Größe des Gradienten.
Eingabe:
beta1 = 0.9
beta2 = 0.999
epsilon = 1.0e-08
weights = [0.0, 0.0]
gradients = [
[100.0, 0.01]
]
learning_rate = 0.01
Erwartete Ausgabe:
expected = [-0.01, -0.01]
Eingabe:
double beta1 = 0.9;
double beta2 = 0.999;
double epsilon = 1.0e-08;
std::vector<double> weights = {0.0, 0.0};
std::vector<std::vector<double>> gradients = {
{100.0, 0.01}
};
double learning_rate = 0.01;
Erwartete Ausgabe:
std::vector<double> expected = {-0.01, -0.01};
Eingabe:
double beta1 = 0.9;
double beta2 = 0.999;
double epsilon = 1.0e-08;
double[] weights = new double[] {0.0, 0.0};
double[][] gradients = new double[][] {
new double[] {100.0, 0.01}
};
double learning_rate = 0.01;
Erwartete Ausgabe:
double[] expected = new double[] {-0.01, -0.01};
Erläuterung
Die Gradienten unterscheiden sich um den Faktor 10.000, trotzdem bewegen sich beide Gewichte um etwa 0.01, auf [-0.01, -0.01]: Adam skaliert jeden Schritt mit der Größe des jeweiligen Gradienten.
So gehst du vor
Versuch es zuerst allein und öffne dann einen Hinweis nach dem anderen.
Hinweis 1
Verfolge pro Gewicht zwei gleitende Mittelwerte: einen für die Gradientenrichtung und einen für seine quadrierte Größe.
Hinweis 2
Wende die Bias-Korrektur mit der aktuellen Schrittzahl an, bevor du durch den Wurzelterm teilst.
Hinweis 3
Achte im Nenner auf epsilon, denn es verhindert eine Division durch null und verändert das finale Update leicht.
Erwartete Komplexität
Zeit O(s × w), zusätzlicher Speicher O(w)
Referenzen
Diese Aufgabe wurde für CodeUtility angepasst. Beachte die oben genannten Vorgaben und Einschränkungen.