Dans les articles précédents, nous avons utilisé la régression linéaire et la régression logistique comme des boîtes noires — on entre des données, on obtient des prédictions. Aujourd’hui, on ouvre ces boîtes. On va construire chaque formule, justifier chaque choix, et comprendre pourquoi les mathématiques nous donnent exactement ces équations et pas d’autres.

Prépare-toi : cet article est dense. Mais chaque étape sera expliquée avec une patience absolue. Quand tu auras fini, tu comprendras ce que très peu de gens comprennent vraiment — même parmi ceux qui utilisent ces modèles quotidiennement.

Partie I
Régression Linéaire — Construction mathématique complète

1.1 Le problème posé

On dispose de n observations. Chaque observation est un couple (xᵢ, yᵢ) : une valeur d’entrée xᵢ et une valeur de sortie yᵢ. On veut trouver une droite ŷ = ax + b qui prédit le mieux possible y à partir de x.

Mais qu’est-ce que « le mieux possible » ? Il faut choisir une mesure de l’erreur. La plus naturelle est la distance entre la valeur réelle yᵢ et la valeur prédite ŷᵢ = axᵢ + b. On appelle cette distance le résidu :

Résidu de l’observation i
eᵢ = yᵢ − ŷᵢ = yᵢ − (axᵢ + b)

Pourquoi ne pas simplement minimiser la somme des résidus Σeᵢ ? Parce que les erreurs positives et négatives s’annuleraient — une droite très mauvaise pourrait avoir une somme nulle. On utilise donc les carrés des résidus, qui sont toujours positifs.

1.2 La fonction de coût J(a, b)

On définit la fonction de coût — aussi appelée erreur quadratique moyenne (MSE pour Mean Squared Error) :

Fonction de coût — Erreur quadratique moyenne
J(a, b) = (1/n) × Σᵢ₌₁ⁿ (yᵢ − axᵢ − b)²

J(a, b) est une fonction de deux variables : la pente a et l’ordonnée à l’origine b. Notre objectif est de trouver les valeurs de a et b qui minimisent J.

📐 Visualisation de la fonction de coût — une parabole en 3D
a b J(a,b) Minimum J* (a*, b*) Courbes de niveau de J — le minimum est le fond du « bol »

1.3 Minimisation par les dérivées partielles

Un minimum d’une fonction de plusieurs variables se trouve là où toutes ses dérivées partielles sont nulles. On pose donc :

Conditions du premier ordre
∂J/∂a = 0    et    ∂J/∂b = 0

Calcul de ∂J/∂b

On part de J(a,b) = (1/n) Σ(yᵢ − axᵢ − b)² ∂J/∂b = (1/n) × Σ 2(yᵢ − axᵢ − b) × (−1)
On simplifie : ∂J/∂b = −(2/n) × Σ(yᵢ − axᵢ − b)
On pose ∂J/∂b = 0 : Σ(yᵢ − axᵢ − b) = 0
On développe : Σyᵢ − a Σxᵢ − nb = 0
On divise par n — on reconnaît les moyennes x̄ = Σxᵢ/n et ȳ = Σyᵢ/n : b = ȳ − a x̄

Cette équation dit quelque chose de beau : la droite de régression passe toujours par le point moyen (x̄, ȳ). Quoi que soient les données, le centre de gravité est toujours sur la droite.

Calcul de ∂J/∂a

∂J/∂a = −(2/n) × Σ xᵢ(yᵢ − axᵢ − b)
On pose ∂J/∂a = 0 : Σ xᵢ(yᵢ − axᵢ − b) = 0
On substitue b = ȳ − ax̄ (résultat précédent) : Σ xᵢ(yᵢ − axᵢ − ȳ + ax̄) = 0
On regroupe : Σ xᵢ(yᵢ − ȳ) − a Σ xᵢ(xᵢ − x̄) = 0
On isole a. On peut montrer que Σxᵢ(xᵢ−x̄) = Σ(xᵢ−x̄)² et Σxᵢ(yᵢ−ȳ) = Σ(xᵢ−x̄)(yᵢ−ȳ) : a = Σ(xᵢ−x̄)(yᵢ−ȳ) / Σ(xᵢ−x̄)²
✅ Formules closes de la régression linéaire

