Skip to content

Généraliser en profondeur : un réseau à N couches

La leçon précédente codait en dur deux couches (W1/b1, W2/b2). Pour généraliser à un nombre arbitraire de couches, on stocke les paramètres dans un dictionnaire indexé par couche, et on boucle dessus :

class DeepNeuralNetwork:
def __init__(self, layer_sizes, n_features):
sizes = [n_features] + list(layer_sizes) + [1]
self.C = len(sizes) - 1 # nombre de couches (hors entree)
self.parametres = {}
for c in range(1, self.C + 1):
self.parametres[f"W{c}"] = np.random.randn(sizes[c], sizes[c-1]) * np.sqrt(2 / sizes[c-1])
self.parametres[f"b{c}"] = np.zeros((sizes[c], 1))

layer_sizes=[16, 16, 8] construit un réseau à 3 couches cachées (16, 16 puis 8 neurones) ; layer_sizes=[4] construit un simple réseau à 1 couche cachée de 4 neurones. C’est la même classe qui couvre les deux cas.

Propagation avant et arrière génériques

Section titled “Propagation avant et arrière génériques”
def _forward(self, X):
activations = {"A0": X}
for c in range(1, self.C + 1):
Z = self.parametres[f"W{c}"] @ activations[f"A{c-1}"] + self.parametres[f"b{c}"]
activations[f"A{c}"] = self._sigmoid(Z)
return activations

La boucle avant accumule les activations de chaque couche (A0, A1, ..., AC) dans un dictionnaire — elles seront réutilisées telles quelles pendant la rétropropagation, qui parcourt les couches en sens inverse :

for c in reversed(range(1, self.C + 1)):
A_prev = activations[f"A{c-1}"]
gradients[f"dW{c}"] = (1 / m) * dZ @ A_prev.T
gradients[f"db{c}"] = (1 / m) * np.sum(dZ, axis=1, keepdims=True)
if c > 1:
dA_prev = self.parametres[f"W{c}"].T @ dZ
dZ = dA_prev * A_prev * (1 - A_prev)

Un problème où la profondeur fait la différence

Section titled “Un problème où la profondeur fait la différence”

Sur une spirale à deux branches — plus difficile que les anneaux concentriques —, un réseau peu profond (4 neurones sur une seule couche) est mis en difficulté :

shallow = DeepNeuralNetwork(layer_sizes=[4], n_features=2)
shallow.fit(X_train, y_train, learning_rate=1.5, n_iterations=6000)
print("accuracy test:", acc_shallow)
deep = DeepNeuralNetwork(layer_sizes=[16, 16, 8], n_features=2)
deep.fit(X_train, y_train, learning_rate=1.5, n_iterations=6000)
print("accuracy test:", acc_deep)
=== Reseau peu profond (1 couche cachee de 4 neurones) ===
perte finale: 0.4593322016426831
accuracy test: 0.7833333333333333
=== Reseau profond (3 couches cachees : 16, 16, 8 neurones) ===
perte finale: 0.0018499924550939052
accuracy test: 1.0

Le réseau peu profond plafonne à 78 % : ses 4 neurones ne suffisent pas à capturer l’enroulement de la spirale. Le réseau profond, avec davantage de couches et de neurones, atteint 100 % — chaque couche supplémentaire peut recombiner les frontières apprises par la précédente en des formes de plus en plus complexes, une capacité qu’aucune augmentation du nombre d’itérations ne peut compenser pour le réseau peu profond.

Cette comparaison change à la fois la profondeur (1 → 3 couches) et le nombre total de neurones (4 → 40) : elle démontre qu’un réseau plus profond et plus large résout un problème que l’autre ne résout pas, pas que la profondeur seule, à nombre de neurones égal, serait toujours supérieure à la largeur seule — une question plus subtile, activement étudiée en recherche, qui dépasse le cadre de ce cours.

👉 Passer à un framework de production : Keras


Junior TSAFACK – 12/09/2026