Torna alle sfide
Difficile 0 risolte 0% di accettazione

Retropropagazione in una rete a due strati

Calcola tutti i gradienti di pesi e bias di una rete con uno strato nascosto sigmoide e un output sigmoide.

Cosa imparerai

  • Calcolare tutti i gradienti di una rete a due layer
  • Far scorrere gli errori all'indietro tra i layer
  • Collegare la matematica al codice passo dopo passo

Problema

La retropropagazione applica la regola della catena strato per strato, dall'output verso gli input. La rete ha n input, h neuroni nascosti e un output, tutti con attivazione sigmoide (σ(z) = 1 / (1 + e^(-z))):

  • strato nascosto: a_j = σ(inputs · hidden_weights[j] + hidden_biases[j])
  • output: y = σ(a · output_weights + output_bias)
  • perdita: L = (y - target)²

Gradienti:

  1. delta_out = 2 × (y - target) × y × (1 - y)
  2. Strato di output: dL/doutput_weights[j] = delta_out × a_j e dL/doutput_bias = delta_out
  3. delta_j = delta_out × output_weights[j] × a_j × (1 - a_j)
  4. Strato nascosto: dL/dhidden_weights[j][i] = delta_j × inputs[i] e dL/dhidden_biases[j] = delta_j

Implementa backprop_gradients(inputs, hidden_weights, hidden_biases, output_weights, output_bias, target).

Output

Restituisci una matrice con h + 1 righe:

  • Riga j (per ogni neurone nascosto j, in ordine): gli n gradienti di hidden_weights[j], poi il gradiente di hidden_biases[j].
  • Ultima riga: gli h gradienti di output_weights, poi il gradiente di output_bias.

Vincoli

1 ≤ n, h ≤ 20

-5 ≤ ogni input, peso e bias ≤ 5

0 ≤ target ≤ 1

Esempi

Esempio 1
Input:
inputs = [1.0]
target = 1.0
output_bias = 0.0
hidden_biases = [0.0]
hidden_weights = [
    [0.0]
]
output_weights = [0.0]

Output atteso:
expected = [
    [0.0, 0.0],
    [-0.125, -0.25]
]
Input:
std::vector<double> inputs = {1.0};
double target = 1.0;
double output_bias = 0.0;
std::vector<double> hidden_biases = {0.0};
std::vector<std::vector<double>> hidden_weights = {
    {0.0}
};
std::vector<double> output_weights = {0.0};

Output atteso:
std::vector<std::vector<double>> expected = {
    {0.0, 0.0},
    {-0.125, -0.25}
};
Input:
double[] inputs = new double[] {1.0};
double target = 1.0;
double output_bias = 0.0;
double[] hidden_biases = new double[] {0.0};
double[][] hidden_weights = new double[][] {
    new double[] {0.0}
};
double[] output_weights = new double[] {0.0};

Output atteso:
double[][] expected = new double[][] {
    new double[] {0.0, 0.0},
    new double[] {-0.125, -0.25}
};

Spiegazione

a = σ(0) = 0.5 e y = σ(0) = 0.5. delta_out = 2 × (0.5 - 1) × 0.25 = -0.25. Riga di output: [-0.25 × 0.5, -0.25] = [-0.125, -0.25]. delta_1 = -0.25 × 0 × 0.25 = 0, quindi la riga nascosta è [0, 0]: un peso di output nullo blocca il gradiente.

Esempio 2
Input:
inputs = [1.0, 0.0]
target = 0.0
output_bias = -1.0
hidden_biases = [0.0, 0.0]
hidden_weights = [
    [0.0, 1.0],
    [0.0, -1.0]
]
output_weights = [1.0, 1.0]

Output atteso:
expected = [
    [0.0625, 0.0, 0.0625],
    [0.0625, 0.0, 0.0625],
    [0.125, 0.125, 0.25]
]
Input:
std::vector<double> inputs = {1.0, 0.0};
double target = 0.0;
double output_bias = -1.0;
std::vector<double> hidden_biases = {0.0, 0.0};
std::vector<std::vector<double>> hidden_weights = {
    {0.0, 1.0},
    {0.0, -1.0}
};
std::vector<double> output_weights = {1.0, 1.0};

Output atteso:
std::vector<std::vector<double>> expected = {
    {0.0625, 0.0, 0.0625},
    {0.0625, 0.0, 0.0625},
    {0.125, 0.125, 0.25}
};
Input:
double[] inputs = new double[] {1.0, 0.0};
double target = 0.0;
double output_bias = -1.0;
double[] hidden_biases = new double[] {0.0, 0.0};
double[][] hidden_weights = new double[][] {
    new double[] {0.0, 1.0},
    new double[] {0.0, -1.0}
};
double[] output_weights = new double[] {1.0, 1.0};

Output atteso:
double[][] expected = new double[][] {
    new double[] {0.0625, 0.0, 0.0625},
    new double[] {0.0625, 0.0, 0.0625},
    new double[] {0.125, 0.125, 0.25}
};

Spiegazione

Entrambi i valori nascosti sono σ(0) = 0.5, quindi y = σ(0.5 + 0.5 - 1) = 0.5 e delta_out = 2 × 0.5 × 0.25 = 0.25. Riga di output: [0.125, 0.125, 0.25]. Ogni delta_j = 0.25 × 1 × 0.25 = 0.0625, che dà le righe nascoste [0.0625, 0, 0.0625] (il secondo input è 0).

Come affrontarlo

Prova prima da solo, poi apri un suggerimento alla volta.

Suggerimento 1

Lavora all'indietro a partire prima dal neurone di output, poi riusa quel segnale per calcolare il contributo di ogni neurone nascosto.

Suggerimento 2

Ti serve un delta per il layer di output e un delta per ogni neurone nascosto, entrambi basati sulle derivate della sigmoid.

Suggerimento 3

Fai attenzione a usare il peso di uscita del singolo neurone nascosto quando propaghi l'errore verso di lui.

Complessità attesa

tempo O(hn), spazio aggiuntivo O(hn)

Riferimenti

Questa sfida è adattata per CodeUtility. Segui la traccia e i vincoli sopra.

Impara dagli altri

Soluzioni della community

Nessuna soluzione pubblicata. Risolvi questa sfida per condividere la prima.