a = Σ(xᵢ−x̄)(yᵢ−ȳ) / Σ(xᵢ−x̄)²

b = ȳ − a·x̄

Ces formules donnent la solution exacte et unique en un seul calcul — c’est la méthode des moindres carrés ordinaires (OLS).

1.4 La forme matricielle — pour plusieurs variables

Avec une seule variable x, on a les formules closes. Mais si on veut prédire y à partir de x₁, x₂, …, xₚ (plusieurs variables), on generalise avec les matrices. Le modèle devient :

Modèle vectoriel
ŷ = Xβ    avec    β = [b, a₁, a₂, …, aₚ]ᵀ

Où X est la matrice des données (n lignes × p+1 colonnes, avec une colonne de 1 pour le biais b). La fonction de coût devient :

Coût en forme matricielle
J(β) = (1/n) ||y − Xβ||² = (1/n)(y − Xβ)ᵀ(y − Xβ)

On dérive par rapport à β et on pose ∂J/∂β = 0. Le calcul matriciel donne les équations normales :

Équations normales → Solution matricielle
XᵀXβ = Xᵀy
β* = (XᵀX)⁻¹ Xᵀy
Condition d’existence : La matrice XᵀX doit être inversible. C’est le cas si et seulement si les colonnes de X sont linéairement indépendantes — autrement dit, aucune variable explicative n’est une combinaison linéaire des autres.

Partie II
Régression Logistique — Construction mathématique complète

2.1 Pourquoi pas le MSE ici ?

Pour la classification binaire (y ∈ {0, 1}), on pourrait naïvement utiliser le même MSE. Deux problèmes fondamentaux apparaissent :

⚠️ Problèmes du MSE pour la classification

Problème 1 — Valeurs hors [0,1] : La droite linéaire peut prédire des valeurs négatives ou supérieures à 1, qui n’ont aucun sens comme probabilités.

Problème 2 — Fonction de coût non convexe : Si on applique le MSE avec la sigmoïde, la fonction J(θ) a de nombreux minima locaux — la descente de gradient ne converge pas vers le minimum global.

Solution : Choisir une autre fonction de coût, dérivée du principe de Maximum de Vraisemblance.

2.2 Le modèle probabiliste

Le modèle logistique suppose que la probabilité que y = 1 suit une loi de Bernoulli :

Modèle probabiliste
p(y=1 | x ; θ) = σ(θᵀx) = 1 / (1 + e^(−θᵀx))
p(y=0 | x ; θ) = 1 − σ(θᵀx)

On peut écrire ces deux équations en une seule formule compacte, valable pour y ∈ {0,1} :

Formule compacte de Bernoulli
p(y | x ; θ) = σ(θᵀx)^y × (1 − σ(θᵀx))^(1−y)

Vérifie : si y=1, on obtient σ(θᵀx)¹ × (1−σ)⁰ = σ(θᵀx). Si y=0, on obtient σ⁰ × (1−σ)¹ = 1−σ. Parfait.

2.3 La vraisemblance — Maximum Likelihood Estimation (MLE)

L’idée du MLE est la suivante : parmi tous les paramètres θ possibles, trouve celui qui rend les données observées les plus probables.

Si les n observations sont indépendantes, la probabilité conjointe d’observer toutes les données est le produit des probabilités individuelles. C’est la vraisemblance :

Fonction de vraisemblance
L(θ) = Πᵢ₌₁ⁿ p(yᵢ | xᵢ ; θ)
L(θ) = Πᵢ₌₁ⁿ σ(θᵀxᵢ)^yᵢ × (1 − σ(θᵀxᵢ))^(1−yᵢ)

2.4 La log-vraisemblance

Maximiser L(θ) est difficile à cause des produits. Or le logarithme est une fonction croissante — maximiser L(θ) revient à maximiser log(L(θ)). Et le log transforme les produits en sommes :

On prend le log : ℓ(θ) = log L(θ) = Σᵢ log[σ(θᵀxᵢ)^yᵢ × (1−σ(θᵀxᵢ))^(1−yᵢ)]
On utilise log(aᵇ) = b·log(a) : ℓ(θ) = Σᵢ [yᵢ log σ(θᵀxᵢ) + (1−yᵢ) log(1−σ(θᵀxᵢ))]

