Volver a los desafíos
Difícil 0 resueltos 0% de aceptación

Retropropagación en una red de dos capas

Calcula todos los gradientes de pesos y sesgos de una red con una capa oculta sigmoide y una salida sigmoide.

Lo que aprenderás

  • Calcular todos los gradientes de una red de dos capas
  • Pasar los errores hacia atrás por las capas
  • Conectar las matemáticas con el código paso a paso

Problema

Ver original (inglés)

La retropropagación aplica la regla de la cadena capa por capa, desde la salida hacia las entradas. La red tiene n entradas, h neuronas ocultas y una salida, todas con activación sigmoide (σ(z) = 1 / (1 + e^(-z))):

  • capa oculta: a_j = σ(inputs · hidden_weights[j] + hidden_biases[j])
  • salida: y = σ(a · output_weights + output_bias)
  • pérdida: L = (y - target)²

Gradientes:

  1. delta_out = 2 × (y - target) × y × (1 - y)
  2. Capa de salida: dL/doutput_weights[j] = delta_out × a_j y dL/doutput_bias = delta_out
  3. delta_j = delta_out × output_weights[j] × a_j × (1 - a_j)
  4. Capa oculta: dL/dhidden_weights[j][i] = delta_j × inputs[i] y dL/dhidden_biases[j] = delta_j

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

Salida

Devuelve una matriz con h + 1 filas:

  • Fila j (para cada neurona oculta j, en orden): los n gradientes de hidden_weights[j] y después el gradiente de hidden_biases[j].
  • Última fila: los h gradientes de output_weights y después el gradiente de output_bias.

Restricciones

1 ≤ n, h ≤ 20

-5 ≤ cada entrada, peso y sesgo ≤ 5

0 ≤ target ≤ 1

Ejemplos

Ejemplo 1
Entrada:
inputs = [1.0]
target = 1.0
output_bias = 0.0
hidden_biases = [0.0]
hidden_weights = [
    [0.0]
]
output_weights = [0.0]

Salida esperada:
expected = [
    [0.0, 0.0],
    [-0.125, -0.25]
]
Entrada:
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};

Salida esperada:
std::vector<std::vector<double>> expected = {
    {0.0, 0.0},
    {-0.125, -0.25}
};
Entrada:
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};

Salida esperada:
double[][] expected = new double[][] {
    new double[] {0.0, 0.0},
    new double[] {-0.125, -0.25}
};

Explicación

a = σ(0) = 0.5 e y = σ(0) = 0.5. delta_out = 2 × (0.5 - 1) × 0.25 = -0.25. Fila de salida: [-0.25 × 0.5, -0.25] = [-0.125, -0.25]. delta_1 = -0.25 × 0 × 0.25 = 0, así que la fila oculta es [0, 0]: un peso de salida igual a cero bloquea el gradiente.

Ejemplo 2
Entrada:
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]

Salida esperada:
expected = [
    [0.0625, 0.0, 0.0625],
    [0.0625, 0.0, 0.0625],
    [0.125, 0.125, 0.25]
]
Entrada:
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};

Salida esperada:
std::vector<std::vector<double>> expected = {
    {0.0625, 0.0, 0.0625},
    {0.0625, 0.0, 0.0625},
    {0.125, 0.125, 0.25}
};
Entrada:
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};

Salida esperada:
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}
};

Explicación

Los dos valores ocultos son σ(0) = 0.5, así que y = σ(0.5 + 0.5 - 1) = 0.5 y delta_out = 2 × 0.5 × 0.25 = 0.25. Fila de salida: [0.125, 0.125, 0.25]. Cada delta_j = 0.25 × 1 × 0.25 = 0.0625, lo que da las filas ocultas [0.0625, 0, 0.0625] (la segunda entrada es 0).

Cómo abordarlo

Inténtalo primero por tu cuenta y luego abre las pistas una a una.

Pista 1

Trabaja hacia atrás desde la neurona de salida primero, y luego reutiliza esa señal para calcular la contribución de cada neurona oculta.

Pista 2

Necesitas un delta para la capa de salida y un delta por cada neurona oculta, ambos basados en derivadas de sigmoid.

Pista 3

Ten cuidado de usar el peso de salida propio de cada neurona oculta al propagar el error hacia atrás a esa neurona.

Complejidad esperada

tiempo O(hn), espacio extra O(hn)

Referencias

Este reto está adaptado para CodeUtility. Sigue el enunciado y las restricciones indicadas arriba.

Aprende de otros

Soluciones de la comunidad

Aún no hay soluciones publicadas. Resuelve este reto para compartir la primera.