Machine learning / Regularization overfitting and optimization
Balancing Model Fit and Generalization Bias-Variance Tradeoff
Contents
- The Bias-Variance Tradeoff
- Understanding Bias
- Understanding Variance
- The Tradeoff
- Underfitting
- Overfitting
- Finding the Right Balance
- Regularization Techniques
- Real-Life Example: Image Classification
- Real-Life Example: Weather Prediction
- Techniques to Mitigate Overfitting
- Model Complexity and Dataset Size
- Practical Tips for Model Selection
- Additional Resources
The Bias-Variance Tradeoff
The bias-variance tradeoff is a fundamental concept in machine learning that describes the balance between a model's ability to fit training data and its capacity to generalize to new, unseen data. This tradeoff is crucial for understanding model performance and avoiding overfitting.
import numpy as np
import matplotlib.pyplot as plt
# Generate sample data
np.random.seed(42)
X = np.linspace(0, 10, 100)
y = 2 * X + 1 + np.random.normal(0, 2, 100)
# Plot the data
plt.scatter(X, y, alpha=0.5)
plt.title("Sample Data with Linear Relationship")
plt.xlabel("X")
plt.ylabel("y")
plt.show()
Understanding Bias
Bias refers to the error introduced by approximating a real-world problem with a simplified model. High bias can lead to underfitting, where the model fails to capture the underlying patterns in the data.
# Fit a linear model (low complexity)
from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X.reshape(-1, 1), y)
# Plot the data and the model
plt.scatter(X, y, alpha=0.5)
plt.plot(X, model.predict(X.reshape(-1, 1)), color='red', label='Linear Model')
plt.title("Linear Model (Low Complexity)")
plt.xlabel("X")
plt.ylabel("y")
plt.legend()
plt.show()
Understanding Variance
Variance refers to the model's sensitivity to small fluctuations in the training data. High variance can lead to overfitting, where the model captures noise in the training data and fails to generalize well to new data.
# Fit a high-degree polynomial model (high complexity)
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
model = make_pipeline(PolynomialFeatures(degree=15), LinearRegression())
model.fit(X.reshape(-1, 1), y)
# Plot the data and the model
plt.scatter(X, y, alpha=0.5)
plt.plot(X, model.predict(X.reshape(-1, 1)), color='red', label='Polynomial Model')
plt.title("Polynomial Model (High Complexity)")
plt.xlabel("X")
plt.ylabel("y")
plt.legend()
plt.show()
The Tradeoff
The bias-variance tradeoff involves finding the right balance between model complexity and generalization ability. As model complexity increases, bias tends to decrease, but variance increases.
# Function to calculate mean squared error
def mse(y_true, y_pred):
return np.mean((y_true - y_pred) ** 2)
# Generate test data
X_test = np.linspace(0, 10, 100)
y_test = 2 * X_test + 1 + np.random.normal(0, 2, 100)
# Calculate MSE for different polynomial degrees
degrees = range(1, 16)
train_mse = []
test_mse = []
for degree in degrees:
model = make_pipeline(PolynomialFeatures(degree), LinearRegression())
model.fit(X.reshape(-1, 1), y)
train_mse.append(mse(y, model.predict(X.reshape(-1, 1))))
test_mse.append(mse(y_test, model.predict(X_test.reshape(-1, 1))))
# Plot the results
plt.plot(degrees, train_mse, label='Training MSE')
plt.plot(degrees, test_mse, label='Test MSE')
plt.title("Bias-Variance Tradeoff")
plt.xlabel("Model Complexity (Polynomial Degree)")
plt.ylabel("Mean Squared Error")
plt.legend()
plt.show()
Underfitting
Underfitting occurs when a model is too simple to capture the underlying patterns in the data. This results in high bias and poor performance on both training and test data.
# Underfit model (linear)
underfit_model = LinearRegression()
underfit_model.fit(X.reshape(-1, 1), y)
# Plot the data and the model
plt.scatter(X, y, alpha=0.5)
plt.plot(X, underfit_model.predict(X.reshape(-1, 1)), color='red', label='Underfit Model')
plt.title("Underfitting Example")
plt.xlabel("X")
plt.ylabel("y")
plt.legend()
plt.show()
print(f"Training MSE: {mse(y, underfit_model.predict(X.reshape(-1, 1))):.2f}")
print(f"Test MSE: {mse(y_test, underfit_model.predict(X_test.reshape(-1, 1))):.2f}")
Overfitting
Overfitting occurs when a model is too complex and captures noise in the training data. This results in high variance and poor generalization to new data.
# Overfit model (high-degree polynomial)
overfit_model = make_pipeline(PolynomialFeatures(degree=15), LinearRegression())
overfit_model.fit(X.reshape(-1, 1), y)
# Plot the data and the model
plt.scatter(X, y, alpha=0.5)
plt.plot(X, overfit_model.predict(X.reshape(-1, 1)), color='red', label='Overfit Model')
plt.title("Overfitting Example")
plt.xlabel("X")
plt.ylabel("y")
plt.legend()
plt.show()
print(f"Training MSE: {mse(y, overfit_model.predict(X.reshape(-1, 1))):.2f}")
print(f"Test MSE: {mse(y_test, overfit_model.predict(X_test.reshape(-1, 1))):.2f}")
Finding the Right Balance
The goal is to find a model with the right complexity that minimizes both bias and variance. This typically involves using techniques like cross-validation to evaluate model performance.
from sklearn.model_selection import cross_val_score
# Function to evaluate model performance
def evaluate_model(degree):
model = make_pipeline(PolynomialFeatures(degree), LinearRegression())
scores = cross_val_score(model, X.reshape(-1, 1), y, cv=5, scoring='neg_mean_squared_error')
return -scores.mean()
# Evaluate models with different complexities
degrees = range(1, 16)
cv_scores = [evaluate_model(degree) for degree in degrees]
# Plot the results
plt.plot(degrees, cv_scores)
plt.title("Cross-Validation Scores")
plt.xlabel("Model Complexity (Polynomial Degree)")
plt.ylabel("Mean Squared Error")
plt.show()
best_degree = degrees[np.argmin(cv_scores)]
print(f"Best polynomial degree: {best_degree}")
Regularization Techniques
Regularization is a common approach to address overfitting by adding a penalty term to the loss function. This encourages simpler models and helps prevent overfitting.
from sklearn.linear_model import Ridge, Lasso
# Ridge regression (L2 regularization)
ridge_model = Ridge(alpha=1.0)
ridge_model.fit(X.reshape(-1, 1), y)
# Lasso regression (L1 regularization)
lasso_model = Lasso(alpha=1.0)
lasso_model.fit(X.reshape(-1, 1), y)
# Plot the results
plt.scatter(X, y, alpha=0.5)
plt.plot(X, ridge_model.predict(X.reshape(-1, 1)), color='red', label='Ridge')
plt.plot(X, lasso_model.predict(X.reshape(-1, 1)), color='green', label='Lasso')
plt.title("Regularized Models")
plt.xlabel("X")
plt.ylabel("y")
plt.legend()
plt.show()
Real-Life Example: Image Classification
In image classification tasks, the bias-variance tradeoff is crucial for achieving good performance. Let's consider a simple example using the MNIST dataset.
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
# Load the digits dataset
digits = load_digits()
X, y = digits.data, digits.target
# Split the data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Train models with different complexities
kernels = ['linear', 'poly', 'rbf']
accuracies = []
for kernel in kernels:
model = SVC(kernel=kernel)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
accuracies.append(accuracy_score(y_test, y_pred))
# Plot the results
plt.bar(kernels, accuracies)
plt.title("SVM Performance on MNIST")
plt.xlabel("Kernel")
plt.ylabel("Accuracy")
plt.show()
Real-Life Example: Weather Prediction
Weather prediction models face the challenge of balancing complexity to capture complex atmospheric patterns while avoiding overfitting to historical data.
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error
# Generate synthetic weather data
np.random.seed(42)
dates = pd.date_range(start='2020-01-01', end='2022-12-31')
temps = 20 + 10 * np.sin(np.arange(len(dates)) * 2 * np.pi / 365) + np.random.normal(0, 3, len(dates))
data = pd.DataFrame({'date': dates, 'temperature': temps})
# Create features
data['day_of_year'] = data['date'].dt.dayofyear
X = data[['day_of_year']]
y = data['temperature']
# Split the data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Train models with different complexities
n_estimators_list = [1, 10, 100, 1000]
train_mse = []
test_mse = []
for n_estimators in n_estimators_list:
model = RandomForestRegressor(n_estimators=n_estimators, random_state=42)
model.fit(X_train, y_train)
train_mse.append(mean_squared_error(y_train, model.predict(X_train)))
test_mse.append(mean_squared_error(y_test, model.predict(X_test)))
# Plot the results
plt.plot(n_estimators_list, train_mse, label='Training MSE')
plt.plot(n_estimators_list, test_mse, label='Test MSE')
plt.title("Weather Prediction Model Complexity")
plt.xlabel("Number of Trees in Random Forest")
plt.ylabel("Mean Squared Error")
plt.xscale('log')
plt.legend()
plt.show()
Techniques to Mitigate Overfitting
There are several techniques to address overfitting and improve model generalization:
- Cross-validation
- Regularization (L1, L2)
- Early stopping
- Ensemble methods
- Data augmentation
from sklearn.model_selection import learning_curve
# Generate learning curves
train_sizes, train_scores, test_scores = learning_curve(
RandomForestRegressor(n_estimators=100, random_state=42),
X, y, cv=5, scoring='neg_mean_squared_error', train_sizes=np.linspace(0.1, 1.0, 10)
)
# Plot learning curves
plt.plot(train_sizes, -train_scores.mean(axis=1), label='Training MSE')
plt.plot(train_sizes, -test_scores.mean(axis=1), label='Cross-validation MSE')
plt.title("Learning Curves")
plt.xlabel("Training Set Size")
plt.ylabel("Mean Squared Error")
plt.legend()
plt.show()
Model Complexity and Dataset Size
The relationship between model complexity and dataset size is crucial. As the dataset size increases, more complex models can be used without overfitting.
# Generate datasets of different sizes
sizes = [100, 1000, 10000]
degrees = range(1, 16)
test_mse_list = []
for size in sizes:
X = np.linspace(0, 10, size)
y = 2 * X + 1 + np.random.normal(0, 2, size)
X_test = np.linspace(0, 10, 1000)
y_test = 2 * X_test + 1 + np.random.normal(0, 2, 1000)
mse_list = []
for degree in degrees:
model = make_pipeline(PolynomialFeatures(degree), LinearRegression())
model.fit(X.reshape(-1, 1), y)
mse_list.append(mse(y_test, model.predict(X_test.reshape(-1, 1))))
test_mse_list.append(mse_list)
# Plot the results
for i, size in enumerate(sizes):
plt.plot(degrees, test_mse_list[i], label=f'n={size}')
plt.title("Model Complexity vs Dataset Size")
plt.xlabel("Polynomial Degree")
plt.ylabel("Test MSE")
plt.legend()
plt.show()
Practical Tips for Model Selection
When selecting a model, consider the following:
- Start with simple models and gradually increase complexity
- Use cross-validation to estimate model performance
- Monitor both training and validation errors
- Consider the interpretability-performance tradeoff
- Use domain knowledge to guide feature engineering and model selection
from sklearn.model_selection import GridSearchCV
# Define parameter grid
param_grid = {
'n_estimators': [10, 50, 100, 200],
'max_depth': [None, 5, 10, 20],
'min_samples_split': [2, 5, 10]
}
# Perform grid search
grid_search = GridSearchCV(RandomForestRegressor(random_state=42), param_grid, cv=5, scoring='neg_mean_squared_error')
grid_search.fit(X, y)
# Print best parameters and score
print("Best parameters:", grid_search.best_params_)
print("Best MSE:", -grid_search.best_score_)
# Plot feature importances
importances = grid_search.best_estimator_.feature_importances_
plt.bar(range(len(importances)), importances)
plt.title("Feature Importances")
plt.xlabel("Feature Index")
plt.ylabel("Importance")
plt.show()
Additional Resources
For further exploration of the bias-variance tradeoff and model selection, consider the following resources:
- "Understanding the Bias-Variance Tradeoff" by Scott Fortmann-Roe (http://scott.fortmann-roe.com/docs/BiasVariance.html)
- "The Elements of Statistical Learning" by Hastie, Tibshirani, and Friedman (https://web.stanford.edu/~hastie/ElemStatLearn/)
- "Machine Learning Yearning" by Andrew Ng (https://www.deeplearning.ai/machine-learning-yearning/)
- "Bias-Variance Tradeoff in Machine Learning" on ArXiv: (https://arxiv.org/abs/2012.14027)