Problem
XOR (exklusives Oder) ist das klassische Problem, das ein einzelnes Neuron nicht lösen kann, ein Netz mit einer verborgenen Schicht aber schon. Trainiere ein Netz mit 2 Eingaben, 2 verborgenen Neuronen und 1 Ausgabe, alle mit Sigmoid (σ(z) = 1 / (1 + e^(-z))), mit dem Verlust L = (y - target)².
Die Trainingsdaten, in jeder Epoche immer in dieser Reihenfolge verwendet:
| Eingabe | target |
|---|---|
| [0, 0] | 0 |
| [0, 1] | 1 |
| [1, 0] | 1 |
| [1, 1] | 0 |
Für jedes Beispiel:
- Vorwärts: a_j = σ(x₁ × hidden_weights[j][0] + x₂ × hidden_weights[j][1] + hidden_biases[j]), y = σ(a₁ × output_weights[0] + a₂ × output_weights[1] + output_bias)
- delta_out = 2 × (y - target) × y × (1 - y) und delta_j = delta_out × output_weights[j] × a_j × (1 - a_j), mit den Gewichten vor der Aktualisierung für dieses Beispiel
- Aktualisiere jeden Parameter mit der Lernrate lr: output_weights[j] -= lr × delta_out × a_j, output_bias -= lr × delta_out, hidden_weights[j][i] -= lr × delta_j × x_i, hidden_biases[j] -= lr × delta_j
Implementiere train_xor(hidden_weights, hidden_biases, output_weights, output_bias, learning_rate, epochs).
Ausgabe
Gib nach dem Training die Vorhersagen y des Netzes für die Eingaben [0, 0], [0, 1], [1, 0] und [1, 1] in dieser Reihenfolge zurück.
Einschränkungen
0 ≤ epochs ≤ 5.000
0 < learning_rate ≤ 5
-2 ≤ jedes Startgewicht und jeder Start-Bias ≤ 2
Beispiele
Eingabe:
epochs = 0
output_bias = 0.0
hidden_biases = [0.0, 0.3]
learning_rate = 1.5
hidden_weights = [
[0.2, 0.8],
[0.9, -0.7]
]
output_weights = [-0.6, 0.4]
Erwartete Ausgabe:
expected = [0.482451, 0.436972, 0.494378, 0.452729]
Eingabe:
long long epochs = 0;
double output_bias = 0.0;
std::vector<double> hidden_biases = {0.0, 0.3};
double learning_rate = 1.5;
std::vector<std::vector<double>> hidden_weights = {
{0.2, 0.8},
{0.9, -0.7}
};
std::vector<double> output_weights = {-0.6, 0.4};
Erwartete Ausgabe:
std::vector<double> expected = {0.482451, 0.436972, 0.494378, 0.452729};
Eingabe:
long epochs = 0;
double output_bias = 0.0;
double[] hidden_biases = new double[] {0.0, 0.3};
double learning_rate = 1.5;
double[][] hidden_weights = new double[][] {
new double[] {0.2, 0.8},
new double[] {0.9, -0.7}
};
double[] output_weights = new double[] {-0.6, 0.4};
Erwartete Ausgabe:
double[] expected = new double[] {0.482451, 0.436972, 0.494378, 0.452729};
Erläuterung
Bei 0 Epochen wird nichts trainiert, also ist das Ergebnis einfach der Vorwärtsdurchlauf mit den Startgewichten: etwa [0.482, 0.437, 0.494, 0.453]. Das untrainierte Netz gibt jeder Eingabe fast dieselbe Antwort.
Eingabe:
epochs = 2000
output_bias = 0.0
hidden_biases = [0.0, 0.3]
learning_rate = 1.5
hidden_weights = [
[0.2, 0.8],
[0.9, -0.7]
]
output_weights = [-0.6, 0.4]
Erwartete Ausgabe:
expected = [0.015727, 0.982119, 0.985515, 0.013682]
Eingabe:
long long epochs = 2000;
double output_bias = 0.0;
std::vector<double> hidden_biases = {0.0, 0.3};
double learning_rate = 1.5;
std::vector<std::vector<double>> hidden_weights = {
{0.2, 0.8},
{0.9, -0.7}
};
std::vector<double> output_weights = {-0.6, 0.4};
Erwartete Ausgabe:
std::vector<double> expected = {0.015727, 0.982119, 0.985515, 0.013682};
Eingabe:
long epochs = 2000;
double output_bias = 0.0;
double[] hidden_biases = new double[] {0.0, 0.3};
double learning_rate = 1.5;
double[][] hidden_weights = new double[][] {
new double[] {0.2, 0.8},
new double[] {0.9, -0.7}
};
double[] output_weights = new double[] {-0.6, 0.4};
Erwartete Ausgabe:
double[] expected = new double[] {0.015727, 0.982119, 0.985515, 0.013682};
Erläuterung
Nach 2.000 Epochen liegen die Vorhersagen bei etwa [0.016, 0.982, 0.986, 0.014]: niedrig für [0, 0] und [1, 1], hoch für [0, 1] und [1, 0]. Das Netz hat XOR gelernt.
So gehst du vor
Versuch es zuerst allein und öffne dann einen Hinweis nach dem anderen.
Hinweis 1
Verarbeite die vier XOR-Samples in jeder Epoche in derselben festen Reihenfolge und aktualisiere die Parameter sofort nach jedem einzelnen.
Hinweis 2
Nutze Vorwärtsdurchlauf und leite dann den Output-Fehler mit den aktuellen Gewichten auf beide Hidden-Neuronen zurück, bevor du updatest.
Hinweis 3
Achte auf die Sigmoid-Ableitung und darauf, dass die Hidden-Layer-Updates von den Inputwerten abhängen, sodass bei nullen Inputs manche Gewichte unverändert bleiben.
Erwartete Komplexität
Zeit O(epochs), zusätzlicher Speicher O(1)
Referenzen
Diese Aufgabe wurde für CodeUtility angepasst. Beachte die oben genannten Vorgaben und Einschränkungen.