W3docs

Polynomiale Regression

Lerne, wie polynomiale Regression funktioniert, wann man sie einsetzt und wie man ein Polynommodell in Python mit scikit-learn aufbaut, bewertet und optimiert.

Lineare Regression zeichnet eine Gerade durch die Daten. Wenn die Beziehung zwischen Ein- und Ausgabe eine Kurve bildet — etwa die Flugbahn eines Balls, das Wachstum einer Bakterienkolonie oder eine Dosis-Wirkungs-Kurve — passt eine Gerade nicht gut, egal wie viele Merkmale man hinzufügt. Polynomiale Regression löst dieses Problem, indem sie potenzierte Versionen der vorhandenen Merkmale (, , …) hinzufügt, sodass das Modell der Krümmung der Daten folgen kann, während es intern weiterhin die gewöhnliche Methode der kleinsten Quadrate verwendet.

Diese Seite behandelt:

  • Wie polynomiale Regression die lineare Regression mathematisch erweitert
  • Wann polynomiale Regression eingesetzt werden sollte und welchen Grad man wählt
  • Eine vollständige scikit-learn-Pipeline: Merkmalstransformation, Training, Auswertung
  • Die Overfitting-Falle und wie man sie mit Train/Test-Kurven erkennt
  • Vergleich der Modellgüte mit RMSE und R²

Wie Polynomiale Regression Funktioniert

Die Gleichung

Lineare Regression passt an:

y = β₀ + β₁x

Polynomiale Regression vom Grad n passt an:

y = β₀ + β₁x + β₂x² + β₃x³ + … + βₙxⁿ

Die entscheidende Erkenntnis ist, dass , und so weiter als zusätzliche Merkmale behandelt werden. Das Modell ist bezüglich seiner Koeffizienten (β-Werte) weiterhin linear; nur die Merkmale sind nichtlinear. Das bedeutet, dass die gewöhnliche Methode der kleinsten Quadrate weiterhin funktioniert — man muss lediglich die Eingabematrix vorab verarbeiten.

Grade und ihre Bedeutung

GradBezeichnungForm
1LinearGerade
2QuadratischEinfache Kurve (Parabel)
3KubischEin Wendepunkt
4+Höherer GradKomplexere Kurven

Die Wahl des richtigen Grades ist die zentrale Fähigkeit. Zu niedrig, und das Modell unteranpasst (verfehlt echte Krümmungen). Zu hoch, und das Modell überanpasst (memoriert Rauschen und versagt bei neuen Daten).

Wann Polynomiale Regression Einzusetzen Ist

Verwende polynomiale Regression, wenn:

  • Ein Streudiagramm eine deutliche Kurve in den Daten zeigt, die eine Gerade nicht erfassen kann
  • Du eine schnelle, interpretierbare Alternative zu baumbasierten Modellen für moderate Krümmungen möchtest
  • Du bereits eine funktionierende lineare Regression als Baseline hast und diese unteranpasst

Bevorzuge andere Ansätze, wenn:

  • Die Beziehung sehr komplex ist oder viele Merkmale hat → versuche Entscheidungsbäume oder Gradient Boosting
  • Du weit außerhalb des Trainingsbereichs vorhersagen musst (Extrapolation) → Polynome hohen Grades divergieren außerhalb des Datenbereichs stark
  • Du viele Eingabemerkmale hast → jedes Merkmal erhält n Polynomterme, sodass die Merkmalsmatrix schnell wächst

Aufbau eines Polynomialen Regressionsmodells

Schritt 1: Bibliotheken Importieren

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_score

scikit-learns PolynomialFeatures-Transformer übernimmt die Erweiterung x → [1, x, x², …, xⁿ] automatisch. Das Einbinden in eine Pipeline zusammen mit LinearRegression hält den Code sauber und verhindert Datenleckagen bei der Kreuzvalidierung.

Schritt 2: Beispieldaten Erstellen

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)

