Model Hyperparameter Tuning
Overview
Hyperparameter tuning is the process of systematically searching for the best combination of model configuration parameters to maximize performance on validation data.
When to Use
- When optimizing model performance beyond baseline configurations
- When comparing different parameter combinations systematically
- When fine-tuning complex models with many hyperparameters
- When seeking the best trade-off between bias, variance, and training time
- When improving model generalization on validation and test data
- When exploring parameter spaces for neural networks, tree models, or ensemble methods
Tuning Methods
- Grid Search: Exhaustive search over parameter grid
- Random Search: Random sampling from parameter space
- Bayesian Optimization: Probabilistic model-based search
- Hyperband: Multi-fidelity optimization
- Evolutionary Algorithms: Genetic algorithm based search
- Population-based Training: Distributed parameter optimization
Hyperparameters by Model Type
- Tree Models: max_depth, min_samples_split, learning_rate
- Neural Networks: learning_rate, batch_size, num_layers, dropout
- SVM: C, kernel, gamma
- Ensemble: n_estimators, max_features, min_samples_leaf
Python Implementation
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
import optuna
from optuna.samplers import TPESampler
import torch
import torch.nn as nn
from torch.optim import Adam
import time
# Create dataset
X, y = make_classification(n_samples=2000, n_features=50, n_informative=30,
n_redundant=10, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
print("Dataset shapes:", X_train_scaled.shape, X_test_scaled.shape)
# 1. Grid Search
print("\n=== 1. Grid Search ===")
start = time.time()
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [5, 10, 15],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4]
}
grid_search = GridSearchCV(
RandomForestClassifier(random_state=42),
param_grid,
cv=5,
scoring='accuracy',
n_jobs=-1,
verbose=0
)
grid_search.fit(X_train_scaled, y_train)
grid_time = time.time() - start
print(f"Best parameters: {grid_search.best_params_}")
print(f"Best CV score: {grid_search.best_score_:.4f}")
print(f"Test score: {grid_search.score(X_test_scaled, y_test):.4f}")
print(f"Time taken: {grid_time:.2f}s")
# 2. Random Search
print("\n=== 2. Random Search ===")
start = time.time()
param_dist = {
'n_estimators': np.arange(50, 300, 10),
'max_depth': np.arange(5, 30, 1),
'min_samples_split': np.arange(2, 20, 1),
'min_samples_leaf': np.arange(1, 10, 1),
'max_features': ['sqrt', 'log2']
}
random_search = RandomizedSearchCV(
RandomForestClassifier(random_state=42),
param_dist,
n_iter=20,
cv=5,
scoring='accuracy',
n_jobs=-1,
random_state=42,
verbose=0
)
random_search.fit(X_train_scaled, y_train)
random_time = time.time() - start
print(f"Best parameters: {random_search.best_params_}")
print(f"Best CV score: {random_search.best_score_:.4f}")
print(f"Test score: {random_search.score(X_test_scaled, y_test):.4f}")
print(f"Time taken: {random_time:.2f}s")
# 3. Bayesian Optimization with Optuna
print("\n=== 3. Bayesian Optimization (Optuna) ===")
def objective(trial):
params = {
'n_estimators': trial.suggest_int('n_estimators', 50, 300),
'max_depth': trial.suggest_int('max_depth', 5, 30),
'min_samples_split': trial.suggest_int('min_samples_split', 2, 20),
'min_samples_leaf': trial.suggest_int('min_samples_leaf', 1, 10),
'max_features': trial.suggest_categorical('max_features', ['sqrt', 'log2'])
}
model = RandomForestClassifier(**params, random_state=42)
scores = cross_val_score(model, X_train_scaled, y_train, cv=5, scoring='accuracy')
return scores.mean()
start = time.time()
sampler = TPESampler(seed=42)
study = optuna.create_study(sampler=sampler, direction='maximize')
study.optimize(objective, n_trials=20, show_progress_bar=False)
optuna_time = time.time() - start
best_trial = study.best_trial
print(f"Best parameters: {best_trial.params}")
print(f"Best CV score: {best_trial.value:.4f}")
# Train final model with best params
best_model = RandomForestClassifier(**best_trial.params, random_state=42)
best_model.fit(X_train_scaled, y_train)
print(f"Test score: {best_model.score(X_test_scaled, y_test):.4f}")
print(f"Time taken: {optuna_time:.2f}s")
# 4. Gradient Boosting hyperparameter tuning
print("\n=== 4. Gradient Boosting Tuning ===")
gb_param_grid = {
'learning_rate': [0.01, 0.05, 0.1, 0.2],
'n_estimators': [100, 200, 300],
'max_depth': [3, 5, 7, 9],
'min_samples_split': [2, 5, 10],
'subsample': [0.8, 0.9, 1.0]
}
gb_search = GridSearchCV(
GradientBoostingClassifier(random_state=42),
gb_param_grid,
cv=5,
scoring='accuracy',
n_jobs=-1,
verbose=0
)
gb_search.fit(X_train_scaled, y_train)
print(f"Best parameters: {gb_search.best_params_}")
print(f"Best CV score: {gb_search.best_score_:.4f}")
print(f"Test score: {gb_search.score(X_test_scaled, y_test):.4f}")
# 5. Learning rate tuning for neural networks
print("\n=== 5. Learning Rate Tuning for Neural Networks ===")
class SimpleNN(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(50, 128)
self.fc2 = nn.Linear(128, 64)
self.fc3 = nn.Linear(64, 1)
self.relu = nn.ReLU()
self.dropout = nn.Dropout(0.3)
def forward(self, x):
x = self.relu(self.fc1(x))
x = self.dropout(x)
x = self.relu(self.fc2(x))
x = self.dropout(x)
x = torch.sigmoid(self.fc3(x))
return x
learning_rates = [0.0001, 0.001, 0.01, 0.1]
lr_results = {}
device = torch.device('cpu')
for lr in learning_rates:
model = SimpleNN().to(device)
optimizer = Adam(model.parameters(), lr=lr)
criterion = nn.BCELoss()
X_train_tensor = torch.FloatTensor(X_train_scaled)
y_train_tensor = torch.FloatTensor(y_train).unsqueeze(1)
best_loss = float('inf')
patience = 10
patience_counter = 0
for epoch in range(100):
output = model(X_train_tensor)
loss = criterion(output, y_train_tensor)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if loss.item() < best_loss:
best_loss = loss.item()
patience_counter = 0
else:
patience_counter += 1
if patience_counter >= patience:
break
lr_results[lr] = best_loss
print(f"Learning Rate {lr}: Best Loss = {best_loss:.6f}")
# 6. Comparison visualization
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# Search method comparison
methods = ['Grid Search', 'Random Search', 'Bayesian Opt']
times = [grid_time, random_time, optuna_time]
scores = [grid_search.best_score_, random_search.best_score_, study.best_value]
x = np.arange(len(methods))
axes[0, 0].bar(x, times, color='steelblue', alpha=0.7)
axes[0, 0].set_ylabel('Time (seconds)')
axes[0, 0].set_title('Tuning Method Comparison - Time')
axes[0, 0].set_xticks(x)
axes[0, 0].set_xticklabels(methods)
axes[0, 1].bar(x, scores, color='coral', alpha=0.7)
axes[0, 1].set_ylabel('CV Accuracy')
axes[0, 1].set_title('Tuning Method Comparison - Accuracy')
axes[0, 1].set_xticks(x)
axes[0, 1].set_xticklabels(methods)
axes[0, 1].set_ylim([0.8, 1.0])
# Hyperparameter importance from Optuna
importance_dict = {}
for param_name in study.best_trial.params.keys():
trial_values = []
for trial in study.trials:
if param_name in trial.params:
trial_values.append(trial.value)
if trial_values:
importance_dict[param_name] = np.std(trial_values)
axes[1, 0].barh(list(importance_dict.keys()), list(importance_dict.values()),
color='lightgreen', edgecolor='black')
axes[1, 0].set_xlabel('Importance (Std Dev)')
axes[1, 0].set_title('Hyperparameter Importance')
# Learning rate tuning for NN
axes[1, 1].plot(list(lr_results.keys()), list(lr_results.values()), marker='o',
linewidth=2, markersize=8, color='purple')
axes[1, 1].set_xlabel('Learning Rate')
axes[1, 1].set_ylabel('Best Training Loss')
axes[1, 1].set_title('Learning Rate Impact on Neural Network')
axes[1, 1].set_xscale('log')
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('hyperparameter_tuning.png', dpi=100, bbox_inches='tight')
print("\nVisualization saved as 'hyperparameter_tuning.png'")
print("\nHyperparameter tuning completed!")
Tuning Strategy by Model
- Tree Models: Focus on depth, min_samples, max_features
- Boosting: Learning_rate, n_estimators, subsample
- Neural Networks: Learning rate, batch size, regularization
- SVM: C and kernel type are most important
Best Practices
- Scale search space logarithmically for continuous parameters
- Use cross-validation for robust estimates
- Start with random search for initial exploration
- Use Bayesian optimization for final refinement
- Monitor for diminishing returns
Deliverables
- Optimal hyperparameters found
- Performance metrics for top configurations
- Tuning efficiency analysis
- Visualization of parameter impact
- Tuning report and recommendations
1---2name: model-hyperparameter-tuning3description: Optimize hyperparameters using grid search, random search, Bayesian optimization, and automated ML frameworks like Optuna and Hyperopt4---5
6# Model Hyperparameter Tuning
7
8## Overview
9
10Hyperparameter tuning is the process of systematically searching for the best combination of model configuration parameters to maximize performance on validation data.
11
12## When to Use
13
14- When optimizing model performance beyond baseline configurations
15- When comparing different parameter combinations systematically
16- When fine-tuning complex models with many hyperparameters
17- When seeking the best trade-off between bias, variance, and training time
18- When improving model generalization on validation and test data
19- When exploring parameter spaces for neural networks, tree models, or ensemble methods
20
21## Tuning Methods
22
23- **Grid Search**: Exhaustive search over parameter grid
24- **Random Search**: Random sampling from parameter space
25- **Bayesian Optimization**: Probabilistic model-based search
26- **Hyperband**: Multi-fidelity optimization
27- **Evolutionary Algorithms**: Genetic algorithm based search
28- **Population-based Training**: Distributed parameter optimization
29
30## Hyperparameters by Model Type
31
32- **Tree Models**: max_depth, min_samples_split, learning_rate
33- **Neural Networks**: learning_rate, batch_size, num_layers, dropout
34- **SVM**: C, kernel, gamma
35- **Ensemble**: n_estimators, max_features, min_samples_leaf
36
37## Python Implementation
38
39```python
40import numpy as np
41import pandas as pd
42import matplotlib.pyplot as plt
43from sklearn.datasets import make_classification
44from sklearn.model_selection import train_test_split, cross_val_score
45from sklearn.preprocessing import StandardScaler
46from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
47from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
48import optuna
49from optuna.samplers import TPESampler
50import torch
51import torch.nn as nn
52from torch.optim import Adam
53import time
54
55# Create dataset
56X, y = make_classification(n_samples=2000, n_features=50, n_informative=30,
57 n_redundant=10, random_state=42)
58X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
59
60scaler = StandardScaler()
61X_train_scaled = scaler.fit_transform(X_train)
62X_test_scaled = scaler.transform(X_test)
63
64print("Dataset shapes:", X_train_scaled.shape, X_test_scaled.shape)
65
66# 1. Grid Search
67print("\n=== 1. Grid Search ===")
68start = time.time()
69
70param_grid = {
71 'n_estimators': [50, 100, 200],
72 'max_depth': [5, 10, 15],
73 'min_samples_split': [2, 5, 10],
74 'min_samples_leaf': [1, 2, 4]
75}
76
77grid_search = GridSearchCV(
78 RandomForestClassifier(random_state=42),
79 param_grid,
80 cv=5,
81 scoring='accuracy',
82 n_jobs=-1,
83 verbose=0
84)
85
86grid_search.fit(X_train_scaled, y_train)
87grid_time = time.time() - start
88
89print(f"Best parameters: {grid_search.best_params_}")
90print(f"Best CV score: {grid_search.best_score_:.4f}")
91print(f"Test score: {grid_search.score(X_test_scaled, y_test):.4f}")
92print(f"Time taken: {grid_time:.2f}s")
93
94# 2. Random Search
95print("\n=== 2. Random Search ===")
96start = time.time()
97
98param_dist = {
99 'n_estimators': np.arange(50, 300, 10),
100 'max_depth': np.arange(5, 30, 1),
101 'min_samples_split': np.arange(2, 20, 1),
102 'min_samples_leaf': np.arange(1, 10, 1),
103 'max_features': ['sqrt', 'log2']
104}
105
106random_search = RandomizedSearchCV(
107 RandomForestClassifier(random_state=42),
108 param_dist,
109 n_iter=20,
110 cv=5,
111 scoring='accuracy',
112 n_jobs=-1,
113 random_state=42,
114 verbose=0
115)
116
117random_search.fit(X_train_scaled, y_train)
118random_time = time.time() - start
119
120print(f"Best parameters: {random_search.best_params_}")
121print(f"Best CV score: {random_search.best_score_:.4f}")
122print(f"Test score: {random_search.score(X_test_scaled, y_test):.4f}")
123print(f"Time taken: {random_time:.2f}s")
124
125# 3. Bayesian Optimization with Optuna
126print("\n=== 3. Bayesian Optimization (Optuna) ===")
127
128def objective(trial):
129 params = {
130 'n_estimators': trial.suggest_int('n_estimators', 50, 300),
131 'max_depth': trial.suggest_int('max_depth', 5, 30),
132 'min_samples_split': trial.suggest_int('min_samples_split', 2, 20),
133 'min_samples_leaf': trial.suggest_int('min_samples_leaf', 1, 10),
134 'max_features': trial.suggest_categorical('max_features', ['sqrt', 'log2'])
135 }
136
137 model = RandomForestClassifier(**params, random_state=42)
138 scores = cross_val_score(model, X_train_scaled, y_train, cv=5, scoring='accuracy')
139 return scores.mean()
140
141start = time.time()
142sampler = TPESampler(seed=42)
143study = optuna.create_study(sampler=sampler, direction='maximize')
144study.optimize(objective, n_trials=20, show_progress_bar=False)
145optuna_time = time.time() - start
146
147best_trial = study.best_trial
148print(f"Best parameters: {best_trial.params}")
149print(f"Best CV score: {best_trial.value:.4f}")
150
151# Train final model with best params
152best_model = RandomForestClassifier(**best_trial.params, random_state=42)
153best_model.fit(X_train_scaled, y_train)
154print(f"Test score: {best_model.score(X_test_scaled, y_test):.4f}")
155print(f"Time taken: {optuna_time:.2f}s")
156
157# 4. Gradient Boosting hyperparameter tuning
158print("\n=== 4. Gradient Boosting Tuning ===")
159
160gb_param_grid = {
161 'learning_rate': [0.01, 0.05, 0.1, 0.2],
162 'n_estimators': [100, 200, 300],
163 'max_depth': [3, 5, 7, 9],
164 'min_samples_split': [2, 5, 10],
165 'subsample': [0.8, 0.9, 1.0]
166}
167
168gb_search = GridSearchCV(
169 GradientBoostingClassifier(random_state=42),
170 gb_param_grid,
171 cv=5,
172 scoring='accuracy',
173 n_jobs=-1,
174 verbose=0
175)
176
177gb_search.fit(X_train_scaled, y_train)
178
179print(f"Best parameters: {gb_search.best_params_}")
180print(f"Best CV score: {gb_search.best_score_:.4f}")
181print(f"Test score: {gb_search.score(X_test_scaled, y_test):.4f}")
182
183# 5. Learning rate tuning for neural networks
184print("\n=== 5. Learning Rate Tuning for Neural Networks ===")
185
186class SimpleNN(nn.Module):
187 def __init__(self):
188 super().__init__()
189 self.fc1 = nn.Linear(50, 128)
190 self.fc2 = nn.Linear(128, 64)
191 self.fc3 = nn.Linear(64, 1)
192 self.relu = nn.ReLU()
193 self.dropout = nn.Dropout(0.3)
194
195 def forward(self, x):
196 x = self.relu(self.fc1(x))
197 x = self.dropout(x)
198 x = self.relu(self.fc2(x))
199 x = self.dropout(x)
200 x = torch.sigmoid(self.fc3(x))
201 return x
202
203learning_rates = [0.0001, 0.001, 0.01, 0.1]
204lr_results = {}
205
206device = torch.device('cpu')
207
208for lr in learning_rates:
209 model = SimpleNN().to(device)
210 optimizer = Adam(model.parameters(), lr=lr)
211 criterion = nn.BCELoss()
212
213 X_train_tensor = torch.FloatTensor(X_train_scaled)
214 y_train_tensor = torch.FloatTensor(y_train).unsqueeze(1)
215
216 best_loss = float('inf')
217 patience = 10
218 patience_counter = 0
219
220 for epoch in range(100):
221 output = model(X_train_tensor)
222 loss = criterion(output, y_train_tensor)
223
224 optimizer.zero_grad()
225 loss.backward()
226 optimizer.step()
227
228 if loss.item() < best_loss:
229 best_loss = loss.item()
230 patience_counter = 0
231 else:
232 patience_counter += 1
233
234 if patience_counter >= patience:
235 break
236
237 lr_results[lr] = best_loss
238 print(f"Learning Rate {lr}: Best Loss = {best_loss:.6f}")
239
240# 6. Comparison visualization
241fig, axes = plt.subplots(2, 2, figsize=(14, 10))
242
243# Search method comparison
244methods = ['Grid Search', 'Random Search', 'Bayesian Opt']
245times = [grid_time, random_time, optuna_time]
246scores = [grid_search.best_score_, random_search.best_score_, study.best_value]
247
248x = np.arange(len(methods))
249axes[0, 0].bar(x, times, color='steelblue', alpha=0.7)
250axes[0, 0].set_ylabel('Time (seconds)')
251axes[0, 0].set_title('Tuning Method Comparison - Time')
252axes[0, 0].set_xticks(x)
253axes[0, 0].set_xticklabels(methods)
254
255axes[0, 1].bar(x, scores, color='coral', alpha=0.7)
256axes[0, 1].set_ylabel('CV Accuracy')
257axes[0, 1].set_title('Tuning Method Comparison - Accuracy')
258axes[0, 1].set_xticks(x)
259axes[0, 1].set_xticklabels(methods)
260axes[0, 1].set_ylim([0.8, 1.0])
261
262# Hyperparameter importance from Optuna
263importance_dict = {}
264for param_name in study.best_trial.params.keys():
265 trial_values = []
266 for trial in study.trials:
267 if param_name in trial.params:
268 trial_values.append(trial.value)
269 if trial_values:
270 importance_dict[param_name] = np.std(trial_values)
271
272axes[1, 0].barh(list(importance_dict.keys()), list(importance_dict.values()),
273 color='lightgreen', edgecolor='black')
274axes[1, 0].set_xlabel('Importance (Std Dev)')
275axes[1, 0].set_title('Hyperparameter Importance')
276
277# Learning rate tuning for NN
278axes[1, 1].plot(list(lr_results.keys()), list(lr_results.values()), marker='o',
279 linewidth=2, markersize=8, color='purple')
280axes[1, 1].set_xlabel('Learning Rate')
281axes[1, 1].set_ylabel('Best Training Loss')
282axes[1, 1].set_title('Learning Rate Impact on Neural Network')
283axes[1, 1].set_xscale('log')
284axes[1, 1].grid(True, alpha=0.3)
285
286plt.tight_layout()
287plt.savefig('hyperparameter_tuning.png', dpi=100, bbox_inches='tight')
288print("\nVisualization saved as 'hyperparameter_tuning.png'")
289
290print("\nHyperparameter tuning completed!")
291```
292
293## Tuning Strategy by Model
294
295- **Tree Models**: Focus on depth, min_samples, max_features
296- **Boosting**: Learning_rate, n_estimators, subsample
297- **Neural Networks**: Learning rate, batch size, regularization
298- **SVM**: C and kernel type are most important
299
300## Best Practices
301
302- Scale search space logarithmically for continuous parameters
303- Use cross-validation for robust estimates
304- Start with random search for initial exploration
305- Use Bayesian optimization for final refinement
306- Monitor for diminishing returns
307
308## Deliverables
309
310- Optimal hyperparameters found
311- Performance metrics for top configurations
312- Tuning efficiency analysis
313- Visualization of parameter impact
314- Tuning report and recommendations