forked from hyperactive-project/Hyperactive
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnsga_ii_sampler_example.py
More file actions
198 lines (161 loc) · 6.69 KB
/
Copy pathnsga_ii_sampler_example.py
File metadata and controls
198 lines (161 loc) · 6.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
"""
NSGAIIOptimizer Example - Multi-objective Optimization with NSGA-II
NSGA-II (Non-dominated Sorting Genetic Algorithm II) is designed for
multi-objective optimization problems where you want to optimize multiple
conflicting objectives simultaneously. It finds a Pareto front of solutions.
Characteristics:
- Multi-objective evolutionary algorithm
- Finds Pareto-optimal solutions (non-dominated set)
- Balances multiple conflicting objectives
- Population-based search with selection pressure
- Elitist approach preserving best solutions
- Crowding distance for diversity preservation
Note: For demonstration, we'll create a multi-objective problem from
a single-objective one by optimizing both performance and model complexity.
"""
import numpy as np
from sklearn.datasets import load_digits
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
from hyperactive.experiment.integrations import SklearnCvExperiment
from hyperactive.opt.optuna import NSGAIIOptimizer
class MultiObjectiveExperiment:
"""Multi-objective experiment: maximize accuracy, minimize complexity."""
def __init__(self, X, y):
self.X = X
self.y = y
def __call__(self, params):
# Create model with parameters
model = RandomForestClassifier(random_state=42, **params)
# Objective 1: Maximize accuracy (we'll return negative for minimization)
scores = cross_val_score(model, self.X, self.y, cv=3)
accuracy = np.mean(scores)
# Objective 2: Minimize model complexity (number of parameters)
# For Random Forest: roughly n_estimators × max_depth × n_features
complexity = (
params["n_estimators"] * params.get("max_depth", 10) * self.X.shape[1]
)
# NSGA-II minimizes objectives, so we return both as minimization
# Note: This is a simplified multi-objective setup for demonstration
return [-accuracy, complexity / 10000] # Scale complexity for better balance
# NSGA-II Algorithm (Multi-objective Optimization):
#
# 1. Core Concepts:
# - Pareto Dominance: Solution A dominates B if A is better in all objectives
# - Pareto Front: Set of non-dominated solutions
# - Trade-offs: Improving one objective may worsen another
#
# 2. NSGA-II Process:
# - Initialize population randomly
# - For each generation:
# a) Fast non-dominated sorting (rank solutions by dominance)
# b) Crowding distance calculation (preserve diversity)
# c) Selection based on rank and crowding distance
# d) Crossover and mutation to create offspring
#
# 3. Selection Criteria:
# - Primary: Non-domination rank (prefer better fronts)
# - Secondary: Crowding distance (prefer diverse solutions)
# - Elitist: Best solutions always survive
#
# 4. Output:
# - Set of Pareto-optimal solutions
# - User chooses final solution based on preferences
# === NSGAIIOptimizer Example ===
# Multi-objective Optimization with NSGA-II
# Load dataset
X, y = load_digits(return_X_y=True)
print(f"Dataset: Handwritten digits ({X.shape[0]} samples, {X.shape[1]} features)")
# Create multi-objective experiment
experiment = MultiObjectiveExperiment(X, y)
# Multi-objective Problem:
# Objective 1: Maximize classification accuracy
# Objective 2: Minimize model complexity
# → Trade-off between performance and simplicity
# Define search space
param_space = {
"n_estimators": (10, 200), # Number of trees
"max_depth": (1, 20), # Tree depth (complexity)
"min_samples_split": (2, 20), # Minimum samples to split
"min_samples_leaf": (1, 10), # Minimum samples per leaf
"max_features": ["sqrt", "log2", None], # Feature sampling
}
# Search Space:
# for param, space in param_space.items():
# print(f" {param}: {space}")
# Configure NSGAIIOptimizer
optimizer = NSGAIIOptimizer(
param_space=param_space,
n_trials=50, # Population evolves over multiple generations
random_state=42,
experiment=experiment,
population_size=20, # Population size for genetic algorithm
mutation_prob=0.1, # Mutation probability
crossover_prob=0.9, # Crossover probability
)
# NSGAIIOptimizer Configuration:
# n_trials: configured above
# population_size: for genetic algorithm
# mutation_prob: mutation probability
# crossover_prob: crossover probability
# Selection: Non-dominated sorting + crowding distance
# Note: This example demonstrates the interface.
# In practice, NSGA-II returns multiple Pareto-optimal solutions.
# For single-objective problems, consider TPE or GP samplers instead.
# Run optimization
# Running NSGA-II multi-objective optimization...
try:
best_params = optimizer.solve()
# Results
print("\n=== Results ===")
print(f"Best parameters: {best_params}")
print(f"Best score: {optimizer.best_score_:.4f}")
print()
# NSGA-II typically returns multiple solutions along Pareto front:
# High accuracy, high complexity models
# Medium accuracy, medium complexity models
# Lower accuracy, low complexity models
# User selects based on preferences/constraints
except Exception as e:
print(f"Multi-objective optimization example: {e}")
print("Note: This demonstrates the interface for multi-objective problems.")
# NSGA-II Evolution Process:
#
# Generation 1: Random initialization
# Diverse population across parameter space
# Wide range of accuracy/complexity trade-offs
# Generations 2-N: Evolutionary improvement
# Non-dominated sorting identifies best fronts
# Crowding distance maintains solution diversity
# Crossover combines good solutions
# Mutation explores new parameter regions
# Final Population: Pareto front approximation
# Multiple non-dominated solutions
# Represents optimal trade-offs
# User chooses based on domain requirements
# Key Advantages:
# Handles multiple conflicting objectives naturally
# Finds diverse set of optimal trade-offs
# No need to specify objective weights a priori
# Provides insight into objective relationships
# Robust to objective scaling differences
# Best Use Cases:
# True multi-objective problems (accuracy vs speed, cost vs quality)
# When trade-offs between objectives are important
# Robustness analysis with multiple criteria
# When single objective formulation is unclear
# Limitations:
# More complex than single-objective methods
# Requires more evaluations (population-based)
# May be overkill for single-objective problems
# Final solution selection still required
# When to Use NSGA-II vs Single-objective Methods:
# Use NSGA-II when:
# Multiple objectives genuinely conflict
# Trade-off analysis is valuable
# Objective weights are unknown
#
# Use TPE/GP when:
# Single clear objective
# Computational budget is limited
# Faster convergence needed