Die zugrundeliegende Beziehung ist quadratisch, sodass ein Polynom vom Grad 2 sie gut wiederherstellen sollte. Dies ist genau die Situation, in der lineare Regression systematisch unteranpasst.

Schritt 3: In Trainings- und Testmengen Aufteilen

Teile immer vor dem Anpassen auf, damit du zurückgehaltene Daten zur Auswertung hast. Siehe Train/Test Split für eine vollständige Erklärung.

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

Schritt 4: Pipeline Aufbauen und Anpassen

degree = 2
model = make_pipeline(
    PolynomialFeatures(degree=degree, include_bias=False),
    LinearRegression()
)
model.fit(X_train, y_train)

make_pipeline verkettet die beiden Schritte: PolynomialFeatures transformiert die Eingabe, dann passt LinearRegression die erweiterten Merkmale an. Du rufst .fit(), .predict() und .score() auf der Pipeline genauso auf wie bei einem einfachen Schätzer.

Schritt 5: Modell Auswerten

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}")

Erwartete Ausgabe:

Train RMSE: 0.4605   Test RMSE: 0.4538
Train R²:   0.9480   Test R²:   0.9514

Ein R² nahe 0,95 auf zurückgehaltenen Daten bedeutet, dass das Modell etwa 95 % der Varianz erklärt — eine hervorragende Anpassung für so verrauschte Daten. Trainings- und Testwerte liegen nahe beieinander, was zeigt, dass das Modell generalisiert und nicht überanpasst.

Interpretation der Metriken:

  • RMSE (Root Mean Squared Error) — der durchschnittliche Fehler in den gleichen Einheiten wie y. Niedriger ist besser.
  • R² (Bestimmtheitsmaß) — Anteil der erklärten Varianz. Werte näher an 1,0 sind besser; 0 bedeutet, das Modell ist nicht besser als die Vorhersage des Mittelwerts.

Schritt 6: Anpassung Visualisieren

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.")

Die rote Kurve sollte glatt durch die Punktwolke verlaufen — nah genug, um die parabolische Form zu erfassen, ohne durch einzelne Rauschpunkte zu schlängeln.

Die Overfitting-Falle

Das größte Risiko der polynomialen Regression ist die Wahl eines zu hohen Grades. Ein Polynom vom Grad 15 kann alle 80 Trainingspunkte perfekt memorieren (RMSE ≈ 0), wird aber zwischen ihnen wild schwingen und bei neuen Daten versagen.

Das Standarddiagnostikum ist eine Validierungskurve: Trage den Trainings- und Testfehler als Funktion des Grades auf.

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.")

Was zu sehen ist:

  • Grad 1 (linear): Sowohl Train- als auch Test-RMSE sind hoch — Unteranpassung.
  • Grad 2: Beide fallen stark — das Modell erfasst die wahre Form.
  • Grad 5+: Der Train-RMSE sinkt weiter, aber der Test-RMSE steigt — die Lücke zwischen Train und Test signalisiert Überanpassung.

Der beste Grad ist derjenige, bei dem der Test-RMSE am niedrigsten ist, bevor er wieder ansteigt. In diesem Beispiel ist das Grad 2, was dem wahren datenerzeugenden Prozess entspricht.

Für eine robustere Gradauswahl verwende Kreuzvalidierung anstelle einer einzelnen Train/Test-Aufteilung.

numpy.polyfit Verwenden (Schnelle Alternative)

Für einfache univariate Probleme bietet NumPys polyfit-Funktion eine einzeilige Anpassung ohne scikit-learn. Sie eignet sich für explorative Analysen, lässt sich aber nicht in Pipelines oder die Kreuzvalidierung integrieren.

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))

Erwartete Ausgabe:

Coefficients (highest to lowest degree): [ 1.121 -0.939  1.76 ]
Prediction at x=6: 36.5

