ParcoursOuvrir l'appli

La descente de gradient : faire apprendre une machine

Les maths du ML, en code · 60 min

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