Vorbereitung auf klassische ML-Interviews

Klassische Machine-Learning-Ingenieur-Interviewfragen

15 ausgewählte klassische Machine-Learning-Interviewfragen, gruppiert nach Erfahrungsstufe. Nutzen Sie diese zur Überprüfung von Grundlagen, praktischen Abwägungen und dem produktionsrelevanten Denken auf Senior-Niveau.

Klassisches ML KI-Interview startenKeine Kreditkarte erforderlich. 1 kostenlose Sitzung verfügbar.
Technisches Interview-Training auf EnglischEin Modus, in dem Nicht-Muttersprachler technische Interviews üben können.

Junior-Fragen

1Worin besteht der Unterschied zwischen einem Modellparameter und einem Hyperparameter im überwachten Lernen?

Im überwachten maschinellen Lernen sind Modellparameter interne Variablen, die direkt aus den Trainingsdaten über einen Optimierungsalgorithmus (wie Gradientenabstieg oder Normalengleichungen) gelernt werden. Beispiele hierfür sind die Regressionsgewichte und der Bias in linearen Modellen oder Aufteilungsschwellenwerte in Entscheidungsbäumen. Im Gegensatz dazu sind Hyperparameter externe Konfigurationseinstellungen, die vor dem Training festgelegt werden und den Lernprozess, die Modellkapazität oder die Architektur steuern. Sie können nicht direkt durch die Minimierung des Standard-Trainingsverlusts gelernt werden, da der Optimierer sonst trivial überanpassen würde (z.B. indem die Baumtiefe auf unendlich gesetzt wird). Beispiele hierfür sind die Lernrate, die Regularisierungsstärke (Lambda/C), die Anzahl der Bäume in einem Ensemble und die maximale Baumtiefe. Hyperparameter werden mithilfe von Validierungsdaten oder Kreuzvalidierung abgestimmt.

from sklearn.linear_model import Ridge
import numpy as np

X = np.array([[1.0], [2.0], [3.0]])
y = np.array([2.0, 4.0, 6.0])

# Hyperparameter: alpha (regularization strength set beforehand)
model = Ridge(alpha=1.0)

# Fitting optimizes internal parameters on training data
model.fit(X, y)

# Learned parameters
print(f"Weight (Parameter): {model.coef_[0]:.4f}")
print(f"Intercept (Parameter): {model.intercept_:.4f}")
Diese Frage mit einem KI-Coach beantworten

2Welche Annahmen macht die Kleinste-Quadrate-Regression (OLS) und wie würde die Residuen-Diagnostik Annahmeverletzungen aufdecken?

Die Kleinste-Quadrate-Regression (OLS) stützt sich auf mehrere Kernannahmen: 1. Linearität: Die Beziehung zwischen Prädiktoren und der Ergebnisgröße ist linear in den Parametern. 2. Unabhängigkeit der Fehlerterme: Beobachtungen und Residuenfehler sind gegenseitig unabhängig (keine Autokorrelation). 3. Homoskedastizität: Die Fehlerterme haben eine konstante Varianz über alle Ebenen der Prädiktoren hinweg. 4. Normalverteilung der Residuen: Die Fehlerterme sind normalverteilt (erforderlich für gültige Konfidenzintervalle und Hypothesentests). 5. Keine Multikollinearität: Prädiktoren sind nicht linear abhängig (die Designmatrix hat vollen Spaltenrang). Die Residuen-Diagnostik deckt Verletzungen wie folgt auf: - Residuen-vs.-Anpassungswerte-Diagramm: Krümmung oder nicht-zufällige Muster zeigen Nicht-Linearität; eine Trichter- oder Fächerform zeigt Heteroskedastizität (nicht-konstante Varianz). - Normal-Q-Q-Diagramm: Systematisches Abweichen von der geraden Diagonalen (z.B. S-Kurven oder schwere Enden) zeigt Nicht-Normalität. - Residuen-vs.-Reihenfolge-/Zeit-Diagramm: Systematische Trends oder zyklische Muster zeigen autokorrelierte Fehler. - Leverage-/Cook-Abstand-Diagramm: Identifiziert Ausreißer mit hoher Hebelwirkung oder einflussreiche Punkte, die das angepasste Modell unverhältnismäßig verschieben.

import numpy as np
import statsmodels.api as sm

np.random.seed(42)
X = np.linspace(1, 10, 50)
# Quadratic underlying pattern creates a linearity violation
y = 2 * X + 0.5 * (X ** 2) + np.random.normal(0, 2, 50)

X_with_const = sm.add_constant(X)
model = sm.OLS(y, X_with_const).fit()
residuals = model.resid

print(f"Mean Residual: {np.mean(residuals):.4f}")
print(f"Curvature in residuals indicates model misspecification.")
Diese Frage mit einem KI-Coach beantworten

3Wie modelliert die logistische Regression die binäre Klassifikation, und welche Rolle spielt dabei die Sigmoidfunktion?

