Problema
Lo XOR (o esclusivo) è il problema classico che un singolo neurone non può risolvere, ma una rete con uno strato nascosto sì. Addestra una rete con 2 input, 2 neuroni nascosti e 1 output, tutti sigmoidi (σ(z) = 1 / (1 + e^(-z))), usando la perdita L = (y - target)².
I dati di addestramento, usati sempre in questo ordine in ogni epoca:
| input | target |
|---|---|
| [0, 0] | 0 |
| [0, 1] | 1 |
| [1, 0] | 1 |
| [1, 1] | 0 |
Per ogni esempio:
- In avanti: 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), e delta_j = delta_out × output_weights[j] × a_j × (1 - a_j), usando i pesi prima dell'aggiornamento di questo esempio
- Aggiorna ogni parametro con il tasso di apprendimento 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
Implementa train_xor(hidden_weights, hidden_biases, output_weights, output_bias, learning_rate, epochs).
Output
Dopo l'addestramento, restituisci le previsioni y della rete per gli input [0, 0], [0, 1], [1, 0] e [1, 1], in quest'ordine.
Vincoli
0 ≤ epochs ≤ 5.000
0 < learning_rate ≤ 5
-2 ≤ ogni peso e bias iniziale ≤ 2
Esempi
Input:
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]
Output atteso:
expected = [0.482451, 0.436972, 0.494378, 0.452729]
Input:
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};
Output atteso:
std::vector<double> expected = {0.482451, 0.436972, 0.494378, 0.452729};
Input:
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};
Output atteso:
double[] expected = new double[] {0.482451, 0.436972, 0.494378, 0.452729};
Spiegazione
Con 0 epoche non si addestra nulla, quindi il risultato è solo la propagazione in avanti con i pesi iniziali: circa [0.482, 0.437, 0.494, 0.453]. La rete non addestrata dà quasi la stessa risposta a ogni input.
Input:
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]
Output atteso:
expected = [0.015727, 0.982119, 0.985515, 0.013682]
Input:
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};
Output atteso:
std::vector<double> expected = {0.015727, 0.982119, 0.985515, 0.013682};
Input:
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};
Output atteso:
double[] expected = new double[] {0.015727, 0.982119, 0.985515, 0.013682};
Spiegazione
Dopo 2.000 epoche le previsioni sono circa [0.016, 0.982, 0.986, 0.014]: basse per [0, 0] e [1, 1], alte per [0, 1] e [1, 0]. La rete ha imparato lo XOR.
Come affrontarlo
Prova prima da solo, poi apri un suggerimento alla volta.
Suggerimento 1
Processa i quattro campioni XOR nello stesso ordine fisso a ogni epoch, e aggiorna i parametri subito dopo ciascuno.
Suggerimento 2
Usa la forward propagation, poi fai backpropagate dell’errore dell’output in entrambi i neuroni nascosti con i pesi correnti prima di applicare gli aggiornamenti.
Suggerimento 3
Fai attenzione alla derivata della sigmoid e al fatto che gli aggiornamenti del layer nascosto dipendono dai valori di input, quindi gli input zero lasciano invariati alcuni pesi.
Complessità attesa
tempo O(epochs), spazio aggiuntivo O(1)
Riferimenti
Questa sfida è adattata per CodeUtility. Segui la traccia e i vincoli sopra.