CSI 4106 - Automne 2025
Version: oct. 26, 2025 12h06
Apprentissage de représentations par rétropropagation des erreurs
David E. Rumelhart, Geoffrey E. Hinton & Ronald J. Williams
Nous décrivons une nouvelle procédure d’apprentissage, la rétropropagation, pour les réseaux d’unités semblables à des neurones. La procédure ajuste à plusieurs reprises les poids des connexions dans le réseau afin de minimiser une mesure de la différence entre le vecteur de sortie réel du réseau et le vecteur de sortie souhaité. En conséquence des ajustements de poids, les unités internes ‘cachées’ qui ne font pas partie de l’entrée ou de la sortie en viennent à représenter des attributs importants du domaine de la tâche, et les régularités de la tâche sont capturées par les interactions de ces unités. La capacité à créer de nouveaux attributs utiles distingue la rétropropagation des méthodes antérieures, plus simples, telles que la procédure de convergence du perceptron.
Des limitations, telles que l’incapacité à résoudre la tâche de classification XOR, ont essentiellement stoppé la recherche sur les réseaux neuronaux.
Le perceptron était limité à une seule couche, et il n’existait aucune méthode connue pour entraîner un perceptron multi-couches.
Les perceptrons à une seule couche sont limités à résoudre des tâches de classification qui sont linéairement séparables.
Le modèle utilise l’erreur quadratique moyenne comme fonction de perte.
La descente de gradient est utilisée pour minimiser la perte.
Une fonction d’activation sigmoïde est utilisée au lieu d’une fonction de seuil, car sa dérivée fournit des informations précieuses pour la descente de gradient.
Montre comment mettre à jour les poids internes en utilisant un algorithme en deux passes consistant en une passe avant et une passe arrière.
Permet l’entraînement des perceptrons multi-couches.
Rétropropagation (backprop) est un algorithme pour calculer méthodiquement les dérivées partielles de la fonction de perte d’un réseau de neurones par rapport à chaque paramètre de poids et de biais.
Rétropropagation applique la règle de la chaîne (chain rule) du calcul différentiel récursivement pour calculer \(\frac{\partial J}{\partial w_{i,j}^{(\ell)}}\) pour tous les paramètres du réseau \(w_{i,j}^{(\ell)}\) de manière efficace, en utilisant des quantités intermédiaires du passage avant, où \(w_{i,j}^{(\ell)}\) désigne le paramètre \(w_{i,j}\) de la couche \(\ell\).
Étant donné,
\[ h(x) = f(g(x)) \]
en utilisant la notation de Lagrange, nous avons
\[ h^\prime(x) = f^\prime(g(x)) g^\prime(x) \]
ou de manière équivalente en utilisant la notation de Leibniz
\[ \frac{dz}{dx} = \frac{dz}{dy} \cdot \frac{dy}{dx} \]
\[ \frac{\partial J}{\partial \hat{y}} \]
\[ \frac{\partial \hat{y}}{\partial z_2} \]
\[ \frac{\partial z_2}{\partial w_2}, \quad \frac{\partial z_2}{\partial b_2}, \quad \frac{\partial z_2}{\partial a_1}, \]
\[ \frac{\partial a_1}{\partial z_1}, \]
\[ \frac{\partial z_1}{\partial w_1}, \quad \frac{\partial z_1}{\partial b_1}, \quad \frac{\partial z_1}{\partial x}, \]
Pour \(w_2\) :
\[ \frac{\partial J}{\partial w_2}=\frac{\partial J}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z_2} \cdot \frac{\partial z_2}{\partial w_2}=\left[-\left(\frac{y}{\hat{y}}-\frac{1-y}{1-\hat{y}}\right)\right] \cdot(\hat{y}(1-\hat{y})) \cdot a_1 \]
Se simplifie en :
\[ \frac{\partial J}{\partial w_2}=(\hat{y}-y) a_1 \]
Pour \(w_1\) :
\[ \frac{\partial J}{\partial w_1}=\frac{\partial J}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z_2} \cdot \frac{\partial z_2}{\partial a_1} \cdot \frac{\partial a_1}{\partial z_1} \cdot \frac{\partial z_1}{\partial w_1} \]
Substituer :
\[ =\left[-\left(\frac{y}{\hat{y}}-\frac{1-y}{1-\hat{y}}\right)\right] \cdot(\hat{y}(1-\hat{y})) \cdot w_2 \cdot\left(a_1\left(1-a_1\right)\right) \cdot x \]
Se simplifie à :
\[ \frac{\partial J}{\partial w_1}=(\hat{y}-y) w_2\left(a_1\left(1-a_1\right)\right) x \]
Pour \(b_1\) :
\[ \frac{\partial J}{\partial b_1}=\frac{\partial J}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z_2} \cdot \frac{\partial z_2}{\partial a_1} \cdot \frac{\partial a_1}{\partial z_1} \cdot \frac{\partial z_1}{\partial b_1} \]
Substituer :
\[ =(\hat{y}-y) w_2\left(a_1\left(1-a_1\right)\right) \cdot 1 \]
Se simplifie à :
\[ \frac{\partial J}{\partial b_1}=(\hat{y}-y) w_2\left(a_1\left(1-a_1\right)\right) \]
\[ \begin{align} \frac{\partial J}{\partial w_2} & = (\hat{y}-y) a_1 \\ \frac{\partial J}{\partial b_2} & = \hat{y}-y \\ \frac{\partial J}{\partial w_1} & = (\hat{y}-y) w_2\left(a_1\left(1-a_1\right)\right) x \\ \frac{\partial J}{\partial b_1} & = (\hat{y}-y) w_2\left(a_1\left(1-a_1\right)\right) \\ \end{align} \]
def backward():
global alpha, w1, b1, w2, b2, a1, z1, z2, y, y_hat
grad_J_w2 = (y_hat - y) * a1
grad_J_b2 = y_hat - y
grad_J_w1 = (y_hat - y) * w2 * (a1 * (1-a1)) * x
grad_J_b1 = (y_hat - y) * w2 * (a1 * (1-a1))
w2 = w2 - alpha * grad_J_w2
b2 = b2 - alpha * grad_J_b2
w1 = w1 - alpha * grad_J_w1
b1 = b1 - alpha * grad_J_b1\[ \begin{align} \frac{\partial J}{\partial w_2} & = \frac{\partial J}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z_2} \cdot \frac{\partial z_2}{\partial w_2}\\ \frac{\partial J}{\partial b_2} & = \frac{\partial J}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z_2} \cdot \frac{\partial z_2}{\partial b_2}\\ \frac{\partial J}{\partial w_1} & = \frac{\partial J}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z_2} \cdot \frac{\partial z_2}{\partial a_1} \cdot \frac{\partial a_1}{\partial z_1} \cdot \frac{\partial z_1}{\partial w_1}\\ \frac{\partial J}{\partial b_1} & = \frac{\partial J}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z_2} \cdot \frac{\partial z_2}{\partial a_1} \cdot \frac{\partial a_1}{\partial z_1} \cdot \frac{\partial z_1}{\partial b_1}\\ \end{align} \]
Soit \[ \begin{align} \delta_1 & = \frac{\partial J}{\partial \hat{y}} \cdot \frac{\partial \hat{y}}{\partial z_2} \\ \delta_2 & = \delta_1 \cdot \frac{\partial z_2}{\partial a_1} \cdot \frac{\partial a_1}{\partial z_1} \end{align} \]
Réécrire \[ \begin{align} \frac{\partial J}{\partial w_2} & = \delta_1 \cdot \frac{\partial z_2}{\partial w_2}\\ \frac{\partial J}{\partial b_2} & = \delta_1 \cdot \frac{\partial z_2}{\partial b_2}\\ \frac{\partial J}{\partial w_1} & = \delta_2 \cdot \frac{\partial z_1}{\partial w_1}\\ \frac{\partial J}{\partial b_1} & = \delta_2 \cdot \frac{\partial z_1}{\partial b_1}\\ \end{align} \]
(Création du Graphe computationnel)
Initialisation
Passage avant
Calcul de la perte
Passage arrière (Rétropropagation)
Mettre à jour les paramètres et répéter de 3 à 6.
(Créer le graphe computationnel.)
Initialiser les poids et biais.
Passage avant : en partant de l’entrée, calculer la sortie de chaque opération dans le graphe et stocker ces valeurs.
Calculer la perte.
Passage arrière : en partant de la sortie et en revenant en arrière, pour chaque opération.
Calculer la dérivée de la sortie par rapport à chacune des entrées.
Pour chaque entrée \(u\),
\[ \delta_u = \frac{\partial J}{\partial u} = \frac{\partial z}{\partial u} \cdot \frac{\partial J}{\partial z} \]
Initialiser les poids et biais du réseau de neurones.
Pour chaque exemple dans le jeu d’entraînement (ou dans un mini-lot) :
Couche d’Entrée : Transmettre les attributs d’entrée à la première couche.
Couches Cachées : Pour chaque couche cachée, calculer les activations (sortie) en appliquant la somme pondérée des entrées plus le biais, suivie d’une fonction d’activation (par exemple, sigmoïde, ReLU).
Couche de Sortie : Même processus que pour les couches cachées. Les activations de la couche de sortie représentent les valeurs prédites.
Calculez la perte (erreur) en utilisant une fonction de perte appropriée en comparant les valeurs prédites aux valeurs cibles réelles.
Couche de sortie : Calculer le gradient de la perte par rapport aux poids et biais de la couche de sortie en utilisant la règle de chaîne du calcul différentiel.
Couches cachées : Propager l’erreur en arrière à travers le réseau, couche par couche. Pour chaque couche, calculer le gradient de la perte par rapport aux poids et biais. Utiliser la dérivée de la fonction d’activation pour aider à calculer ces gradients.
Mettre à jour les poids et biais : Ajuster les poids et biais en utilisant les gradients calculés et un taux d’apprentissage, qui détermine la taille de chaque mise à jour.
Ajuster itérativement les paramètres pour réduire la différence entre les sorties prédites et réelles.
Utilise la descente de gradient sur la fonction de perte :
\[ \theta \leftarrow \theta - \alpha \nabla_\theta J(\theta) \]
où \(\alpha\) est le taux d’apprentissage.
Créer le graphe computationnel
Initialiser les poids et biais
Passage avant : calculer les activations et la perte.
Passage arrière : calculer les gradients en utilisant la règle de la chaîne.
Mettre à jour les paramètres :
\(W^{(\ell)} \leftarrow W^{(\ell)} - \alpha \frac{\partial J}{\partial W^{(\ell)}}\)
\(b^{(\ell)} \leftarrow b^{(\ell)} - \alpha \frac{\partial J}{\partial b^{(\ell)}}\)
Répéter jusqu’à convergence.
Calculer les activations couche par couche :
\(z^{(\ell)} = W^{(\ell)} a^{(\ell-1)} + b^{(\ell)}\),
\(a^{(\ell)} = \phi(z^{(\ell)})\).
Obtenir la prédiction \(\hat{y}\) et calculer la perte \(J(\hat{y}, y)\).
\[ \delta^{(\ell)} = (W^{(\ell+1)} \delta^{(\ell+1)}) \odot \phi'(z^{(\ell)}) \]
L’implémentation complète est présentée ci-dessous et sera examinée dans les diapositives suivantes.
import numpy as np
# Activations & perte
def sigmoid(z):
return 1.0 / (1.0 + np.exp(-z))
def sigmoid_prime(z):
s = sigmoid(z)
return s * (1.0 - s)
def relu(z):
return np.maximum(0.0, z)
def relu_prime(z):
return (z > 0).astype(z.dtype)
def bce_loss(y_true, y_prob, eps=1e-9):
"""Entropie croisée binaire moyennée sur les échantillons (avec découpage pour la stabilité)."""
y_prob = np.clip(y_prob, eps, 1 - eps)
return -np.mean(y_true * np.log(y_prob) + (1 - y_true) * np.log(1 - y_prob))
# Initialisateurs
def he_init(rng, fan_in, fan_out):
# He normal : bon pour ReLU
std = np.sqrt(2.0 / fan_in)
return rng.normal(0.0, std, size=(fan_in, fan_out))
def xavier_init(rng, fan_in, fan_out):
# Glorot/Xavier normal : bon pour sigmoid/tanh
std = np.sqrt(2.0 / (fan_in + fan_out))
return rng.normal(0.0, std, size=(fan_in, fan_out))
# SimpleMLP (API imite NaiveMLP)
class SimpleMLP:
"""
MLP minimal pour la classification binaire.
- Caché : ReLU (par défaut) avec He init ; ou 'sigmoid' avec Xavier init
- Sortie : Sigmoid + BCE (δ_L = a_L - y)
- API : forward -> probas (N,), predict_proba, predict, loss, train
"""
def __init__(self, layer_sizes, lr=0.1, seed=None, l2=0.0,
hidden_activation="relu", lr_decay=None):
"""
layer_sizes : par ex., [2, 4, 4, 1]
lr : taux d'apprentissage
l2 : force de régularisation L2 (0 désactive)
hidden_activation : 'relu' (par défaut) ou 'sigmoid'
lr_decay : flottant optionnel dans (0,1) ; multiplie lr par cela à chaque époque (par ex., 0.9)
"""
self.sizes = list(layer_sizes)
self.lr = float(lr)
self.base_lr = float(lr)
self.lr_decay = lr_decay
self.l2 = float(l2)
self.hidden_activation = hidden_activation
rng = np.random.default_rng(seed)
# Initialiser poids/biais par couche
self.W = []
for din, dout in zip(self.sizes[:-1], self.sizes[1:]):
if hidden_activation == "relu":
Wk = he_init(rng, din, dout)
else:
Wk = xavier_init(rng, din, dout)
self.W.append(Wk)
self.b = [np.zeros(dout) for dout in self.sizes[1:]]
# activations (cachée vs sortie)
def _act(self, z, last=False):
if last:
return sigmoid(z) # couche de sortie
return relu(z) if self.hidden_activation == "relu" else sigmoid(z)
def _act_prime(self, z, last=False):
if last:
return sigmoid_prime(z) # rarement nécessaire avec BCE+sigmoid
return relu_prime(z) if self.hidden_activation == "relu" else sigmoid_prime(z)
# forward (public) : retourne probabilités (N,)
def forward(self, X):
a = X
L = len(self.W)
for ell, (W, b) in enumerate(zip(self.W, self.b), start=1):
a = self._act(a @ W + b, last=(ell == L))
return a.ravel()
# Alias pour correspondre à NaiveMLP
def predict_proba(self, X):
return self.forward(X)
def predict(self, X, threshold=0.5):
return (self.predict_proba(X) >= threshold).astype(int)
def loss(self, X, y):
# BCE + L2 optionnel
p = self.predict_proba(X)
base = bce_loss(y, p)
if self.l2 > 0:
reg = 0.5 * self.l2 * sum((W**2).sum() for W in self.W)
# Normaliser reg par le nombre d'échantillons pour être cohérent avec la perte moyenne
base += reg / max(1, X.shape[0])
return base
# interne : forward caches pour rétropropagation
def _forward_full(self, X):
a = X
activations = [a]
zs = []
L = len(self.W)
for ell, (W, b) in enumerate(zip(self.W, self.b), start=1):
z = a @ W + b
a = self._act(z, last=(ell == L))
zs.append(z)
activations.append(a)
return activations, zs
# entraînement : descente de gradient par mini-lots avec rétropropagation
def train(self, X, y, epochs=30, batch_size=None, verbose=True, shuffle=True):
"""
X : (N, d), y : (N,) dans {0,1}
batch_size : None -> lot complet ; sinon int
"""
N = X.shape[0]
idx = np.arange(N)
B = N if batch_size is None else int(batch_size)
for ep in range(1, epochs + 1):
if shuffle:
np.random.shuffle(idx)
if self.lr_decay:
self.lr = self.base_lr * (self.lr_decay ** (ep - 1))
base_loss = self.loss(X, y)
for start in range(0, N, B):
sl = idx[start:start+B]
Xb = X[sl]
yb = y[sl].reshape(-1, 1) # (B,1)
# Forward caches
activations, zs = self._forward_full(Xb)
A_L = activations[-1] # (B,1)
Bsz = Xb.shape[0]
# Rétropropagation
# Couche de sortie : BCE + sigmoid => delta_L = (A_L - y)
delta = (A_L - yb) # (B,1)
grads_W = [None] * len(self.W)
grads_b = [None] * len(self.b)
# Gradients de la dernière couche
grads_W[-1] = activations[-2].T @ delta / Bsz # (n_{L-1}, 1)
grads_b[-1] = delta.mean(axis=0) # (1,)
# Couches cachées : l = L-1 à 1
for l in range(2, len(self.sizes)):
z = zs[-l] # (B, n_l)
sp = self._act_prime(z, last=False) # (B, n_l)
delta = (delta @ self.W[-l+1].T) * sp # (B, n_l)
grads_W[-l] = activations[-l-1].T @ delta / Bsz # (n_{l-1}, n_l)
grads_b[-l] = delta.mean(axis=0) # (n_l,)
# Régularisation L2 (ajouter aux gradients)
if self.l2 > 0:
for k in range(len(self.W)):
grads_W[k] = grads_W[k] + self.l2 * self.W[k]
# Étape de gradient
for k in range(len(self.W)):
self.W[k] -= self.lr * grads_W[k]
self.b[k] -= self.lr * grads_b[k]
new_loss = self.loss(X, y)
if verbose:
print(f"Époque {ep:3d} | perte {base_loss:.5f} → {new_loss:.5f} | Δ={base_loss - new_loss:.5f} | lr={self.lr:.4f}")def he_init(rng, fan_in, fan_out):
# He normal : bon pour ReLU
std = np.sqrt(2.0 / fan_in)
return rng.normal(0.0, std, size=(fan_in, fan_out))
def xavier_init(rng, fan_in, fan_out):
# Glorot/Xavier normal : bon pour sigmoid/tanh
std = np.sqrt(2.0 / (fan_in + fan_out))
return rng.normal(0.0, std, size=(fan_in, fan_out))class SimpleMLP:
def __init__(self, layer_sizes, lr=0.1, seed=None, l2=0.0,
hidden_activation="relu", lr_decay=None):
self.sizes = list(layer_sizes)
self.lr = float(lr)
self.base_lr = float(lr)
self.lr_decay = lr_decay
self.l2 = float(l2)
self.hidden_activation = hidden_activation
rng = np.random.default_rng(seed)
# Initialiser les poids/biais par couche
self.W = []
for din, dout in zip(self.sizes[:-1], self.sizes[1:]):
if hidden_activation == "relu":
Wk = he_init(rng, din, dout)
else:
Wk = xavier_init(rng, din, dout)
self.W.append(Wk)
self.b = [np.zeros(dout) for dout in self.sizes[1:]] def train(self, X, y, epochs=30, batch_size=None, verbose=True, shuffle=True):
"""
X: (N, d), y: (N,) dans {0,1}
batch_size: None -> plein lot; sinon int
"""
N = X.shape[0]
idx = np.arange(N)
B = N if batch_size is None else int(batch_size)
for ep in range(1, epochs + 1):
if shuffle:
np.random.shuffle(idx)
if self.lr_decay:
self.lr = self.base_lr * (self.lr_decay ** (ep - 1))
base_loss = self.loss(X, y)
for start in range(0, N, B):
sl = idx[start:start+B]
Xb = X[sl]
yb = y[sl].reshape(-1, 1) # (B,1)
# Caches avant
activations, zs = self._forward_full(Xb)
A_L = activations[-1] # (B,1)
Bsz = Xb.shape[0]
# Rétropropagation
# Couche de sortie : BCE + sigmoïde => delta_L = (A_L - y)
delta = (A_L - yb) # (B,1)
grads_W = [None] * len(self.W)
grads_b = [None] * len(self.b)
# Gradients de la dernière couche
grads_W[-1] = activations[-2].T @ delta / Bsz # (n_{L-1}, 1)
grads_b[-1] = delta.mean(axis=0) # (1,)
# Couches cachées : l = L-1 jusqu'à 1
for l in range(2, len(self.sizes)):
z = zs[-l] # (B, n_l)
sp = self._act_prime(z, last=False) # (B, n_l)
delta = (delta @ self.W[-l+1].T) * sp # (B, n_l)
grads_W[-l] = activations[-l-1].T @ delta / Bsz # (n_{l-1}, n_l)
grads_b[-l] = delta.mean(axis=0) # (n_l,)
# Régularisation L2 (ajouter aux gradients)
if self.l2 > 0:
for k in range(len(self.W)):
grads_W[k] = grads_W[k] + self.l2 * self.W[k]
# Étape de gradient
for k in range(len(self.W)):
self.W[k] -= self.lr * grads_W[k]
self.b[k] -= self.lr * grads_b[k]
new_loss = self.loss(X, y)
if verbose:
print(f"Époque {ep:3d} | perte {base_loss:.5f} → {new_loss:.5f} | Δ={base_loss - new_loss:.5f} | lr={self.lr:.4f}")from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_circles
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
X, y = make_circles(n_samples=200, factor=0.5, noise=0.08, random_state=1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0, stratify=y)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
model = SimpleMLP([2, 4, 4, 1], lr=0.3, seed=42, hidden_activation="relu", l2=0.0, lr_decay=0.95)
model.train(X_train, y_train, epochs=150, batch_size=32, verbose=False)
print("Exactitude de l'entraînement :", accuracy_score(y_train, model.predict(X_train)))
print("Exactitude du test :", accuracy_score(y_test, model.predict(X_test)))Exactitude de l'entraînement : 0.95
Exactitude du test : 0.9166666666666666
La différentiation automatique (autodiff) applique systématiquement la règle de la chaîne pour calculer des dérivées exactes des fonctions exprimées sous forme de programmes informatiques. Elle propage les dérivées à travers des opérations élémentaires, soit en avant (des entrées vers les sorties) soit en arrière (des sorties vers les entrées), permettant un calcul de gradient efficace et précis essentiel pour les algorithmes d’optimisation et d’apprentissage.
Problème de gradient évanescent: Les gradients deviennent trop petits, entravant la mise à jour des poids.
La recherche sur les réseaux neuronaux a stagné (à nouveau) au début des années 2000.
Sigmoïde et sa dérivée (plage : 0 à 0,25) étaient des facteurs clés.
Initialisation courante: Poids/biais issus de \(\mathcal{N}(0, 1)\) ont contribué au problème.
Glorot et Bengio (2010) a mis en lumière les problèmes.
Fonctions d’activation alternatives : Unité Linéaire Rectifiée (ReLU) et ses variantes (par exemple, Leaky ReLU, Parametric ReLU, et Exponential Linear Unit).
Initialisation des poids : Initialisation Xavier (Glorot) ou He.
Une méthode d’initialisation similaire mais légèrement différente conçue pour fonctionner avec ReLU, ainsi que Leaky ReLU, ELU, GELU, Swish, et Mish.
L’initialisation aléatoire des poids1 est suffisante pour briser la symétrie dans un réseau de neurones, permettant ainsi que les termes de biais soient fixés à zéro sans nuire à la capacité d’apprentissage du réseau.
import numpy as np
import matplotlib.pyplot as plt
# Définir la fonction Leaky ReLU
def leaky_relu(x, alpha=0.21):
return np.where(x > 0, x, alpha * x)
# Définir la dérivée de la fonction Leaky ReLU
def leaky_relu_derivative(x, alpha=0.2):
return np.where(x > 0, 1, alpha)
# Générer une gamme de valeurs d'entrée
x_values = np.linspace(-4, 4, 400)
# Calculer le Leaky ReLU et sa dérivée
leaky_relu_values = leaky_relu(x_values)
leaky_relu_derivative_values = leaky_relu_derivative(x_values)
# Créer le graphique
plt.figure(figsize=(5, 3))
# Tracer le Leaky ReLU
plt.subplot(1, 2, 1)
plt.plot(x_values, leaky_relu_values, label='Leaky ReLU', color='blue')
plt.title('Fonction d\'Activation Leaky ReLU')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.grid(True)
plt.axhline(0, color='black',linewidth=0.5)
plt.axvline(0, color='black',linewidth=0.5)
plt.legend()
# Tracer la dérivée du Leaky ReLU
plt.subplot(1, 2, 2)
plt.plot(x_values, leaky_relu_derivative_values, label='Dérivée de Leaky ReLU', color='red')
plt.title('Dérivée de Leaky ReLU')
plt.xlabel('x')
plt.ylabel("f'(x)")
plt.grid(True)
plt.axhline(0, color='black',linewidth=0.5)
plt.axvline(0, color='black',linewidth=0.5)
plt.legend()
# Afficher les graphiques
plt.tight_layout()
plt.show()| Concept | Rôle |
|---|---|
| Fonctions d’activation | Introduisent la non-linéarité (par exemple, Sigmoïde, ReLU). |
| Fonction de perte | Mesure l’erreur de prédiction (par exemple, Entropie Croisée Binaire). |
| Taux d’apprentissage (α) | Contrôle la taille des pas dans les mises à jour des paramètres. |
| Descente de gradient | Méthode d’optimisation pour l’ajustement des poids. |
| Règle de chaîne | Mécanisme pour propager les dérivées en arrière. |
| Différentiation automatique | Implémentation logicielle de la rétropropagation (par exemple, TensorFlow, PyTorch). |
Une série de vidéos, avec animations, fournissant l’intuition derrière l’algorithme de rétropropagation.
Réseaux neuronaux (playlist)
Une des séries de vidéos les plus complètes sur l’algorithme de rétropropagation.
Introduction aux réseaux neuronaux (playlist)
Dans son livre, Neural Networks and Deep Learning, Michael Nielsen fournit une implémentation complète d’un réseau neuronal en Python.
Marcel Turcotte
École de science informatique et de génie électrique (SIGE)
Université d’Ottawa