Die logistische Regression modelliert die binäre Klassifikation, indem sie die A-posteriori-Klassenwahrscheinlichkeit $P(Y=1|X)$ schätzt. Um sicherzustellen, dass die vorhergesagten Wahrscheinlichkeiten im Bereich $(0, 1)$ bleiben, modelliert die logistische Regression die Log-Odds (Logit) der positiven Klasse als lineare Funktion der Eingaben: $\ln\left(\frac{p}{1-p}\right) = w^T x + b$. Die Sigmoid- (logistische) Funktion, $\sigma(z) = \frac{1}{1 + e^{-z}}$, dient als Linkfunktion, die einen beliebigen reellwertigen linearen Score $z = w^T x + b \in (-\infty, +\infty)$ monoton in eine gültige Wahrscheinlichkeit $p \in (0, 1)$ abbildet. Diskrete Klassenentscheidungen werden durch Anwenden einer Entscheidungsschwelle $\tau$ (typischerweise 0,5) getroffen: $\hat{y} = 1$, wenn $P(Y=1|X) \ge \tau$, andernfalls $0$. Da $\sigma(z) = 0.5$ genau dann auftritt, wenn $z = 0$, ist die Entscheidungsgrenze im Merkmalsraum die lineare Hyperebene $w^T x + b = 0$, was die Standard-Logistische Regression zu einem linearen Klassifikator macht.

import numpy as np

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

w = np.array([1.5, -2.0])
b = 0.5
x = np.array([2.0, 1.0])

z = np.dot(w, x) + b
prob = sigmoid(z)
threshold = 0.5
pred = int(prob >= threshold)

print(f"Log-odds (z): {z:.2f}")
print(f"Probability: {prob:.4f}")
print(f"Class Prediction: {pred}")
Diese Frage mit einem KI-Coach beantworten

4Was ist L2-Regularisierung, und wie verändert die Ridge-Regression die Zielfunktion und die Koeffizientenschätzungen?

Die L2-Regularisierung (Ridge-Regression) fügt der Kostenfunktion der gewöhnlichen kleinsten Quadrate (OLS) einen Strafterm hinzu, der proportional zur Summe der quadrierten Gewichte ist: $$\min_w \|y - Xw\|_2^2 + \lambda \|w\|_2^2$$ Analytisch modifiziert Ridge die Normalengleichungen, indem es $\lambda I$ zur Gram-Matrix vor der Inversion hinzufügt: $$w_{\text{ridge}} = (X^T X + \lambda I)^{-1} X^T y$$ Wesentliche Auswirkungen auf die Zielfunktion und die Koeffizientenschätzungen: 1. Schrumpfung: Koeffizienten werden proportional zur Merkmalsvarianz und -korrelation gegen Null geschrumpft, was die Modellkomplexität reduziert, ohne sie auf exakt Null zu zwingen. 2. Multikollinearität und Invertierbarkeit: Wenn Merkmale kollinear sind oder $p > N$, ist $X^T X$ singulär oder schlecht konditioniert. Das Hinzufügen von $\lambda I$ stellt sicher, dass $(X^T X + \lambda I)$ strikt positiv definit und invertierbar ist, was die Parameterschätzungen stabilisiert. 3. Bias-Varianz-Kompromiss: Eine Erhöhung von $\lambda$ führt eine absichtliche Verzerrung (Bias) in die Koeffizientenschätzungen ein, reduziert aber gleichzeitig die Varianz erheblich, was zu einem geringeren erwarteten Generalisierungsfehler auf ungesehenen Daten führt. 4. Anforderung an die Merkmalskalierung: Da der Strafterm alle Gewichte gleich behandelt, würden Merkmale mit größeren Skalen überproportional regularisiert. Merkmale müssen vor dem Fitting standardisiert werden (Nullmittelwert, Einheitsvarianz).

import numpy as np

def ridge_regression(X, y, alpha):
    X_std = (X - np.mean(X, axis=0)) / np.std(X, axis=0)
    n_features = X_std.shape[1]
    
    I = np.eye(n_features)
    beta = np.linalg.inv(X_std.T @ X_std + alpha * I) @ X_std.T @ y
    return beta

X = np.array([[1.0, 2.0], [2.0, 4.1], [3.0, 5.9], [4.0, 8.2]])
y = np.array([2.1, 4.0, 6.2, 8.1])
weights = ridge_regression(X, y, alpha=1.0)
print('Ridge Weights:', weights)
Diese Frage mit einem KI-Coach beantworten

5Wie unterteilt ein Entscheidungsbaum den Merkmalsraum rekursiv, und welche Kriterien werden verwendet, um Klassifikations-Splits auszuwählen?

