Retour aux défis
Difficile 0 résolus 0 % de réussite

Rétropropagation dans un réseau à deux couches

Calculez tous les gradients des poids et des biais d'un réseau avec une couche cachée sigmoïde et une sortie sigmoïde.

Ce que vous allez apprendre

  • Calculer tous les gradients d'un réseau à deux couches
  • Faire remonter les erreurs à travers les couches
  • Relier les maths au code étape par étape

Problème

La rétropropagation applique la règle de dérivation en chaîne couche par couche, de la sortie vers les entrées. Le réseau a n entrées, h neurones cachés et une sortie, tous avec une activation sigmoïde (σ(z) = 1 / (1 + e^(-z))) :

  • couche cachée : a_j = σ(inputs · hidden_weights[j] + hidden_biases[j])
  • sortie : y = σ(a · output_weights + output_bias)
  • perte : L = (y - target)²

Gradients :

  1. delta_out = 2 × (y - target) × y × (1 - y)
  2. Couche de sortie : dL/doutput_weights[j] = delta_out × a_j et dL/doutput_bias = delta_out
  3. delta_j = delta_out × output_weights[j] × a_j × (1 - a_j)
  4. Couche cachée : dL/dhidden_weights[j][i] = delta_j × inputs[i] et dL/dhidden_biases[j] = delta_j

Implémentez backprop_gradients(inputs, hidden_weights, hidden_biases, output_weights, output_bias, target).

Sortie

Renvoyez une matrice de h + 1 lignes :

  • Ligne j (pour chaque neurone caché j, dans l'ordre) : les n gradients de hidden_weights[j], puis le gradient de hidden_biases[j].
  • Dernière ligne : les h gradients de output_weights, puis le gradient de output_bias.

Contraintes

1 ≤ n, h ≤ 20

-5 ≤ chaque entrée, poids et biais ≤ 5

0 ≤ target ≤ 1

Exemples

Exemple 1
Entrée :
inputs = [1.0]
target = 1.0
output_bias = 0.0
hidden_biases = [0.0]
hidden_weights = [
    [0.0]
]
output_weights = [0.0]

Sortie attendue :
expected = [
    [0.0, 0.0],
    [-0.125, -0.25]
]
Entrée :
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};

Sortie attendue :
std::vector<std::vector<double>> expected = {
    {0.0, 0.0},
    {-0.125, -0.25}
};
Entrée :
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};

Sortie attendue :
double[][] expected = new double[][] {
    new double[] {0.0, 0.0},
    new double[] {-0.125, -0.25}
};

Explication

a = σ(0) = 0.5 et y = σ(0) = 0.5. delta_out = 2 × (0.5 - 1) × 0.25 = -0.25. Ligne de sortie : [-0.25 × 0.5, -0.25] = [-0.125, -0.25]. delta_1 = -0.25 × 0 × 0.25 = 0, donc la ligne cachée vaut [0, 0] : un poids de sortie nul bloque le gradient.

Exemple 2
Entrée :
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]

Sortie attendue :
expected = [
    [0.0625, 0.0, 0.0625],
    [0.0625, 0.0, 0.0625],
    [0.125, 0.125, 0.25]
]
Entrée :
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};

Sortie attendue :
std::vector<std::vector<double>> expected = {
    {0.0625, 0.0, 0.0625},
    {0.0625, 0.0, 0.0625},
    {0.125, 0.125, 0.25}
};
Entrée :
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};

Sortie attendue :
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}
};

Explication

Les deux valeurs cachées valent σ(0) = 0.5, donc y = σ(0.5 + 0.5 - 1) = 0.5 et delta_out = 2 × 0.5 × 0.25 = 0.25. Ligne de sortie : [0.125, 0.125, 0.25]. Chaque delta_j = 0.25 × 1 × 0.25 = 0.0625, ce qui donne les lignes cachées [0.0625, 0, 0.0625] (la deuxième entrée vaut 0).

Comment l'aborder

Essayez d'abord seul, puis ouvrez les indices un par un.

Indice 1

Travaille d’abord à rebours depuis le neurone de sortie, puis réutilise ce signal pour calculer la contribution de chaque neurone caché.

Indice 2

Il te faut un delta pour la couche de sortie et un delta par neurone caché, tous basés sur les dérivées de sigmoid.

Indice 3

Fais attention à utiliser le poids de sortie propre à chaque neurone caché quand tu propages l’erreur vers lui.

Complexité attendue

temps O(hn), espace supplémentaire O(hn)

Références

Ce défi a été adapté pour CodeUtility. Suivez l'énoncé et les contraintes ci-dessus.

Apprenez des autres

Solutions de la communauté

Aucune solution publiée pour l'instant. Résolvez ce défi pour partager la première.