Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Optuna integration skill for automated hyperparameter optimization with advanced search strategies, pruning, multi-objective optimization, and visualization capabilities.
.claude/skills/a5c-ai-optuna-hyperparameter-tuner/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-10 | ✗→✓ | ▲ Improved | 85% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 106% | 0% |
| case-03 | ✓→✗ | ▼ Worse | 182% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 136% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 144% | 0% |
Optimize hyperparameters using Optuna with advanced search strategies, pruning, and visualization.
This skill provides comprehensive capabilities for hyperparameter optimization using Optuna, the state-of-the-art hyperparameter optimization framework. It supports various samplers, pruners, multi-objective optimization, and integration with popular ML frameworks.
bashpip install optuna>=3.0.0
bash# Database backends pip install optuna[mysql] # MySQL support pip install optuna[postgresql] # PostgreSQL support # Visualization pip install optuna-dashboard # Web dashboard pip install plotly # Interactive plots # Framework integrations pip install optuna-integration[sklearn] pip install optuna-integration[pytorch] pip install optuna-integration[tensorflow]
pythonimport optuna def objective(trial): # Suggest hyperparameters learning_rate = trial.suggest_float('learning_rate', 1e-5, 1e-1, log=True) n_estimators = trial.suggest_int('n_estimators', 50, 500) max_depth = trial.suggest_int('max_depth', 3, 15) subsample = trial.suggest_float('subsample', 0.5, 1.0) # Train model model = XGBClassifier( learning_rate=learning_rate, n_estimators=n_estimators, max_depth=max_depth, subsample=subsample, random_state=42 ) # Cross-validation score = cross_val_score(model, X_train, y_train, cv=5, scoring='accuracy').mean() return score # Create study study = optuna.create_study( direction='maximize', study_name='xgboost-tuning', storage='sqlite:///optuna.db', load_if_exists=True ) # Optimize study.optimize(objective, n_trials=100, timeout=3600) # Best results print(f"Best trial: {study.best_trial.number}") print(f"Best value: {study.best_value:.4f}") print(f"Best params: {study.best_params}")
pythonimport optuna from optuna.pruners import MedianPruner def objective_with_pruning(trial): # Suggest hyperparameters learning_rate = trial.suggest_float('learning_rate', 1e-5, 1e-1, log=True) n_epochs = trial.suggest_int('n_epochs', 10, 100) # Create model model = create_model(learning_rate) # Training loop with pruning for epoch in range(n_epochs): train_loss = train_one_epoch(model) val_accuracy = evaluate(model) # Report intermediate value trial.report(val_accuracy, epoch) # Prune if unpromising if trial.should_prune(): raise optuna.TrialPruned() return val_accuracy # Create study with pruner study = optuna.create_study( direction='maximize', pruner=MedianPruner(n_startup_trials=5, n_warmup_steps=10) ) study.optimize(objective_with_pruning, n_trials=100)
pythonimport optuna def multi_objective(trial): # Hyperparameters learning_rate = trial.suggest_float('learning_rate', 1e-5, 1e-1, log=True) model_size = trial.suggest_categorical('model_size', ['small', 'medium', 'large']) # Train model model = create_model(learning_rate, model_size) train(model) # Multiple objectives accuracy = evaluate_accuracy(model) inference_time = measure_inference_time(model) return accuracy, inference_time # maximize accuracy, minimize time # Create multi-objective study study = optuna.create_study( directions=['maximize', 'minimize'], study_name='pareto-optimization' ) study.optimize(multi_objective, n_trials=100) # Get Pareto front pareto_front = study.best_trials for trial in pareto_front: print(f"Accuracy: {trial.values[0]:.4f}, Time: {trial.values[1]:.4f}")
pythonimport optuna from optuna.integration import OptunaSearchCV # Define parameter distributions param_distributions = { 'n_estimators': optuna.distributions.IntDistribution(50, 500), 'max_depth': optuna.distributions.IntDistribution(3, 15), 'learning_rate': optuna.distributions.FloatDistribution(1e-5, 1e-1, log=True), 'subsample': optuna.distributions.FloatDistribution(0.5, 1.0) } # Create search search = OptunaSearchCV( XGBClassifier(random_state=42), param_distributions, n_trials=100, cv=5, scoring='accuracy', study=study, # Optional: use existing study n_jobs=-1 ) # Fit search.fit(X_train, y_train) # Results print(f"Best score: {search.best_score_:.4f}") print(f"Best params: {search.best_params_}")
pythonimport optuna from optuna.integration import PyTorchLightningPruningCallback def objective(trial): # Hyperparameters lr = trial.suggest_float('lr', 1e-5, 1e-1, log=True) hidden_size = trial.suggest_int('hidden_size', 32, 256) dropout = trial.suggest_float('dropout', 0.1, 0.5) # Create model model = LightningModel( hidden_size=hidden_size, dropout=dropout, lr=lr ) # Create trainer with pruning callback trainer = pl.Trainer( max_epochs=100, callbacks=[ PyTorchLightningPruningCallback(trial, monitor='val_accuracy') ] ) trainer.fit(model, train_loader, val_loader) return trainer.callback_metrics['val_accuracy'].item()
pythonimport optuna # Create shared study with database storage study = optuna.create_study( study_name='distributed-study', storage='postgresql://user:pass@host:5432/optuna', direction='maximize', load_if_exists=True ) # Run on multiple workers (each worker runs this) study.optimize(objective, n_trials=25) # Each worker does 25 trials # Results are automatically aggregated print(f"Total trials: {len(study.trials)}")
javascriptconst hyperparameterTuningTask = defineTask({ name: 'optuna-hyperparameter-tuning', description: 'Optimize hyperparameters using Optuna', inputs: { studyName: { type: 'string', required: true }, direction: { type: 'string', default: 'maximize' }, nTrials: { type: 'number', default: 100 }, timeout: { type: 'number' }, parameterSpace: { type: 'object', required: true }, objectiveScript: { type: 'string', required: true }, sampler: { type: 'string', default: 'tpe' }, pruner: { type: 'string', default: 'median' } }, outputs: { bestValue: { type: 'number' }, bestParams: { type: 'object' }, nTrialsCompleted: { type: 'number' }, studyPath: { type: 'string' } }, async run(inputs, taskCtx) { return { kind: 'skill', title: `Optimize: ${inputs.studyName}`, skill: { name: 'optuna-hyperparameter-tuner', context: { operation: 'optimize', studyName: inputs.studyName, direction: inputs.direction, nTrials: inputs.nTrials, timeout: inputs.timeout, parameterSpace: inputs.parameterSpace, objectiveScript: inputs.objectiveScript, sampler: inputs.sampler, pruner: inputs.pruner } }, io: { inputJsonPath: `tasks/${taskCtx.effectId}/input.json`, outputJsonPath: `tasks/${taskCtx.effectId}/result.json` } }; } });
json{ "mcpServers": { "optuna": { "command": "uvx", "args": ["optuna-mcp"], "env": { "OPTUNA_STORAGE": "sqlite:///optuna.db" } } } }
optuna_create_study - Create new optimization studyoptuna_get_study - Retrieve study informationoptuna_list_studies - List all studiesoptuna_get_best_trial - Get best trial from studyoptuna_get_trials - List trials in studyoptuna_visualize - Generate visualizationoptuna_suggest_params - Get parameter suggestions| Sampler | Use Case | Pros | Cons | |---------|----------|------|------| | TPESampler | Default, most cases | Efficient, handles conditionals | May miss global optimum | | CmaEsSampler | Continuous parameters | Good for correlated params | Only continuous | | GridSampler | Small discrete spaces | Exhaustive | Exponential complexity | | RandomSampler | Baseline, parallel | Simple, embarrassingly parallel | Inefficient | | NSGAIISampler | Multi-objective | Pareto optimization | Slower convergence | | QMCSampler | Space exploration | Low discrepancy | Not adaptive |
| Pruner | Use Case | Aggressiveness | |--------|----------|----------------| | MedianPruner | Default, safe | Moderate | | HyperbandPruner | Deep learning | Aggressive | | SuccessiveHalvingPruner | Resource-efficient | High | | PercentilePruner | Configurable threshold | Variable | | NopPruner | No pruning needed | None |
pythonimport optuna.visualization as vis # Optimization history fig = vis.plot_optimization_history(study) fig.write_html('optimization_history.html') # Parameter importance fig = vis.plot_param_importances(study) fig.write_html('param_importance.html') # Parallel coordinate fig = vis.plot_parallel_coordinate(study) fig.write_html('parallel_coordinate.html') # Contour plot (2 params) fig = vis.plot_contour(study, params=['learning_rate', 'max_depth']) fig.write_html('contour.html') # Slice plot fig = vis.plot_slice(study) fig.write_html('slice.html')
bash# Launch dashboard optuna-dashboard sqlite:///optuna.db # Access at http://localhost:8080
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 61,930 | 21,068 | -66% | 1 | 1 | 0% | 1,326 | 8,365 | +531% | 0 | 0 | — |
case-02 | pass→pass | 12,073 | 12,793 | +6% | 1 | 1 | 0% | 2,581 | 6,098 | +136% | 0 | 0 | — |
case-03 | pass→fail | 8,283 | 7,739 | -7% | 1 | 1 | 0% | 1,820 | 5,127 | +182% | 0 | 0 | — |
case-04 | pass→pass | 8,480 | 4,568 | -46% | 1 | 1 | 0% | 1,721 | 4,205 | +144% | 0 | 0 | — |
case-05 | pass→pass | 10,668 | 9,380 | -12% | 1 | 1 | 0% | 2,200 | 5,048 | +129% | 0 | 0 | — |
case-06 | pass→pass | 14,873 | 12,744 | -14% | 1 | 1 | 0% | 2,814 | 5,771 | +105% | 0 | 0 | — |
case-07 | pass→pass | 11,927 | 9,081 | -24% | 1 | 1 | 0% | 2,106 | 4,570 | +117% | 0 | 0 | — |
case-08 | pass→pass | 10,752 | 11,494 | +7% | 1 | 1 | 0% | 1,986 | 4,849 | +144% | 0 | 0 | — |
case-09 | pass→pass | 3,222 | 3,498 | +9% | 1 | 1 | 0% | 634 | 3,892 | +514% | 0 | 0 | — |
case-10 | fail→pass | 13,285 | 6,786 | -49% | 1 | 1 | 0% | 2,424 | 4,475 | +85% | 0 | 0 | — |
case-11 | pass→pass | 6,941 | 3,709 | -47% | 1 | 1 | 0% | 1,100 | 3,978 | +262% | 0 | 0 | — |
case-12 | fail→pass | 11,615 | 3,476 | -70% | 1 | 1 | 0% | 1,895 | 3,909 | +106% | 0 | 0 | — |
case-13 | pass→pass | 3,988 | 5,208 | +31% | 1 | 1 | 0% | 785 | 4,194 | +434% | 0 | 0 | — |
case-14 | pass→pass | 2,540 | 2,560 | +1% | 1 | 1 | 0% | 402 | 3,691 | +818% | 0 | 0 | — |
case-15 | pass→pass | 3,551 | 3,283 | -8% | 1 | 1 | 0% | 712 | 3,903 | +448% | 0 | 0 | — |
case-16 | pass→pass | 3,519 | 3,095 | -12% | 1 | 1 | 0% | 641 | 3,866 | +503% | 0 | 0 | — |
case-17 | pass→pass | 2,247 | 2,634 | +17% | 1 | 1 | 0% | 429 | 3,741 | +772% | 0 | 0 | — |
case-18 | pass→pass | 3,753 | 2,460 | -34% | 1 | 1 | 0% | 729 | 3,639 | +399% | 0 | 0 | — |
case-19 | pass→pass | 6,021 | 4,644 | -23% | 1 | 1 | 0% | 1,115 | 4,114 | +269% | 0 | 0 | — |
case-20 | pass→pass | 14,347 | 12,205 | -15% | 1 | 1 | 0% | 3,186 | 5,882 | +85% | 0 | 0 | — |
case-21 | pass→pass | 6,409 | 6,530 | +2% | 1 | 1 | 0% | 1,384 | 4,717 | +241% | 0 | 0 | — |
case-22 | pass→pass | 11,342 | 9,925 | -12% | 1 | 1 | 0% | 2,344 | 5,161 | +120% | 0 | 0 | — |
case-23 | pass→pass | 9,029 | 8,472 | -6% | 1 | 1 | 0% | 2,059 | 5,144 | +150% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted, and 22 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +4 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.