Ein Entscheidungsbaum unterteilt den Merkmalsraum durch einen Top-Down-, Greedy-Algorithmus, der als **rekursive binäre Partitionierung** bezeichnet wird. Beginnend am Wurzelknoten mit allen Trainingsdaten sucht der Algorithmus über alle Merkmale und möglichen Schwellenwerte hinweg nach dem einzelnen achsenparallelen Split ($X_j \le t$), der die Reduzierung der Unreinheit maximiert. Der Datensatz wird in zwei Kindknoten aufgeteilt, und dieses Verfahren wird rekursiv auf jeden Kindknoten angewendet, bis ein Abbruchkriterium erreicht ist (z. B. maximale Tiefe, Mindestanzahl an Samples pro Blatt oder reine Knoten). Da Splits jeweils ein Merkmal gegen einen Schwellenwert bewerten, sind die resultierenden Entscheidungsgrenzen orthogonale Hyperebenen (achsenparallele rechteckige Regionen im Merkmalsraum). Zur Bewertung und Auswahl des besten Splits in Klassifikationsbäumen werden zwei Hauptkriterien für die Unreinheit verwendet: 1. **Gini-Unreinheit (verwendet in CART)**: Misst die Wahrscheinlichkeit, dass ein zufällig ausgewähltes Sample falsch klassifiziert würde, wenn es zufällig gemäß der Klassenverteilung des Knotens beschriftet würde. Für $K$ Klassen mit Proportionen $p_k$: $$I_G = 1 - \sum_{k=1}^K p_k^2$$ 2. **Entropie und Informationsgewinn (verwendet in ID3, C4.5)**: Entropie misst die Unsicherheit in einem Knoten: $H = -\sum_{k=1}^K p_k \log_2(p_k)$. Der Split wird so gewählt, dass der **Informationsgewinn** maximiert wird, der der Entropie des Elternknotens abzüglich der gewichteten mittleren Entropie der Kindknoten entspricht: $$IG = H(\text{parent}) - \sum_{c \in \{\text{left, right}\}} \frac{N_c}{N} H(c)$$ Beide Metriken erreichen 0, wenn ein Knoten vollständig rein ist (alle Samples gehören zu einer einzigen Klasse), und ihr Maximum, wenn die Klassen gleichmäßig verteilt sind.

import numpy as np

def gini(labels):
    _, counts = np.unique(labels, return_counts=True)
    p = counts / len(labels)
    return 1.0 - np.sum(p ** 2)

def entropy(labels):
    _, counts = np.unique(labels, return_counts=True)
    p = counts / len(labels)
    return -np.sum(p * np.log2(p + 1e-12))

# Evenly split node (impure) vs single-class node (pure)
impure_node = np.array([0]*10 + [1]*10)
pure_node = np.array([0]*20)

print(f"Impure - Gini: {gini(impure_node):.2f}, Entropy: {entropy(impure_node):.2f}")
print(f"Pure   - Gini: {gini(pure_node):.2f}, Entropy: {entropy(pure_node):.2f}")
Diese Frage mit einem KI-Coach beantworten

6Was ist k-Nächste-Nachbarn (kNN), und wie trifft es Vorhersagen für Klassifikation und Regression?

k-Nächste-Nachbarn (kNN) ist ein nicht-parametrischer, instanzbasierter (lazy) überwachter Lernalgorithmus. Er trainiert kein explizites parametrisches Modell; stattdessen speichert er den Trainingsdatensatz und führt alle Berechnungen während der Inferenz durch. Ablauf der Vorhersage: 1. Distanzberechnung: Wenn eine Abfrageinstanz ausgewertet wird, berechnet der Algorithmus ihre Distanz zu allen gespeicherten Trainingsinstanzen mithilfe einer angegebenen Metrik (z.B. euklidische, Manhattan- oder Minkowski-Distanz). 2. Nachbarauswahl: Er wählt die $k$ Trainingsinstanzen mit den kleinsten Distanzen zur Abfrageinstanz aus. 3. Aggregation: - Klassifikation: Die Klasse wird per Mehrheitsentscheid (Modus) unter den $k$ Nachbarn zugewiesen (oder durch distanzgewichtete Abstimmung). - Regression: Der kontinuierliche Zielwert wird durch Bildung des lokalen Durchschnitts (Mittelwert oder Median) der Zielwerte der $k$ Nachbarn vorhergesagt (oder durch distanzgewichteten Durchschnitt). Da Distanzberechnungen direkt von Merkmalsmaßstäben abhängen, ist Merkmalsnormalisierung oder -standardisierung unerlässlich, um zu verhindern, dass Merkmale mit großem Betrag die Distanzberechnungen dominieren.

from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor
from sklearn.preprocessing import StandardScaler
import numpy as np

X_train = np.array([[1000.0, 1.0], [2000.0, 2.0], [1500.0, 1.5], [5000.0, 5.0]])
y_cls = np.array([0, 0, 0, 1])
y_reg = np.array([10.0, 20.0, 15.0, 50.0])

# Feature scaling is mandatory for distance-based algorithms
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_train)

# 1. Classification (Majority Vote)
clf = KNeighborsClassifier(n_neighbors=3)
clf.fit(X_scaled, y_cls)

# 2. Regression (Local Average)
reg = KNeighborsRegressor(n_neighbors=3)
reg.fit(X_scaled, y_reg)

query = scaler.transform([[1800.0, 1.8]])
print('Classification:', clf.predict(query))
print('Regression:', reg.predict(query))
Diese Frage mit einem KI-Coach beantworten