On veut maximiser ℓ(θ). Par convention, on préfère minimiser les fonctions de coût. On définit donc la Binary Cross-Entropy Loss (perte par entropie croisée) :

Fonction de coût — Binary Cross-Entropy
J(θ) = −(1/n) ℓ(θ)
J(θ) = −(1/n) Σᵢ [yᵢ log(pᵢ) + (1−yᵢ) log(1−pᵢ)]
où pᵢ = σ(θᵀxᵢ)
Pourquoi « Cross-Entropy » ? En théorie de l’information, l’entropie croisée mesure la différence entre deux distributions de probabilité. Ici, elle mesure la distance entre la distribution prédite par le modèle et la distribution réelle des étiquettes {0,1}. Minimiser cette distance, c’est maximiser l’accord entre modèle et données.

2.5 La descente de gradient

Contrairement à la régression linéaire, il n’existe pas de formule close pour minimiser J(θ) en logistique. On utilise un algorithme itératif : la descente de gradient.

L’idée : partir d’un θ quelconque, calculer le gradient (la direction de montée) et faire un pas dans la direction opposée (descente).

Algorithme de descente de gradient
θ ← θ − α · ∇J(θ)
α = taux d’apprentissage (hyperparamètre)

2.6 Calcul du gradient — la dérivée complète

Il faut calculer ∂J/∂θ. On procède étape par étape en utilisant la règle de la chaîne.

Étape 1 — Dérivée de la sigmoïde

Une propriété remarquable de la sigmoïde : sa dérivée s’exprime simplement en fonction d’elle-même.

σ(z) = 1/(1+e⁻ᶻ) dσ/dz = e⁻ᶻ / (1+e⁻ᶻ)²
On réécrit : dσ/dz = [1/(1+e⁻ᶻ)] × [e⁻ᶻ/(1+e⁻ᶻ)]
Or e⁻ᶻ/(1+e⁻ᶻ) = 1 − 1/(1+e⁻ᶻ) = 1 − σ(z) : dσ/dz = σ(z) × (1 − σ(z))

Étape 2 — Gradient de J(θ)

On part de J(θ) = −(1/n) Σ[yᵢ log(pᵢ) + (1−yᵢ)log(1−pᵢ)] avec pᵢ = σ(θᵀxᵢ)
On dérive par rapport à θ par la règle de la chaîne : ∂J/∂θ = −(1/n) Σ [yᵢ × (1/pᵢ) − (1−yᵢ) × 1/(1−pᵢ)] × ∂pᵢ/∂θ
Or ∂pᵢ/∂θ = pᵢ(1−pᵢ) × xᵢ (dérivée de la sigmoïde × dérivée de θᵀxᵢ)
On substitue et on simplifie : ∂J/∂θ = −(1/n) Σ [yᵢ(1−pᵢ) − (1−yᵢ)pᵢ] × xᵢ
On développe le crochet : yᵢ − yᵢpᵢ − pᵢ + yᵢpᵢ = yᵢ − pᵢ : ∂J/∂θ = (1/n) Σ (pᵢ − yᵢ) × xᵢ
✅ Gradient de la régression logistique

∇J(θ) = (1/n) Σᵢ (pᵢ − yᵢ) · xᵢ

Ce résultat est remarquable : le gradient a exactement la même forme que celui de la régression linéaire — (prédiction − réalité) × entrée. Seule la définition de pᵢ change (sigmoïde au lieu de droite).

La mise à jour à chaque itération :

θ ← θ − α × (1/n) × Xᵀ(p − y)

2.7 Comparaison finale des deux modèles

Aspect Régression Linéaire Régression Logistique
Sortieŷ = θᵀx ∈ ℝp = σ(θᵀx) ∈ [0,1]
Fonction de coûtMSE : (1/n)Σ(yᵢ−ŷᵢ)²Cross-Entropy : −(1/n)Σ[y log p + (1−y)log(1−p)]
Origine de JGéométrique (moindres carrés)Probabiliste (MLE)
SolutionFormule close : β=(XᵀX)⁻¹XᵀyItérative : descente de gradient
Gradient(1/n)Xᵀ(ŷ−y)(1/n)Xᵀ(p−y)
Convexité de J✅ Toujours convexe✅ Toujours convexe
UsagePrédire une valeur continuePrédire une probabilité 0/1
Les deux modèles sont plus proches qu’il n’y paraît. Ils partagent la même architecture linéaire — seule la façon d’interpréter la sortie et de mesurer l’erreur diffère. — Unité profonde du machine learning

Partie III
Implémentation Python from scratch — sans sklearn

Maintenant qu’on comprend les formules, on les implémente à la main. Pas de sklearn. Pas de bibliothèques de machine learning. Juste numpy et les formules qu’on vient de dériver.

Régression Linéaire — formules closes

Python — From Scratch # ============================================================ # RÉGRESSION LINÉAIRE — IMPLÉMENTATION FROM SCRATCH # Formules dérivées mathématiquement dans cet article # ============================================================ import numpy as np import matplotlib.pyplot as plt class RegressionLineaire: def __init__(self): self.a = None # pente self.b = None # ordonnée à l’origine def fit(self, x, y): «  » » Formules closes dérivées par les moindres carrés : a = Σ(xᵢ−x̄)(yᵢ−ȳ) / Σ(xᵢ−x̄)² b = ȳ − a·x̄ «  » » x_moy = np.mean(x) y_moy = np.mean(y) # Numérateur : covariance numerateur = np.sum((x – x_moy) * (y – y_moy)) # Dénominateur : variance de x denominateur = np.sum((x – x_moy)**2) self.a = numerateur / denominateur self.b = y_moy – self.a * x_moy print(f »Pente a = {self.a:.4f} ») print(f »Ordonnée b = {self.b:.4f} ») def predict(self, x): return self.a * x + self.b def mse(self, x, y): «  » »MSE = (1/n) Σ(yᵢ − ŷᵢ)² » » » y_pred = self.predict(x) return np.mean((y – y_pred)**2) def r2(self, x, y): «  » »R² = 1 − SS_res/SS_tot » » » ss_res = np.sum((y – self.predict(x))**2) ss_tot = np.sum((y – np.mean(y))**2) return 1 – ss_res/ss_tot # — Test avec données agricoles du Borgou — x = np.array([100, 150, 200, 120, 180, 250], dtype=float) y = np.array([200, 280, 350, 230, 320, 420], dtype=float) modele = RegressionLineaire() modele.fit(x, y) print(f »MSE = {modele.mse(x, y):.2f} ») print(f »R² = {modele.r2(x, y):.4f} ») print(f »Prédiction pour 220mm : {modele.predict(220):.1f} kg »)

Régression Logistique — descente de gradient

Python — From Scratch # ============================================================ # RÉGRESSION LOGISTIQUE — IMPLÉMENTATION FROM SCRATCH # Gradient dérivé mathématiquement : ∇J = (1/n)Xᵀ(p−y) # ============================================================ class RegressionLogistique: def __init__(self, alpha=0.1, n_iter=1000): self.alpha = alpha # taux d’apprentissage self.n_iter = n_iter # nombre d’itérations self.theta = None self.historique_cout = [] def _sigmoide(self, z): «  » »σ(z) = 1 / (1 + e^{-z}) » » » return 1 / (1 + np.exp(-np.clip(z, –500, 500))) def _cout(self, p, y): «  » » Cross-Entropy : J = -(1/n) Σ[y·log(p) + (1-y)·log(1-p)] «  » » n = len(y) eps = 1e-15 # éviter log(0) return -(1/n) * np.sum(y * np.log(p + eps) + (1-y) * np.log(1-p + eps)) def fit(self, X, y): «  » » Descente de gradient : θ ← θ − α × (1/n) × Xᵀ(p − y) «  » » n, p = X.shape # Ajouter colonne de biais X_b = np.c_[np.ones((n, 1)), X] # Initialisation des paramètres à zéro self.theta = np.zeros(X_b.shape[1]) for iteration in range(self.n_iter): # Prédictions : p = σ(Xθ) z = X_b @ self.theta p_pred = self._sigmoide(z) # Gradient : ∇J = (1/n) × Xᵀ(p − y) gradient = (1/n) * X_b.T @ (p_pred – y) # Mise à jour : θ ← θ − α·∇J self.theta -= self.alpha * gradient # Enregistrer le coût cout = self._cout(p_pred, y) self.historique_cout.append(cout) if iteration % 200 == 0: print(f »Itération {iteration:4d} | Coût = {cout:.4f} ») def predict_proba(self, X): X_b = np.c_[np.ones((X.shape[0], 1)), X] return self._sigmoide(X_b @ self.theta) def predict(self, X, seuil=0.5): return (self.predict_proba(X) >= seuil).astype(int) def accuracy(self, X, y): return np.mean(self.predict(X) == y) # — Test : réussite au BEPC — X = np.array([[2],[4],[6],[8],[10],[12],[3],[9]], dtype=float) y = np.array([0,0,1,1,1,1,0,1], dtype=float) modele_log = RegressionLogistique(alpha=0.5, n_iter=1000) modele_log.fit(X, y) print(f »\nPrécision : {modele_log.accuracy(X, y):.0%} ») print(f »P(réussite | 7h) = {modele_log.predict_proba(np.array([[7]]))[0]:.1%} ») # Courbe d’apprentissage plt.plot(modele_log.historique_cout, color=‘#3a6a9a’, linewidth=2) plt.xlabel(‘Itération’) plt.ylabel(‘Cross-Entropy Loss’) plt.title(‘Convergence de la descente de gradient’) plt.grid(alpha=0.3) plt.show()

✏️ Exercice corrigé — Construction complète sur données réelles

Voici 5 observations : heures de travail x = [1, 2, 3, 4, 5] et salaire journalier y = [1500, 2200, 2800, 3600, 4000] FCFA.

a) Calcule x̄ et ȳ.

b) Calcule Σ(xᵢ−x̄)(yᵢ−ȳ) et Σ(xᵢ−x̄)².

c) Déduis a et b. Écris l’équation de la droite.

d) Calcule le MSE de ce modèle.

e) Pour la logistique : si θ₀ = −5 et θ₁ = 1, calcule p pour x = 3 heures. Décision si seuil = 0,5 ?

▸ Correction

a) x̄ = (1+2+3+4+5)/5 = 3  |  ȳ = (1500+2200+2800+3600+4000)/5 = 2820

b)

Σ(xᵢ−x̄)(yᵢ−ȳ) = (−2)(−1320)+(−1)(−620)+(0)(−20)+(1)(780)+(2)(1180)

= 2640 + 620 + 0 + 780 + 2360 = 6400

Σ(xᵢ−x̄)² = 4+1+0+1+4 = 10

c) a = 6400/10 = 640 FCFA/heure

b = 2820 − 640×3 = 2820 − 1920 = 900 FCFA

Droite : ŷ = 640x + 900

d) Prédictions : ŷ = [1540, 2180, 2820, 3460, 4100]

Résidus : [−40, 20, −20, 140, −100]

Résidus² : [1600, 400, 400, 19600, 10000]

MSE = (1600+400+400+19600+10000)/5 = 32000/5 = 6400

e) z = θ₀ + θ₁×3 = −5 + 3 = −2

p = σ(−2) = 1/(1+e²) = 1/(1+7,389) ≈ 0,119 soit 12%

0,119 < 0,5 → décision : 0 (négatif)

Ce qu’on retient

Tu viens de traverser l’intégralité de la construction mathématique des deux modèles les plus utilisés en machine learning. La régression linéaire naît des moindres carrés et admet une solution exacte via les matrices. La régression logistique naît du principe de vraisemblance maximale et s’optimise par descente de gradient.

Ces deux modèles partagent une architecture commune — un produit linéaire θᵀx transformé différemment — et leurs gradients ont la même forme élégante : (prédiction − réalité) × entrée. Ce n’est pas un hasard, c’est la signature d’une famille mathématique profonde : les modèles linéaires généralisés (GLM).

Dans le prochain article, nous plongerons dans la loi normale et la courbe en cloche — la distribution qui gouverne les notes de tes élèves, les erreurs de mesure, et presque tout ce qui varie autour d’une moyenne dans la nature.