Die angepasste Kurve ist näherungsweise y ≈ 1,12x² - 0,94x + 1,76, was nahe an der wahren Beziehung y = x² in diesen Daten liegt. Die Koeffizienten sind nicht exakt [1, 0, 0], weil nur fünf verrauschte Datenpunkte zum Anpassen vorhanden sind.

np.poly1d kapselt die Koeffizienten, sodass man das Polynom wie eine Funktion aufrufen kann: poly(6) berechnet 1,121(36) - 0,939(6) + 1,76 ≈ 36,5.

Vollständige Pipeline (Alle Schritte Zusammen)

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}")

Erwartete Ausgabe:

Test RMSE: 0.4538
Test R²:   0.9514
  x: -0.9625
  x^2: 0.4909
  intercept: 1.9654

Die wiederhergestellten Koeffizienten (x^2 ≈ 0,49, x ≈ -0,96, Achsenabschnitt ≈ 1,97) stimmen eng mit den wahren Werten (0,5, -1, 2) überein, was bestätigt, dass das Modell die korrekte Form erlernt hat.

Feature-Skalierung und Polynomiale Regression

Bei der Arbeit mit Polynomen höheren Grades wachsen die Merkmalswerte schnell — x = 100 ergibt x² = 10.000 und x³ = 1.000.000. Dies kann das Kleinste-Quadrate-Problem numerisch instabil machen.

Die Standardlösung ist, Merkmale mit StandardScaler innerhalb der Pipeline zu skalieren, vor der polynomialen Erweiterung:

from sklearn.preprocessing import StandardScaler

model = make_pipeline(
    StandardScaler(),
    PolynomialFeatures(degree=3, include_bias=False),
    LinearRegression()
)

Die Platzierung von StandardScaler an erster Stelle stellt sicher, dass er nur auf Trainingsdaten angepasst und konsistent auf Testdaten angewendet wird — kein Datenleck. Siehe Feature Scaling für Details, warum Skalierung wichtig ist.

Häufige Fallstricke

Die Train/Test-Aufteilung überspringen. Wenn man auf denselben Daten anpasst und auswertet, scheinen Polynome hohen Grades perfekt zu funktionieren, während sie bei neuen Eingaben vollständig versagen. Werte immer auf zurückgehaltenen Daten aus.

Extrapolation außerhalb des Trainingsbereichs. Polynomkurven schwingen und divergieren außerhalb des Bereichs der Trainingsdaten. Ein Modell, das auf x ∈ [0, 5] trainiert wurde, kann bei x = 10 absurde Vorhersagen liefern. Lineare Regression extrapoliert konservativer.

Keine Skalierung vor Termen hohen Grades. Große Merkmalswerte kombiniert mit einer Erweiterung hohen Grades können numerischen Überlauf oder schlecht konditionierte Matrizen erzeugen. Verwende StandardScaler in der Pipeline.

Den Grad anhand des Trainingsfehlers allein wählen. Der Trainings-RMSE nimmt immer ab, wenn der Grad zunimmt. Verwende den Test-RMSE oder Kreuzvalidierung, um den Grad zu finden, der generalisiert.

include_bias=False vergessen. PolynomialFeatures fügt standardmäßig eine konstante Spalte (Achsenabschnittsterm) hinzu. LinearRegression fügt ebenfalls einen eigenen Achsenabschnitt hinzu. Das Übergeben von include_bias=False an PolynomialFeatures vermeidet die redundante Spalte.

Nächste Schritte

  • Lineare Regression — die Grundlage mit gerader Linie, auf der polynomiale Regression aufbaut
  • Multiple Regression — viele Merkmale kombinieren (polynomiale Regression auf mehrere Eingaben angewendet erzeugt auch Interaktionsterme)
  • Train/Test Split — die korrekte Methode zur Auswertung jedes Regressionsmodells
  • Kreuzvalidierung — robuster als eine einzelne Aufteilung zur Abstimmung des Polynomgrades
  • Feature Scaling — warum und wie man Eingaben vor dem Anpassen standardisiert
Was this page helpful?