Régression Polynomiale
Apprenez comment fonctionne la régression polynomiale, quand l'utiliser, et comment construire, évaluer et ajuster un modèle polynomial en Python avec scikit-learn.
La régression linéaire trace une droite à travers les données. Lorsque la relation entre l'entrée et la sortie suit une courbe — pensez à la trajectoire d'une balle, à la croissance d'une colonie bactérienne ou à une courbe dose-réponse — une droite est un mauvais ajustement, peu importe le nombre de caractéristiques ajoutées. La régression polynomiale résout ce problème en ajoutant des versions élevées à des puissances des caractéristiques existantes (x², x³, …), ce qui permet au modèle de se courber pour s'adapter à la courbure des données tout en utilisant le mécanisme des moindres carrés ordinaires en coulisses.
Cette page couvre :
- Comment la régression polynomiale étend mathématiquement la régression linéaire
- Quand utiliser la régression polynomiale et quel degré choisir
- Un pipeline scikit-learn complet : transformation des caractéristiques, entraînement, évaluation
- Le piège du surapprentissage et comment le détecter avec des courbes d'entraînement/test
- La comparaison de la qualité des modèles avec RMSE et R²
Comment fonctionne la régression polynomiale
L'équation
La régression linéaire ajuste :
y = β₀ + β₁xLa régression polynomiale de degré n ajuste :
y = β₀ + β₁x + β₂x² + β₃x³ + … + βₙxⁿL'idée clé est que x², x³, etc. sont traités comme des caractéristiques supplémentaires. Le modèle est toujours linéaire par rapport à ses coefficients (valeurs β) ; seules les caractéristiques sont non linéaires. Cela signifie que les moindres carrés ordinaires fonctionnent toujours — il suffit de pré-traiter la matrice d'entrée en premier.
Degrés et leur signification
| Degré | Nom | Forme |
|---|---|---|
| 1 | Linéaire | Droite |
| 2 | Quadratique | Courbe simple (parabole) |
| 3 | Cubique | Un point d'inflexion |
| 4+ | Ordre supérieur | Courbes plus complexes |
Choisir le bon degré est la compétence centrale. Trop bas et le modèle sous-apprend (manque la vraie courbure). Trop haut et le modèle sur-apprend (mémorise le bruit et échoue sur de nouvelles données).
Quand utiliser la régression polynomiale
Utilisez la régression polynomiale quand :
- Un nuage de points montre une courbe claire dans les données qu'une droite ne peut pas capturer
- Vous voulez une alternative rapide et interprétable aux modèles à base d'arbres pour une courbure modérée
- Vous avez déjà une base de travail avec la régression linéaire et elle sous-apprend
Préférez d'autres approches quand :
- La relation est très complexe ou comporte de nombreuses caractéristiques → essayez les arbres de décision ou le gradient boosting
- Vous devez prédire bien au-delà de la plage d'entraînement (extrapolation) → les polynômes de degré élevé divergent fortement en dehors de la plage de données
- Vous avez de nombreuses caractéristiques d'entrée → chaque caractéristique obtient
ntermes polynomiaux, ce qui fait croître rapidement la matrice de caractéristiques
Construction d'un modèle de régression polynomiale
Étape 1 : Importer les bibliothèques
import numpy as np
import matplotlib
matplotlib.use('Agg') # non-interactive backend for scripts
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_scoreLe transformateur PolynomialFeatures de scikit-learn gère automatiquement l'expansion x → [1, x, x², …, xⁿ]. L'encapsuler avec LinearRegression dans un Pipeline garde le code propre et prévient les fuites de données lors de la validation croisée.
Étape 2 : Créer des données d'exemple
rng = np.random.default_rng(42)
X = rng.uniform(-3, 3, 80).reshape(-1, 1) # 80 points from -3 to 3
y = 0.5 * X.ravel()**2 - X.ravel() + 2 + rng.normal(0, 0.5, 80)
# True relationship: y ≈ 0.5x² - x + 2 (a parabola with noise)La relation sous-jacente est quadratique, donc un polynôme de degré 2 devrait la retrouver correctement. C'est le type de situation où la régression linéaire sous-apprend systématiquement.
Étape 3 : Diviser en ensembles d'entraînement et de test
Divisez toujours avant d'ajuster afin d'avoir des données retenues pour l'évaluation. Voir Train/Test Split pour une explication complète.
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
print(f"Training samples: {len(X_train)}") # 64
print(f"Test samples: {len(X_test)}") # 16Étape 4 : Construire et ajuster le pipeline
degree = 2
model = make_pipeline(
PolynomialFeatures(degree=degree, include_bias=False),
LinearRegression()
)
model.fit(X_train, y_train)make_pipeline enchaîne les deux étapes : PolynomialFeatures transforme l'entrée, puis LinearRegression s'ajuste sur les caractéristiques développées. Vous appelez .fit(), .predict() et .score() sur le pipeline exactement comme sur un estimateur simple.
Étape 5 : Évaluer le modèle
y_pred_train = model.predict(X_train)
y_pred_test = model.predict(X_test)
rmse_train = np.sqrt(mean_squared_error(y_train, y_pred_train))
rmse_test = np.sqrt(mean_squared_error(y_test, y_pred_test))
r2_train = r2_score(y_train, y_pred_train)
r2_test = r2_score(y_test, y_pred_test)
print(f"Train RMSE: {rmse_train:.4f} Test RMSE: {rmse_test:.4f}")
print(f"Train R²: {r2_train:.4f} Test R²: {r2_test:.4f}")Résultat attendu :
Train RMSE: 0.4605 Test RMSE: 0.4538
Train R²: 0.9480 Test R²: 0.9514Un R² proche de 0,95 sur des données retenues signifie que le modèle explique environ 95 % de la variance — un excellent ajustement pour des données aussi bruitées. Les scores d'entraînement et de test sont proches, ce qui indique que le modèle généralise plutôt qu'il ne sur-apprend.
Interprétation des métriques :
- RMSE (Root Mean Squared Error) — l'erreur moyenne dans les mêmes unités que
y. Plus c'est bas, mieux c'est. - R² (coefficient de détermination) — fraction de la variance expliquée. Des valeurs plus proches de 1,0 sont meilleures ; 0 signifie que le modèle n'est pas meilleur que la prédiction de la moyenne.
Étape 6 : Visualiser l'ajustement
X_line = np.linspace(-3.5, 3.5, 200).reshape(-1, 1)
y_line = model.predict(X_line)
plt.figure(figsize=(7, 5))
plt.scatter(X_train, y_train, alpha=0.6, label='Train', color='steelblue', s=25)
plt.scatter(X_test, y_test, alpha=0.8, label='Test', color='orange', s=40, zorder=5)
plt.plot(X_line, y_line, color='red', linewidth=2, label=f'Degree-{degree} fit')
plt.xlabel('x')
plt.ylabel('y')
plt.title('Polynomial Regression (degree 2)')
plt.legend()
plt.tight_layout()
plt.savefig('poly_reg_fit.png', dpi=120)
print("Plot saved.")La courbe rouge devrait passer régulièrement à travers le nuage de points — assez proche pour capturer la forme parabolique sans serpenter à travers les points de bruit individuels.
Le piège du surapprentissage
Le principal risque de la régression polynomiale est de choisir un degré trop élevé. Un polynôme de degré 15 peut mémoriser parfaitement les 80 points d'entraînement (RMSE ≈ 0) mais oscillera sauvagement entre eux et échouera sur de nouvelles données.
Le diagnostic standard est une courbe de validation : tracer l'erreur d'entraînement et de test en fonction du degré.
degrees = range(1, 12)
train_rmses, test_rmses = [], []
for d in degrees:
m = make_pipeline(PolynomialFeatures(d, include_bias=False), LinearRegression())
m.fit(X_train, y_train)
train_rmses.append(np.sqrt(mean_squared_error(y_train, m.predict(X_train))))
test_rmses.append(np.sqrt(mean_squared_error(y_test, m.predict(X_test))))
plt.figure(figsize=(7, 4))
plt.plot(degrees, train_rmses, 'o-', label='Train RMSE', color='steelblue')
plt.plot(degrees, test_rmses, 's-', label='Test RMSE', color='orange')
plt.xlabel('Polynomial Degree')
plt.ylabel('RMSE')
plt.title('Validation Curve: Choosing the Best Degree')
plt.legend()
plt.tight_layout()
plt.savefig('poly_reg_validation_curve.png', dpi=120)
print("Plot saved.")Ce que vous verrez :
- Degré 1 (linéaire) : le RMSE d'entraînement et de test sont tous deux élevés — sous-apprentissage.
- Degré 2 : les deux chutent fortement — le modèle capture la vraie forme.
- Degré 5+ : le RMSE d'entraînement continue de baisser, mais le RMSE de test augmente — l'écart entre entraînement et test signale un surapprentissage.
Le meilleur degré est celui où le RMSE de test est le plus bas avant qu'il ne commence à remonter. Dans cet exemple, c'est le degré 2, ce qui correspond au vrai processus générateur de données.
Pour une sélection de degré plus robuste, utilisez la validation croisée au lieu d'une seule division entraînement/test.
Utilisation de numpy.polyfit (alternative rapide)
Pour des problèmes univariés simples, la fonction polyfit de NumPy offre un ajustement en une ligne sans scikit-learn. Elle est utile pour l'analyse exploratoire mais ne s'intègre pas aux pipelines ni à la validation croisée.
import numpy as np
x = np.array([1, 2, 3, 4, 5], dtype=float)
y = np.array([2.1, 4.0, 9.2, 16.1, 25.0])
# Fit a degree-2 polynomial
# Returns coefficients from highest to lowest degree: [a2, a1, a0]
coefficients = np.polyfit(x, y, 2)
poly = np.poly1d(coefficients)
print("Coefficients (highest to lowest degree):", np.round(coefficients, 3))
print("Prediction at x=6:", round(poly(6), 2))Résultat attendu :
Coefficients (highest to lowest degree): [ 1.121 -0.939 1.76 ]
Prediction at x=6: 36.5La courbe ajustée est approximativement y ≈ 1,12x² - 0,94x + 1,76, ce qui est proche de la vraie relation y = x² dans ces données. Les coefficients ne sont pas exactement [1, 0, 0] car il n'y a que cinq points de données bruités à ajuster.
np.poly1d encapsule les coefficients afin que vous puissiez appeler le polynôme comme une fonction : poly(6) évalue 1,121(36) - 0,939(6) + 1,76 ≈ 36,5.
Pipeline complet (toutes les étapes ensemble)
import numpy as np
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
# 1. Generate data (true relationship: y = 0.5x² - x + 2 + noise)
rng = np.random.default_rng(42)
X = rng.uniform(-3, 3, 80).reshape(-1, 1)
y = 0.5 * X.ravel()**2 - X.ravel() + 2 + rng.normal(0, 0.5, 80)
# 2. Split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 3. Build and fit
model = make_pipeline(PolynomialFeatures(degree=2, include_bias=False), LinearRegression())
model.fit(X_train, y_train)
# 4. Evaluate
rmse = np.sqrt(mean_squared_error(y_test, model.predict(X_test)))
r2 = r2_score(y_test, model.predict(X_test))
print(f"Test RMSE: {rmse:.4f}")
print(f"Test R²: {r2:.4f}")
# 5. Inspect learned coefficients
lr = model.named_steps['linearregression']
pf = model.named_steps['polynomialfeatures']
feature_names = pf.get_feature_names_out(['x'])
for name, coef in zip(feature_names, lr.coef_):
print(f" {name}: {coef:.4f}")
print(f" intercept: {lr.intercept_:.4f}")Résultat attendu :
Test RMSE: 0.4538
Test R²: 0.9514
x: -0.9625
x^2: 0.4909
intercept: 1.9654Les coefficients retrouvés (x^2 ≈ 0,49, x ≈ -0,96, intercept ≈ 1,97) correspondent étroitement aux vraies valeurs (0,5, -1, 2), ce qui confirme que le modèle a appris la forme correcte.
Mise à l'échelle des caractéristiques et régression polynomiale
Lorsqu'on travaille avec des polynômes de degré élevé, les valeurs des caractéristiques croissent rapidement — x = 100 produit x² = 10 000 et x³ = 1 000 000. Cela peut rendre le problème des moindres carrés numériquement instable.
La solution standard est de mettre les caractéristiques à l'échelle avec StandardScaler à l'intérieur du pipeline, avant l'expansion polynomiale :
from sklearn.preprocessing import StandardScaler
model = make_pipeline(
StandardScaler(),
PolynomialFeatures(degree=3, include_bias=False),
LinearRegression()
)Placer StandardScaler en premier signifie qu'il est ajusté uniquement sur les données d'entraînement et appliqué de manière cohérente aux données de test — sans fuite. Voir Feature Scaling pour des détails sur l'importance de la mise à l'échelle.
Pièges courants
Ignorer la division entraînement/test. Si vous ajustez et évaluez sur les mêmes données, les polynômes de degré élevé sembleront fonctionner parfaitement tout en échouant complètement sur de nouvelles entrées. Évaluez toujours sur des données retenues.
Extrapoler en dehors de la plage d'entraînement. Les courbes polynomiales oscillent et divergent en dehors de la plage des données d'entraînement. Un modèle entraîné sur x ∈ [0, 5] peut donner des prédictions absurdes à x = 10. La régression linéaire extrapole de manière plus conservative.
Ne pas mettre à l'échelle avant les termes de degré élevé. De grandes valeurs de caractéristiques combinées à une expansion de degré élevé peuvent produire un débordement numérique ou des matrices mal conditionnées. Utilisez StandardScaler dans le pipeline.
Choisir le degré en se basant uniquement sur l'erreur d'entraînement. Le RMSE d'entraînement diminue toujours à mesure que le degré augmente. Utilisez le RMSE de test ou la validation croisée pour trouver le degré qui généralise.
Oublier include_bias=False. PolynomialFeatures ajoute une colonne constante (terme d'intercept) par défaut. LinearRegression ajoute également son propre intercept. Passer include_bias=False à PolynomialFeatures évite la colonne redondante.
Étapes suivantes
- Régression linéaire — la base en droite sur laquelle s'appuie la régression polynomiale
- Régression multiple — combiner de nombreuses caractéristiques (la régression polynomiale appliquée à plusieurs entrées produit également des termes d'interaction)
- Train/Test Split — la bonne façon d'évaluer tout modèle de régression
- Validation croisée — plus robuste qu'une seule division pour ajuster le degré polynomial
- Feature Scaling — pourquoi et comment standardiser les entrées avant l'ajustement