7Geben Sie die bedingte Unabhängigkeitsannahme von Naive Bayes an und erklären Sie, warum Naive Bayes auch dann gut funktionieren kann, wenn diese verletzt wird.

Die bedingte Unabhängigkeitsannahme von Naive Bayes besagt, dass, gegeben das Klassenlabel Y = y, alle Features X_1, X_2, ..., X_d wechselseitig unabhängig sind: $P(X_1, ..., X_d | Y = y) = \prod_{j=1}^d P(X_j | Y = y)$. Unter Verwendung des Bayes-Theorems ist die Posteriori-Wahrscheinlichkeit: $P(Y = y | X) \propto P(Y = y) \prod_{j=1}^d P(X_j | Y = y)$, wobei $P(Y = y)$ der Klassen-Prior und $P(X_j | Y = y)$ die klassenbedingte Wahrscheinlichkeit ist (z.B. Gauß-Verteilung für kontinuierliche Features, Multinomialverteilung für Zählwerte). Naive Bayes funktioniert in der Praxis oft gut, obwohl Unabhängigkeitsverletzungen vorliegen, da die Klassifikation auf der argmax-Entscheidungsregel ($argmax_y P(Y=y | X)$) basiert und nicht auf einer genauen Wahrscheinlichkeitskalibrierung. Selbst wenn Feature-Korrelationen dazu führen, dass vorhergesagte Wahrscheinlichkeiten übermäßig selbstsicher oder verzerrt werden, behält die korrekte Klasse oft die höchste relative Rangfolge. Solange die Korrelation die Rangfolge der Klassenwahrscheinlichkeiten nicht umkehrt, bleibt die 0-1 Klassifikationsentscheidung genau.

from sklearn.naive_bayes import GaussianNB
import numpy as np

X = np.array([[1.0, 1.1], [1.2, 0.9], [-1.0, -1.2], [-0.8, -1.1]])
y = np.array([1, 1, 0, 0])

model = GaussianNB()
model.fit(X, y)
# Prediction uses argmax over class posterior scores
print("Predicted class:", model.predict([[1.1, 1.0]]))
Diese Frage mit einem KI-Coach beantworten

Middle-Fragen

8Leiten Sie die geschlossene Lösung der Kleinste-Quadrate-Methode (OLS) her oder erklären Sie sie, und geben Sie an, wann sie eindeutig existiert.

Die Zielfunktion der Kleinste-Quadrate-Methode (OLS – Ordinary Least Squares) minimiert die Residuenquadratsumme: $S(\beta) = \|y - X\beta\|^2 = (y - X\beta)^T (y - X\beta) = y^T y - 2\beta^T X^T y + \beta^T X^T X \beta$. Setzt man den Gradienten bezüglich $\beta$ auf null: $$\nabla_\beta S(\beta) = -2 X^T y + 2 X^T X \beta = 0 \implies X^T X \beta = X^T y$$ Dies sind die Normalengleichungen. Wenn $X^T X$ nicht-singulär (invertierbar) ist, ist die eindeutige geschlossene Lösung: $$\hat{\beta} = (X^T X)^{-1} X^T y$$ Geometrisch stellt $\hat{y} = X\hat{\beta} = X(X^T X)^{-1} X^T y = H y$ die orthogonale Projektion des Zielvektors $y$ auf den Spaltenraum der Designmatrix $X$ dar, wobei $H$ die Projektionsmatrix (Hut-Matrix) ist. Die Lösung existiert genau dann eindeutig, wenn $X^T X$ invertierbar ist, was erfordert, dass die $N \times P$-Designmatrix $X$ vollen Spaltenrang ($Rank(X) = P$) hat. Dies wiederum erfordert $N \ge P$ und keine exakte Multikollinearität (kein Feature ist eine Linearkombination anderer). Wenn $X$ rang-defizient ist, ist $X^T X$ singulär, was zu unendlich vielen Lösungen führt, was oft durch Regularisierung oder die Moore-Penrose-Pseudoinverse $X^+ y$ angegangen wird.

import numpy as np

# Design matrix X (with intercept column) and target y
X = np.array([[1, 1], [1, 2], [1, 3], [1, 4]])
y = np.array([2.1, 3.9, 6.2, 8.0])

# Normal equations: (X^T X)^(-1) X^T y
beta_hat = np.linalg.inv(X.T @ X) @ X.T @ y
H = X @ np.linalg.inv(X.T @ X) @ X.T
y_hat = H @ y

print(f"Beta: {beta_hat}")
print(f"Predictions: {y_hat}")
Diese Frage mit einem KI-Coach beantworten

9Was ist die Maximum-Likelihood-Schätzung, und wie führt sie zur Kreuzentropie-Zielfunktion der logistischen Regression?

Die Maximum-Likelihood-Schätzung (MLE) ist eine Methode zur Schätzung von Modellparametern $\theta$, indem Werte gewählt werden, die die Likelihood $L(\theta) = P(\mathcal{D}|\theta)$ des beobachteten Datensatzes maximieren. In der binären logistischen Regression wird jedes Label $y_i \in \{0, 1\}$ als eine unabhängige Bernoulli-Zufallsvariable modelliert, bedingt durch $x_i$, mit der Erfolgswahrscheinlichkeit $p_i = \sigma(w^T x_i + b)$. Die Wahrscheinlichkeitsmassenfunktion für Beobachtung $i$ ist $P(y_i|x_i) = p_i^{y_i} (1 - p_i)^{1 - y_i}$. Unter Annahme von i.i.d.-Stichproben ist die gemeinsame Likelihood: $$L(w, b) = \prod_{i=1}^N p_i^{y_i} (1 - p_i)^{1 - y_i}$$ Die Bildung des natürlichen Logarithmus wandelt das Produkt in eine rechnerisch handhabbare Summe von Log-Likelihoods um: $$\ell(w, b) = \sum_{i=1}^N \left[ y_i \ln(p_i) + (1 - y_i) \ln(1 - p_i) \right]$$ Da Optimierungsalgorithmen standardmäßig als Minimierungsprobleme formuliert werden, negieren wir die Log-Likelihood und normalisieren sie durch die Stichprobengröße $N$, was die Negative Log-Likelihood (NLL) ergibt, die genau der Kreuzentropie (Log Loss) für Binärklassifikation entspricht: $$J(w, b) = -\frac{1}{N} \sum_{i=1}^N \left[ y_i \ln(p_i) + (1 - y_i) \ln(1 - p_i) \right]$$ Diese Zielfunktion ist konvex in Bezug auf die linearen Logits/Gewichte, sodass geeignete numerische Solver ein globales Optimum finden. Strikte Konvexität und eine endliche eindeutige MLE erfordern zusätzliche Bedingungen wie ausreichenden Merkmalsrang, Regularisierung und keine perfekte Klassentrennung.

import numpy as np

y_true = np.array([1, 0, 1, 1])
y_prob = np.array([0.9, 0.2, 0.8, 0.4])

# Binary cross-entropy (Negative Log-Likelihood)
epsilon = 1e-15  # prevent log(0)
y_prob = np.clip(y_prob, epsilon, 1 - epsilon)
bce_loss = -np.mean(y_true * np.log(y_prob) + (1 - y_true) * np.log(1 - y_prob))

print(f"Binary Cross-Entropy Loss: {bce_loss:.4f}")
Diese Frage mit einem KI-Coach beantworten

10Wie optimiert der Gradientenabstieg ein klassisches ML-Ziel (Machine Learning), und wie beeinflussen Lernrate, Konvergenz und Konvexität das Training?

Der Gradientenabstieg minimiert eine empirische Verlustfunktion, indem er Modellparameter iterativ in die entgegengesetzte Richtung des Gradienten der Zielfunktion in Bezug auf diese Parameter aktualisiert: $\theta_{t+1} = \theta_t - \eta \nabla L(\theta_t)$. Wichtige Faktoren, die das Training beeinflussen, sind: 1. Lernrate ($\eta$): Steuert die Schrittgröße. Wenn sie zu klein gewählt wird, ist die Konvergenz extrem langsam und das Training kann stagnieren. Wenn sie zu groß gewählt wird, überschreiten die Aktualisierungen das Minimum, was zu Oszillation oder numerischer Divergenz führt. 2. Konvergenz: Wird durch die Überwachung von Abbruchkriterien wie einer kleinen Gradientennorm ($||\nabla L(\theta)|| \le \epsilon$), minimaler Parameterverschiebung oder einem Verlustplateau über aufeinanderfolgende Iterationen hinweg bestimmt. 3. Konvexität: Bei konvexen Zielen (z.B. Standard-OLS-Linearregression oder logistischer Regression) ist jedes lokale Minimum garantiert auch ein globales Minimum, was dem Gradientenabstieg ermöglicht, mit geeigneten Schrittgrößen zuverlässig zu konvergieren. Bei nicht-konvexen Zielen (z.B. mehrschichtigen neuronalen Netzen) enthält die Verlustlandschaft mehrere lokale Minima, Sattelpunkte und Plateaus, wodurch die endgültige Lösung empfindlich auf die Initialisierung reagiert. 4. Optimierung vs. Generalisierung: Die Konvergenz des Trainingsverlustes spiegelt den Optimierungserfolg wider, während der Validierungsverlust die Generalisierungsfähigkeit bewertet. Ein niedriger Trainingsverlust bei hohem Validierungsfehler deutet auf Overfitting hin und nicht auf einen Optimierungsfehler.

import numpy as np

def gradient_descent(X, y, lr=0.01, max_iters=1000, tol=1e-6):
    n_samples, n_features = X.shape
    theta = np.zeros(n_features)
    prev_loss = float('inf')
    
    for i in range(max_iters):
        predictions = X @ theta
        error = predictions - y
        loss = (1 / (2 * n_samples)) * np.dot(error, error)
        
        if abs(prev_loss - loss) < tol:
            print(f'Converged at iteration {i}')
            break
        prev_loss = loss
        
        grad = (1 / n_samples) * (X.T @ error)
        theta -= lr * grad
        
    return theta
Diese Frage mit einem KI-Coach beantworten

11Vergleichen Sie die L1-, L2- und ElasticNet-Regularisierung (regularization) hinsichtlich Sparsität (sparsity), korrelierter Merkmale (correlated features) und praktischer Modellselektion (practical model selection).

L1 (Lasso), L2 (Ridge) und ElasticNet-Regularisierung unterscheiden sich in der Penalisierungsformulierung, der Geometrie der Nebenbedingung, der Sparsität und dem Umgang mit korrelierten Prädiktoren: 1. **Sparsität & Geometrie:** - L1 verwendet einen Betragsstraf-Term ($\lambda \|w\|_1$). Die Begrenzung der Nebenbedingung ist ein Diamant/Polytope mit spitzen Eckpunkten auf den Koordinatenachsen. Wenn die Verlustkonturen diese Ecken schneiden, werden die Gewichte exakt auf Null gesetzt, was zu automatischer Merkmalsauswahl führt. - L2 verwendet eine quadrierte euklidische Norm-Strafe ($\lambda \|w\|_2^2$). Die Begrenzung der Nebenbedingung ist eine glatte Hypersphäre ohne Ecken, die die Gewichte asymptotisch gegen Null schrumpfen lässt, sie aber selten exakt auf Null setzt. 2. **Korrelierte Merkmale:** - Unter starker Kollinearität neigt L1 dazu, willkürlich ein Merkmal aus einer Gruppe korrelierter Prädiktoren auszuwählen und die restlichen Koeffizienten auf Null zu setzen, was zu instabilen Schätzungen über Resamples hinweg führt. - L2 behält alle korrelierten Merkmale bei, verteilt die Gewichte unter ihnen und schrumpft sie gemeinsam. 3. **ElasticNet:** - Kombiniert beide Straf-Terme: $\lambda_1 \|w\|_1 + \lambda_2 \|w\|_2^2$ (oft parametrisiert mit $\alpha$ und $l_1\_\text{ratio}$). Dies kann in einer kombinierten Formulierung als $\alpha \|w\|_1 + (1-\alpha) \|w\|_2^2$ ausgedrückt werden. - Bietet die Sparsität und Merkmalsauswahl von Lasso, wobei der Gruppierungseffekt von Ridge erhalten bleibt, indem Cluster korrelierter Prädiktoren gemeinsam ausgewählt werden. Es ist besonders nützlich, wenn $p > N$ ist oder bei starker Multikollinearität.

from sklearn.linear_model import Ridge, Lasso, ElasticNet
import numpy as np

np.random.seed(42)
X1 = np.random.randn(100, 1)
X2 = X1 + np.random.randn(100, 1) * 0.01  # highly correlated
X = np.hstack([X1, X2])
y = 3 * X1.ravel() + np.random.randn(100) * 0.1

ridge = Ridge(alpha=1.0).fit(X, y)
lasso = Lasso(alpha=0.1).fit(X, y)
elastic = ElasticNet(alpha=0.1, l1_ratio=0.5).fit(X, y)

print('Ridge coefs:', ridge.coef_)
print('Lasso coefs:', lasso.coef_)
print('ElasticNet coefs:', elastic.coef_)
Diese Frage mit einem KI-Coach beantworten

12Was ist auf hoher Ebene der Unterschied zwischen Ridge-Regression, Principal Component Regression (PCR) und Partial Least Squares (PLS)?

Ridge-Regression, Principal Component Regression (PCR) und Partial Least Squares (PLS) sind drei lineare Techniken, die zur Handhabung von Multikollinearität und hoher Dimensionalität eingesetzt werden. Sie unterscheiden sich jedoch darin, wie sie die Varianz reduzieren und ob die Reduktion kontinuierlich oder überwacht ist: 1. **Ridge-Regression:** Behält alle ursprünglichen $p$ Merkmale bei und wendet eine kontinuierliche Schrumpfung auf die Koeffizientenbeträge über einen L2-Strafterm an. Sie konstruiert keine niederdimensionalen latenten Komponenten oder verwirft Merkmalsdimensionen; vielmehr schrumpft sie die Varianz entlang der Richtungen mit niedrigen Eigenwerten von $X^T X$. 2. **Principal Component Regression (PCR):** Eine zweistufige, unüberwachte Dimensionsreduktionsmethode. Sie wendet zuerst die Hauptkomponentenanalyse (Principal Component Analysis, PCA) streng auf die Prädiktormatrix $X$ an, um orthogonale Richtungen maximaler Varianz zu finden, behält die Top $k$ Hauptkomponenten bei und passt eine OLS-Regression (Ordinary Least Squares) auf diese $k$ Komponenten an. Da PCA die Zielvariable $y$ ignoriert, besteht das Risiko, Komponenten zu verwerfen, die eine geringe Varianz in $X$ aber eine hohe Vorhersagekraft für $y$ haben. 3. **Partial Least Squares (PLS):** Eine überwachte Dimensionsreduktionsmethode. Sie konstruiert $k$ orthogonale latente Komponenten, indem sie lineare Kombinationen von $X$ findet, die die Kovarianz zwischen $X$ und der Antwort $y$ maximieren. Durch explizite Einbeziehung von Zielinformationen identifiziert PLS Komponenten, die sowohl die Merkmalsvarianz als auch die Antwortvariation erklären.

from sklearn.linear_model import Ridge, LinearRegression
from sklearn.decomposition import PCA
from sklearn.cross_decomposition import PLSRegression
from sklearn.pipeline import make_pipeline

# 1. Ridge: Regularized full feature space
ridge = Ridge(alpha=1.0)

# 2. PCR: Unsupervised PCA followed by OLS
pcr = make_pipeline(PCA(n_components=2), LinearRegression())

# 3. PLS: Supervised latent component projection and regression
pls = PLSRegression(n_components=2)
Diese Frage mit einem KI-Coach beantworten

Senior-Fragen

13Wie optimieren moderne Gradient-Boosting-Implementierungen wie XGBoost, LightGBM und CatBoost das Training oder handhaben tabellarische Merkmale anders?

Moderne GBDT (Gradient Boosting Decision Tree)-Frameworks unterscheiden sich erheblich in ihren Split-Findungs-Algorithmen, Baumwachstumsstrategien und der Handhabung tabellarischer/kategorialer Merkmale: 1. XGBoost: Verlässt sich traditionell auf exakte gierige oder approximative Quantil-Sketch-Split-Findung (und später Fast Hist), verwendet ebenenweises (tiefenweises) Baumwachstum und behandelt fehlende Werte, indem es eine optimale Standard-Zweigrichtung während der Split-Suche lernt. 2. LightGBM: Verwendet eine Histogramm-basierte Split-Findung (Bündelung kontinuierlicher Merkmale in diskrete Buckets, typischerweise 256), blattweises (best-first) Baumwachstum für eine schnellere Verlustreduktion, Gradient-based One-Side Sampling (GOSS), um Instanzen mit großem Gradienten zu behalten, während Instanzen mit kleinem Gradienten unterabgetastet werden, und Exclusive Feature Bundling (EFB), um sich gegenseitig ausschließende sparse Merkmale zusammenzuführen. Für kategoriale Variablen findet es optimale Splits durch Sortieren kategorialer Histogramm-Bins ($O(K \log K)$). 3. CatBoost: Verwendet oblivious (symmetrische) Entscheidungsbäume, bei denen alle Knoten auf einer gegebenen Tiefe genau denselben Split teilen, was ein schnelles vektorisiertes CPU (Central Processing Unit)/GPU (Graphics Processing Unit)-Scoring ermöglicht. Seine primäre Innovation sind Ordered Target Statistics und geordnetes Boosting, das Zielstatistiken über zufällige Permutationen von Trainingsdaten berechnet, um Target Leakage und Prediction Shift zu verhindern.

from catboost import CatBoostClassifier
import lightgbm as lgb
import pandas as pd

df = pd.DataFrame({
    'city': ['NY', 'LDN', 'NY', 'PAR', 'LDN', 'TOK'],
    'age': [25, 42, 30, 22, 55, 38],
    'target': [1, 0, 1, 0, 1, 0]
})
cat_cols = ['city']
df['city'] = df['city'].astype('category')

# LightGBM handles pandas 'category' dtype natively via integer binning
lgb_clf = lgb.LGBMClassifier(max_depth=3, n_estimators=10)
lgb_clf.fit(df[['city', 'age']], df['target'])

# CatBoost handles categorical column names natively with ordered TS
cb_clf = CatBoostClassifier(iterations=10, cat_features=cat_cols, verbose=False)
cb_clf.fit(df[['city', 'age']], df['target'])
Diese Frage mit einem KI-Coach beantworten

14Wie würden Sie entscheiden, ob eine benutzerdefinierte Verlustfunktion (custom loss) für ein Gradient Boosting Modell unter asymmetrischen Geschäftskosten geeignet ist?

Die Entscheidung, ob eine benutzerdefinierte Verlustfunktion in Gradient Boosting unter asymmetrischen Geschäftskosten implementiert werden sollte, erfordert die Bewertung, ob die Asymmetrie nachgelagert durch Wahrscheinlichkeitskalibrierung und Schwellenwertoptimierung gehandhabt werden kann, oder ob sie die Optimierungslandschaft während der Bauminduktion grundlegend verändert: 1. **Schwellenwertoptimierung vs. Benutzerdefinierte Verlustfunktion**: Für Klassifizierungsaufgaben mit asymmetrischen Fehlerkosten (z. B. wenn falsch-negative $10\times$ teurer sind als falsch-positive) ist die Standard-Kreuzentropie eine geeignete Bewertungsregel, die darauf abzielt, die Posteriori-Wahrscheinlichkeiten $P(y=1|x)$ zu schätzen. Eine Kalibrierung sollte jedoch überprüft und bei Bedarf auf Validierungsdaten korrigiert werden. Eine Verschiebung des Klassifizierungsentscheidungsschwellenwerts basierend auf der Geschäftskostenmatrix $\tau = \frac{C_{FP}}{C_{FP} + C_{FN}}$ oder die Anwendung von Stichprobengewichten ist oft sauberer und vermeidet benutzerdefinierte Ableitungen. Für asymmetrische Regression (z. B. asymmetrische Pinball-Loss-Funktion für Bestandsnachfrage) oder nicht-lineare Geschäftsstrafen, bei denen Standardziele die Split-Findung nicht leiten können, ist jedoch eine benutzerdefinierte Verlustfunktion gerechtfertigt. 2. **Mathematische Anforderungen für GBDT**: Bei Boostern zweiter Ordnung (XGBoost, LightGBM) benötigt eine benutzerdefinierte Verlustfunktion $L(y, \hat{y})$ normalerweise berechenbare Gradienten erster Ordnung ($g_i = \partial L / \partial \hat{y}_i$) und gültige Krümmungs-/Hessian-Werte zweiter Ordnung ($h_i = \partial^2 L / \partial \hat{y}_i^2$) für die Split-Gewinn- und Blattgewichts-Berechnungen ($w^* = -\sum g_i / (\sum h_i + \lambda)$). Hessian-Matrizen sollten nicht-negativ sein oder für numerische Stabilität sicher approximiert/abgeschnitten werden; einige Implementierungen unterstützen Ziele erster Ordnung oder approximative Ziele, sodass die Anforderung Framework-spezifisch ist. Nicht-differenzierbare oder diskontinuierliche Geschäftsmetriken sollten durch glatte Surrogate-Approximationen (z. B. Huberisierte oder Log-Cosh-Varianten) ersetzt werden.

import numpy as np
import xgboost as xgb

def asymmetric_mse_objective(preds, dtrain):
    labels = dtrain.get_label()
    residual = preds - labels
    # Penalize underestimation (residual < 0) 5x more heavily than overestimation
    penalty = np.where(residual < 0, 5.0, 1.0)
    grad = 2.0 * penalty * residual
    hess = 2.0 * penalty
    return grad, hess

# Usage:
# model = xgb.train(params, dtrain, obj=asymmetric_mse_objective)
Diese Frage mit einem KI-Coach beantworten

15Was ist LambdaMART, und wie adaptiert es Gradient Boosting für Ziele im Learning-to-Rank (LTR)?

LambdaMART ist ein Learning-to-Rank (LTR)-Algorithmus, der MART (Multiple Additive Regression Trees / Gradient Boosting) mit dem LambdaRank-Framework kombiniert. Beim Ranking hängen Zielmetriken wie NDCG (Normalized Discounted Cumulative Gain) und MAP (Mean Average Precision) von der diskreten Sortierreihenfolge (Rängen) ab, was sie fast überall flach und in Bezug auf kontinuierliche Modellbewertungen nicht differenzierbar macht. LambdaMART umgeht dies, indem es virtuelle Gradienten, sogenannte „Lambda-Gradienten“ ($\lambda_{ij}$), für Paare von Elementen $(i, j)$ innerhalb derselben Abfrage konstruiert. Der grundlegende paarweise Gradient stammt aus einer logistischen Verlustfunktion auf Score-Differenzen ($s_i - s_j$). LambdaMART skaliert diesen Gradienten mit der exakten Änderung in der Ziel-Ranking-Metrik ($|\Delta \text{NDCG}_{ij}|$), die auftreten würde, wenn die Positionen von Dokument $i$ und Dokument $j$ vertauscht würden: $$\lambda_{ij} = \frac{-\sigma}{1 + e^{\sigma(s_i - s_j)}} |\Delta \text{NDCG}_{ij}|$$ Für jedes einzelne Dokument $i$ wird der Gesamtgradient berechnet, indem paarweise Lambdas über alle Paare aggregiert werden, die Dokument $i$ betreffen: $\lambda_i = \sum_{j: j \succ i} \lambda_{ij} - \sum_{k: i \succ k} \lambda_{ki}$. Standard-Regressionsbäume im Boosting-Ensemble passen dann diese zusammengesetzten Lambda-Gradienten pro Dokument in jeder Boosting-Iteration an und optimieren so direkt listbasierte Ranking-Metriken.

import lightgbm as lgb
import numpy as np

# Simulated query-grouped data: 2 queries with 3 docs each
X = np.random.randn(6, 10)
y = np.array([3, 1, 0, 2, 0, 1])  # Relevance grades (0-3)
group = [3, 3]                     # Query group sizes

train_data = lgb.Dataset(X, label=y, group=group)
params = {
    'objective': 'lambdarank',
    'metric': 'ndcg',
    'ndcg_eval_at': [1, 3],
    'learning_rate': 0.1,
    'n_estimators': 50
}

ranker = lgb.train(params, train_data)
Diese Frage mit einem KI-Coach beantworten