La descente de gradient : faire apprendre une machine
Le prix d'une course de moto-taxi
Un conducteur de moto-taxi (un « benskineur ») a noté le prix de 8 courses selon la distance :
| Distance (km) | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 |
|---|---|---|---|---|---|---|---|---|
| Prix (FCFA) | 250 | 350 | 500 | 600 | 700 | 850 | 1 100 | 1 350 |
On cherche une règle prix = a x distance + b qui colle au mieux à ces courses, pour proposer un prix juste pour une distance jamais faite. On ne va pas deviner a et b : on va laisser l'ordinateur les apprendre.
Mesurer l'erreur
Pour savoir si une règle est bonne, il faut un nombre qui dit à quel point elle se trompe.
def mse(a, b, x, y):
predictions = a * x + b
return ((predictions - y) ** 2).mean()
Pour chaque choix de a et b, la MSE donne un nombre. Imagine un paysage où chaque point (a, b) a une altitude, la MSE. On cherche le fond de la vallée. Et tu sais maintenant comment faire : suivre la pente vers le bas.
L'algorithme
Commençons par un modèle encore plus simple, prix = a x distance, avec un seul paramètre a. La dérivée de la MSE par rapport à a se calcule (c'est un exercice de terminale) :
dérivée = 2 x moyenne( (a x distance - prix) x distance )
a = 0.0 # on part de n'importe où
pas = 0.01
for etape in range(1000):
derivee = 2 * ((a * distance - prix) * distance).mean()
a = a - pas * derivee
Chaque passage dans la boucle s'appelle une itération. Au début, a vaut 0 et le modèle prédit des courses gratuites : l'erreur est énorme, la dérivée aussi, et a grandit vite. Puis les pas deviennent de plus en plus petits, à mesure que la pente s'aplatit.
C'est exactement ce que font les modèles de deep learning, avec des millions ou des milliards de paramètres au lieu d'un seul.
Exercice : Apprendre un seul paramètre
Complète la boucle de descente de gradient pour apprendre a dans le modèle prix = a x distance, avec un pas de 0,01 et 1 000 itérations. Range aussi dans erreur_finale la MSE à la fin.
La meilleure valeur possible est a ≈ 140,2 : le correcteur vérifie que ta boucle la trouve.
Voir une solution
import numpy as np
distance = np.array([1, 2, 3, 4, 5, 6, 8, 10])
prix = np.array([250, 350, 500, 600, 700, 850, 1100, 1350])
a = 0.0
pas = 0.01
for etape in range(1000):
derivee = 2 * ((a * distance - prix) * distance).mean()
a = a - pas * derivee
erreur_finale = ((a * distance - prix) ** 2).mean()
print(a, erreur_finale)Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice
Voir le taux d'apprentissage en action
Lance avec pas = 0.01, puis essaie 0.001 (trop lent : après 20 itérations, a est encore loin) et 0.05 (trop grand : a part dans tous les sens et l'erreur explose).
import numpy as np
distance = np.array([1, 2, 3, 4, 5, 6, 8, 10])
prix = np.array([250, 350, 500, 600, 700, 850, 1100, 1350])
a = 0.0
pas = 0.01
for etape in range(20):
derivee = 2 * ((a * distance - prix) * distance).mean()
a = a - pas * derivee
erreur = ((a * distance - prix) ** 2).mean()
print(f"étape {etape + 1:2} : a = {a:12.2f} erreur = {erreur:16.0f}")
Deux paramètres : a et b
Le modèle prix = a x distance suppose qu'une course de 0 km coûte 0 FCFA. En vrai, il y a un prix de base : on paie quelque chose même pour un trajet très court. Le modèle complet est prix = a x distance + b.
Avec deux paramètres, on calcule deux dérivées (le gradient), une pour a et une pour b, et on met à jour les deux à chaque itération :
erreur = a * distance + b - prix
derivee_a = 2 * (erreur * distance).mean()
derivee_b = 2 * erreur.mean()
a = a - pas * derivee_a
b = b - pas * derivee_b
Avec deux paramètres, il faut plus d'itérations : b « descend » plus lentement que a, parce que les distances (jusqu'à 10) rendent la pente selon a plus forte.
Exercice : Le modèle complet
Écris entrainer(x, y, pas, iterations) qui part de a = 0 et b = 0, fait la descente de gradient pour le modèle y = a x + b, et renvoie a, b.
Puis utilise-la sur les courses avec un pas de 0,01 et 5 000 itérations, et calcule prix_7km : le prix prédit pour une course de 7 km, arrondi à l'entier.
Voir une solution
import numpy as np
def entrainer(x, y, pas, iterations):
"""Apprend a et b du modèle y = a * x + b par descente de gradient."""
a = 0.0
b = 0.0
for _ in range(iterations):
erreur = a * x + b - y
derivee_a = 2 * (erreur * x).mean()
derivee_b = 2 * erreur.mean()
a = a - pas * derivee_a
b = b - pas * derivee_b
return a, b
distance = np.array([1, 2, 3, 4, 5, 6, 8, 10])
prix = np.array([250, 350, 500, 600, 700, 850, 1100, 1350])
a, b = entrainer(distance, prix, 0.01, 5000)
prix_7km = round(a * 7 + b)
print(f"prix = {a:.1f} x distance + {b:.1f}")
print("Course de 7 km :", prix_7km, "FCFA")Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice