contenu
online learning
scikit SGDRegressor
Entrainer un modele revient a minimiser une fonction de cout
chaque famille de modele a sa propre fonction de cout
Modèle : \(\hat{y} = w^T x + b\)
Fonction de coût (MSE - Mean Squared Error) : \(E(w, b) = \frac{1}{2n}\sum_{i=1}^n (y_i - \hat{y}_i)^2 = \frac{1}{2n}\sum_{i=1}^n (y_i - w^T x_i - b)^2\)
Modèle (classification binaire) :
\(\hat{y} = \sigma(w^T x + b) = \frac{1}{1 + e^{-(w^T x + b)}}\) où $\sigma$ est la fonction sigmoïde
Fonction de coût (Log Loss / Cross-Entropy) :
\(E(w, b) = -\frac{1}{n}\sum_{i=1}^n \left[ y_i \log(\hat{y}_i) + (1-y_i) \log(1-\hat{y}_i) \right]\) où $y_i \in {0, 1}$
critères de division :
1. Pour la régression - MSE (Mean Squared Error) :
\(\text{MSE} = \frac{1}{n}\sum_{i=1}^n (y_i - \bar{y})^2\) où $\bar{y}$ est la moyenne des $y$ dans le nœud
2. Pour la classification - Gini Impurity :
\(\text{Gini} = 1 - \sum_{k=1}^K p_k^2\) où $p_k$ est la proportion de la classe $k$
3. Pour la classification - Entropie (Entropy) :
\[\text{Entropy} = -\sum_{k=1}^K p_k \log_2(p_k)\]Algorithme glouton (greedy) qui choisit la meilleure division à chaque nœud, pas de descente de gradient pour les arbres de decisions !
Objectif : Trouver le minimum d’une fonction dérivable $f(x)$ (la fonction de cout)
Principe : Itération selon la direction opposée au gradient
\[x_{n+1} = x_n - \eta \nabla f(x_n)\]où $\eta$ est le taux d’apprentissage (learning rate) et $\nabla f(x_n)$ le gradient, la derivée de la fonction $f(x)$
Qu’est-ce que le gradient ?
Le gradient, c’est la pente de la fonction à un point donné.
Analogie : Imaginez que vous êtes dans une vallée. Le gradient vous montre la direction de la montée la plus raide. Pour descendre au fond de la vallée (trouver le minimum), on va dans la direction opposée au gradient.
Exemple : Minimiser $f(x) = x^2 - 4x + 5$
Dérivée : $f’(x) = 2x - 4$
Algorithme :
Résultat : $f(2) = 1$ est le minimum global
def gradient_descent(f, df, x0, learning_rate=0.1, max_iter=100, tol=1e-6):
"""
Trouve le minimum d'une fonction par descente de gradient
Args:
f: fonction à minimiser
df: dérivée de la fonction
x0: point de départ
learning_rate: taux d'apprentissage (eta)
max_iter: nombre maximum d'itérations
tol: tolérance pour l'arrêt (early stopping)
"""
x = x0
for i in range(max_iter):
# Calcul du gradient
grad = df(x)
# Mise à jour
x_new = x - learning_rate * grad
# Critère d'arrêt
if abs(x_new - x) < tol:
break
x = x_new
return x
Le gradient n’est PAS une constante, c’est une fonction de la position :
\[\nabla E(w)\]Le gradient ne s’applique qu’aux modeles dit lineaires ou il faut trouver les poids / les coefficients.
$w$ = vecteur des paramètres (weights)
\[w = \begin{pmatrix} w_1 \\ w_2 \\ \vdots \\ w_d \end{pmatrix} \in \mathbb{R}^d\]où $d$ = nombre de features (dimensions)
Modèle : \(\hat{y} = w_1 x_1 + w_2 x_2 + ... + w_d x_d + b\)
Ou en notation vectorielle : \(\hat{y} = w^T x + b\)
Paramètres :
Mise à jour vectorielle :
\[w_{t+1} = w_t - \eta \nabla E(w_t)\]Cela signifie chaque composante est mise à jour :
\[w_i^{(t+1)} = w_i^{(t)} - \eta \frac{\partial E}{\partial w_i}\]Avec $f(x) = x^2 - 4x + 5$ :
En pratique ML :
Modèle | Taille de $w$ |
---|---|
Régression simple (1 feature) | $w \in \mathbb{R}$ |
Régression multiple (100 features) | $w \in \mathbb{R}^{100}$ |
Réseau de neurones simple | $w \in \mathbb{R}^{10000}$ |
GPT-3 | $w \in \mathbb{R}^{175 \text{ milliards}}$ |
À chaque itération :
Le gradient change car on l’évalue à chaque nouveau point
descendre une montagne :
Le gradient change naturellement car la pente n’est pas la même partout !
Contexte Machine Learning : On a $n$ echantillons d’entraînement $(x_1, y_1), …, (x_n, y_n)$
Fonction de coût totale :
\[E(w) = \frac{1}{n}\sum_{i=1}^n L(y_i, f(x_i; w))\]où $L$ = perte (loss) sur un echantillon
Problème : Calculer le gradient complet est coûteux
\[\nabla E(w) = \frac{1}{n}\sum_{i=1}^n \nabla L_i(w)\]Avec $n$ = 1 million d’exemples → impossible à chaque itération !
Idée : Au lieu de calculer le gradient sur TOUS les exemples, utiliser UN SEUL echantillon aléatoire
Gradient complet (trop lent) :
\[\nabla E(w) = \frac{1}{n}\sum_{i=1}^n \nabla L_i(w)\]Gradient stochastique (rapide) :
\(\nabla \tilde{E}(w) = \nabla L_k(w)\) où $k$ est tiré au hasard parmi ${1, …, n}$
Propriété du gradient stochastique : En moyenne, ça marche !
\[\mathbb{E}[\nabla L_k] = \frac{1}{n}\sum_{i=1}^n \nabla L_i = \nabla E\]Le terme “stochastique” vient de l’échantillonnage aléatoire
Chaque itération :
Ce qui est stochastique :
Ce qui est exact :
Question : Un seul exemple, c’est pas trop approximatif ?
Réponse : C’est bruité, mais non biaisé
Non biaisé = correct en moyenne
Si on répète l’expérience 1000 fois :
Analogie : Lancer un dé
Ce qui est calculé/estimé :
Batch GD :
SGD :
Pour un seul echantillon : $L_i(w) = \text{loss}(y_i, f(x_i; w))$
fonction de Coût avec régularisation estimée sur tous les échantillons : $E(w) = \frac{1}{n}\sum_{i=1}^n L_i(w) + \alpha R(w)$
Composantes :
Frameworks optimisés :
Optimisations :
Principe : Mise à jour après chaque observation
Avantages :
Application :
Principe : Mise à jour des paramètres après chaque observation qui arrive
Algorithme :
À l’instant $t$, une nouvelle observation $(x_t, y_t)$ arrive :
Prédiction avec le modèle actuel : \(\hat{y}_t = f(x_t; w_t)\)
Calcul de la perte sur cette seule observation : \(L_t = L(y_t, \hat{y}_t)\)
Mise à jour immédiate des paramètres : \(w_{t+1} = w_t - \eta_t \nabla L_t(w_t)\)
Différence avec SGD classique :
SGD Classique | Online Learning |
---|---|
Dataset fixe, plusieurs epochs | Flux continu, une seule passe |
$w_{t+1} = w_t - \eta \nabla L_k$ | $w_{t+1} = w_t - \eta_t \nabla L_t$ |
$k$ tiré aléatoirement | Ordre d’arrivée des données |
Learning rate adaptatif souvent utilisé : \(\eta_t = \frac{\eta_0}{1 + \lambda t}\)
où $t$ est le nombre d’observations vues
Exemple - Régression linéaire online :
Observation $(x_t, y_t)$ arrive : \(w_{t+1} = w_t + \eta_t (y_t - w_t^T x_t) \cdot x_t\)
Exemple - Moyenne mobile :
Calculer la moyenne en ligne : \(\mu_t = \mu_{t-1} + \frac{1}{t}(x_t - \mu_{t-1})\)
from sklearn.linear_model import SGDRegressor
model = SGDRegressor(
loss='squared_error', # Fonction de perte
penalty='l2', # Régularisation
alpha=0.0001, # Force régularisation
learning_rate='invscaling', # Stratégie LR
eta0=0.01, # LR initial
max_iter=1000, # Nb itérations
tol=1e-3, # Critère arrêt
early_stopping=True # Arrêt anticipé
)
$\eta$ trop grand :
$\eta$ trop petit :
$\eta$ optimal : Convergence rapide et stable
1. Constant : $\eta_t = \eta_0$
2. Décroissance temporelle : \(\eta_t = \frac{\eta_0}{1 + t \cdot d}\)
3. Inverse scaling : \(\eta_t = \frac{\eta_0}{t^\alpha}\)
4. Adaptatif : Ajustement selon performance validation
Méthodes avancées :
AdaGrad : Adapte $\eta$ par paramètre \(\eta_t^{(i)} = \frac{\eta_0}{\sqrt{\sum_{\tau=1}^t (g_\tau^{(i)})^2 + \epsilon}}\)
RMSprop : Moyenne mobile des gradients
Adam : Combine momentum + adaptation
Type | Taille échantillon | Caractéristiques |
---|---|---|
Batch GD | Tous ($n$) | Stable, lent, grande mémoire |
Stochastic GD | 1 | Rapide, bruité, converge |
Mini-Batch GD | $m$ (ex: 32-256) | Optimal : vectorisation + stabilité |
Formule mini-batch : \(w_{t+1} = w_t - \eta \frac{1}{m}\sum_{i=1}^m \nabla L_i(w_t)\)
Problème : Dataset trop large pour la RAM
Solution : Charger les données par batch
for epoch in range(n_epochs):
for batch in data_loader:
gradient = compute_gradient(batch)
weights -= learning_rate * gradient
Applications : Big Data, streaming
Effet : Sélection de variables (sparsité)
Gradient sous-différentiel : \(\frac{\partial R}{\partial w_i} = \text{sign}(w_i)\)
Usage : Features redondantes, interprétabilité
Effet : Réduction de tous les poids
Gradient : \(\frac{\partial R}{\partial w_i} = 2w_i\)
Mise à jour : \(w_{t+1} = (1 - 2\eta\alpha)w_t - \eta \nabla L(w_t)\)
L1 (Lasso) :
\[R(w) = \|w\|_1 = \sum_{i=1}^d |w_i|\]L2 (Ridge) :
\[R(w) = \|w\|_2^2 = \sum_{i=1}^d w_i^2\]L1 : Gradient constant (en valeur absolue)
\[\frac{\partial |w_i|}{\partial w_i} = \begin{cases} +1 & \text{si } w_i > 0 \\ -1 & \text{si } w_i < 0 \\ [-1, 1] & \text{si } w_i = 0 \end{cases}\]L2 : Gradient proportionnel à $w$
\[\frac{\partial w_i^2}{\partial w_i} = 2w_i\]Avec L1 :
\[w_{t+1} = w_t - \eta(\nabla L + \alpha \cdot \text{sign}(w_t))\]➜ On retire une quantité constante $\eta \alpha$ à chaque itération ➜ Les petits poids arrivent rapidement à zéro exact
Avec L2 :
\[w_{t+1} = w_t - \eta(\nabla L + 2\alpha w_t) = (1-2\eta\alpha)w_t - \eta \nabla L\]➜ On multiplie par $(1-2\eta\alpha)$ ➜ Les poids diminuent mais n’atteignent jamais zéro (décroissance exponentielle)
Contrainte L1 : $|w_1| + |w_2| \leq t$ → forme un losange
Contrainte L2 : $w_1^2 + w_2^2 \leq t$ → forme un cercle
Quand les contours de la fonction de coût rencontrent la contrainte :
[Imagine un graphique 2D avec contours et contraintes]
Combinaison L1 + L2 :
\[R(w) = \rho \|w\|_1 + \frac{1-\rho}{2}\|w\|_2^2\]Avantages :
SGDRegressor(penalty='elasticnet',
l1_ratio=0.5)
Paramètres Scikit-learn :
SGDRegressor(
penalty='l2', # 'l1', 'l2', 'elasticnet'
alpha=0.0001, # Force régularisation
l1_ratio=0.15 # Si elasticnet
)
Impact :
Principe : Arrêter l’entraînement avant la fin si pas d’amélioration
Algorithme :
Implémentation :
SGDRegressor(
early_stopping=True,
validation_fraction=0.1,
n_iter_no_change=5
)
from sklearn.linear_model import SGDRegressor
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
# Normalisation
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Division
X_train, X_val, y_train, y_val = train_test_split(
X_scaled, y, test_size=0.2
)
# Modèle
sgd = SGDRegressor(
loss='squared_error',
penalty='l2',
alpha=0.001,
learning_rate='adaptive',
eta0=0.01,
early_stopping=True
)
sgd.fit(X_train, y_train)
SGD classique : $w_t = w_{t-1} - \eta \nabla L$
SGD + Momentum : \(v_t = \beta v_{t-1} + \nabla L\) \(w_t = w_{t-1} - \eta v_t\)
Nesterov : Momentum anticipé
AdaGrad : LR adaptatif par paramètre
RMSprop : Moyenne mobile du gradient carré
Adam (Adaptive Moment Estimation) :
AdamW : Adam avec weight decay découplé
Nadam : Nesterov + Adam
RAdam : Warmup automatique
Choisir : Adam par défaut, SGD+momentum si besoin de généralisation
Optimiseur | Vitesse | Stabilité | Généralisation | Usage |
---|---|---|---|---|
SGD | ⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | Classique |
SGD+Momentum | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | Recommandé |
Adam | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | Deep Learning |
RMSprop | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ | RNN |
Problème SGD : Oscillations dans les ravins
Solution : Accumuler la “vitesse”
\(v_t = \beta v_{t-1} + (1-\beta) \nabla L_t\) \(w_t = w_{t-1} - \eta v_t\)
Effet :
Courbes d’apprentissage :
Signes de problèmes :
Stratégies :
Paramètres critiques :
cas general
deep learning
Choix du Learning Rate : Sensible, difficile à optimiser
Minima locaux : Peut rester coincé (fonctions non-convexes)
Saddle points : Ralentit près des points-selle
Scaling : Sensible à l’échelle des features
Solution : Optimiseurs adaptatifs (Adam, etc.)
SGD convient si :
Alternatives :
Variance Reduction :
Second-ordre :
Distribué :
Particularités :
Best Practices :
Papers fondateurs :
Documentation :
Cours :
SGD = Outil fondamental du Machine Learning
Points clés :
Recommandation : Commencer avec SGD+momentum ou Adam