From optuna-hpo
Hyperparameter optimisation for LLM fine-tuning using Optuna. Orchestrates distributed trials across cloud backends (Huggingface Jobs, Modal, etc.) with intelligent sampling, early stopping, and cost tracking. Use when users want to find optimal hyperparameters for training transformer models.
How this skill is triggered — by the user, by Claude, or both
Slash command
/optuna-hpo:optuna-hpoThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill orchestrates hyperparameter optimisation (HPO) for transformer-based LLM fine-tuning using Optuna. It dispatches individual training trials to cloud backends (Huggingface Jobs for now), tracks results, and provides visualisation through a Gradio dashboard.
references/backends.mdreferences/pruning_strategies.mdreferences/search_spaces.mdreferences/troubleshooting.mdscripts/backend_interface.pyscripts/gradio_dashboard.pyscripts/hf_jobs_runner.pyscripts/hpo_orchestrator.pyscripts/hub_sync.pyscripts/modal_runner.pyscripts/runpod_runner.pyscripts/search_spaces.pyThis skill orchestrates hyperparameter optimisation (HPO) for transformer-based LLM fine-tuning using Optuna. It dispatches individual training trials to cloud backends (Huggingface Jobs for now), tracks results, and provides visualisation through a Gradio dashboard.
Key capabilities:
Use this skill when users want to:
When assisting with HPO:
ALWAYS clarify before launching - Use AskUserQuestion to confirm model, dataset, training method, search space, and budget before dispatching any trials. GPU time costs money.
Validate datasets first - Use the dataset inspector from model-trainer to verify format compatibility before starting an HPO study.
Estimate costs upfront - Calculate approximate costs based on hardware, trial count, and expected duration. Get user approval.
Use sensible defaults - Template C (Core + LoRA + Scheduler) is the default search space. TPE + MedianPruner is the default sampler/pruner.
Track everything - Ensure all trials report metrics for pruning decisions and final analysis.
Before starting any HPO study, verify:
CRITICAL: Verify authentication before launching any trials.
from huggingface_hub import whoami
# Check authentication
try:
info = whoami()
print(f"✓ Authenticated as: {info['name']}")
except Exception as e:
print(f"✗ Not authenticated: {e}")
print("Run: huggingface-cli login")
Requirements:
huggingface-cli login or HF_TOKEN environment variableTo create a token with write access:
huggingface-cli login and paste the tokenThe orchestrator will verify authentication automatically on startup and provide clear error messages if credentials are missing or insufficient.
datasets.load_dataset()CRITICAL: Always clarify before launching trials.
When a user requests HPO, gather information using AskUserQuestion:
Question: "Which training method?"
Options:
- SFT (supervised fine-tuning)
- DPO (preference alignment) [v2]
- Other
Question: "Which search space scope?"
Options:
- Standard (learning_rate, batch_size, weight_decay)
- With LoRA params (+ lora_r, lora_alpha, lora_dropout)
- Comprehensive (+ scheduler, warmup) [recommended]
- Custom (I'll specify)
Question: "Trial budget?"
Options:
- Quick exploration (5 trials, ~$15)
- Moderate sweep (15 trials, ~$45)
- Thorough optimisation (30 trials, ~$90)
- Custom
After gathering inputs, display a summary for approval:
HPO study configuration
━━━━━━━━━━━━━━━━━━━━━━━━━
Model: Qwen/Qwen2.5-0.5B
Dataset: username/my-data
Method: SFT
Backend: HF Jobs (a10g-large)
Search space:
- learning_rate: 1e-6 to 1e-4 (log)
- batch_size: [4, 8, 16, 32]
- lora_r: [8, 16, 32, 64]
- weight_decay: 0.0 to 0.3
- warmup_ratio: 0.0 to 0.2
...
Trials: 15 (3 parallel)
Est. cost: $40-50
Est. time: ~2 hours
Proceed? [Yes / Modify / Cancel]
CRITICAL: Before starting trials, ask the user if they want to launch the Gradio dashboard for real-time monitoring.
Question: "Would you like to launch the Gradio dashboard for real-time monitoring?"
Options:
- Yes, launch dashboard (Recommended)
- No, CLI progress reports are sufficient
If yes, provide the launch command:
python scripts/gradio_dashboard.py --study ./optuna_studies/{study_name}.db --name {study_name}
The dashboard provides:
Suggest running this in a separate terminal so the user can monitor while trials execute.
If trials consistently fail, pause and ask:
Question: "3 trials have failed. How to proceed?"
Options:
- Show error logs and debug
- Adjust configuration
- Cancel study
If approaching budget limit:
Question: "Budget 80% consumed ($40/$50). Continue?"
Options:
- Continue (may exceed budget)
- Stop after current trials
- Cancel remaining trials
# /// script
# dependencies = ["optuna>=3.0.0", "huggingface_hub"]
# ///
from scripts.hpo_orchestrator import HPOStudy
from scripts.hf_jobs_runner import HFJobsRunner
study = HPOStudy(
name="qwen-sft-hpo",
model="Qwen/Qwen2.5-0.5B",
dataset="trl-lib/Capybara",
method="sft",
runner=HFJobsRunner(flavour="a10g-large"),
n_trials=10,
)
best = study.optimise()
print(f"Best trial: {best.hyperparameters}")
print(f"Best objective: {best.objective_value}")
study = HPOStudy(
name="qwen-sft-hpo",
model="Qwen/Qwen2.5-0.5B",
dataset="trl-lib/Capybara",
method="sft",
# Custom search space
search_space={
"learning_rate": ("float_log", 1e-6, 1e-4),
"lora_r": ("categorical", [8, 16, 32]),
"per_device_train_batch_size": ("categorical", [4, 8]),
},
# Execution
runner=HFJobsRunner(flavour="a10g-large"),
n_trials=20,
n_parallel=3,
# Persistence
storage="hub://username/my-hpo-study",
# Budget
budget_usd=100,
)
best = study.optimise()
# Launch dashboard
study.launch_dashboard()
Template A: Standard (fast, minimal)
SEARCH_SPACE_STANDARD = {
"learning_rate": ("float_log", 1e-6, 1e-4),
"per_device_train_batch_size": ("categorical", [4, 8, 16, 32]),
"weight_decay": ("float", 0.0, 0.3),
"num_train_epochs": ("categorical", [1, 2, 3]),
}
Template B: With LoRA (recommended for PEFT)
SEARCH_SPACE_LORA = {
**SEARCH_SPACE_STANDARD,
"lora_r": ("categorical", [8, 16, 32, 64]),
"lora_alpha": ("categorical", [16, 32, 64]),
"lora_dropout": ("float", 0.0, 0.1),
}
Template C: Comprehensive (default)
SEARCH_SPACE_COMPREHENSIVE = {
**SEARCH_SPACE_LORA,
"lr_scheduler_type": ("categorical", ["linear", "cosine", "cosine_with_restarts"]),
"warmup_ratio": ("float", 0.0, 0.2),
"gradient_accumulation_steps": ("categorical", [1, 2, 4, 8]),
}
Define arbitrary hyperparameters:
search_space = {
# Float with log scale
"learning_rate": ("float_log", 1e-6, 1e-4),
# Float with linear scale
"weight_decay": ("float", 0.0, 0.3),
# Categorical choices
"lora_r": ("categorical", [8, 16, 32]),
# Integer range
"num_train_epochs": ("int", 1, 5),
}
See references/search_spaces.md for complete documentation.
from scripts.hf_jobs_runner import HFJobsRunner
runner = HFJobsRunner(
flavour="a10g-large", # GPU type
timeout="2h", # Per-trial timeout
secrets={"HF_TOKEN": "$HF_TOKEN"},
)
Available flavours: t4-small, t4-medium, l4x1, a10g-small, a10g-large, a100-large
Implement the TrialRunner protocol:
from scripts.backend_interface import TrialRunner, JobStatus, TrialResult
class MyCustomRunner(TrialRunner):
def submit(self, script: str, config: dict) -> str:
# Submit job, return job_id
...
def status(self, job_id: str) -> JobStatus:
# Return current status
...
def results(self, job_id: str) -> TrialResult:
# Return trial results
...
def cancel(self, job_id: str) -> None:
# Cancel running job
...
See references/backends.md for adding Modal, RunPod, etc.
study = HPOStudy(
name="my-study",
storage="local", # SQLite in ./optuna_studies/
...
)
# Store directly on Hub
study = HPOStudy(
name="my-study",
storage="hub://username/my-hpo-study",
...
)
# Or sync after completion
study = HPOStudy(name="my-study", storage="local", ...)
study.optimise()
study.sync_to_hub("username/my-hpo-study")
For better organisation, store results and scripts in separate datasets:
study = HPOStudy(
name="qwen-sft-hpo",
model="Qwen/Qwen2.5-0.5B",
dataset="trl-lib/Capybara",
method="sft",
runner=HFJobsRunner(flavour="a10g-large"),
# Separate datasets
results_dataset="username/hpo-results", # CSV table with all trial results
scripts_dataset="username/hpo-scripts", # Training scripts for each trial
storage="hub://username/hpo-study-db", # Optuna database
n_trials=20,
)
# After optimisation, sync all
study.optimise()
study.sync_all() # Syncs study DB, results table, and scripts
Results dataset contains:
{study_name}_results.csv - Table with columns:
study_name, trial_id, job_idstatus, objective_value, duration_minutes, cost_usdREADME.md - Summary with best hyperparametersScripts dataset contains:
{study_name}/trial_{id}.py - Complete training script for each trialYou can also sync individually:
study.sync_results_table() # Just the CSV table
study.sync_scripts() # Just the scripts
study.sync_to_hub() # Just the Optuna database
Launch the visualisation dashboard:
study.launch_dashboard()
# Opens browser at http://localhost:7860
Or from command line:
python scripts/gradio_dashboard.py --study ./optuna_studies/my-study.db
python scripts/gradio_dashboard.py --hub username/my-hpo-study
Dashboard features:
study = HPOStudy(
sampler="tpe", # Tree-structured Parzen Estimator
pruner="median", # Prune below-median trials
...
)
import optuna
study = HPOStudy(
sampler=optuna.samplers.TPESampler(
n_startup_trials=5,
multivariate=True,
),
pruner=optuna.pruners.HyperbandPruner(
min_resource=1,
max_resource=10,
reduction_factor=3,
),
...
)
See references/pruning_strategies.md for guidance on when to use each strategy.
study = HPOStudy(
n_trials=20,
n_parallel=3, # Run up to 3 trials simultaneously
...
)
study = HPOStudy(
n_trials=50,
n_parallel=5,
budget_usd=100, # Hard budget limit
warn_at_percent=80, # Warn when 80% consumed
...
)
When budget is nearly exhausted, the skill will pause and ask the user how to proceed.
Trials have a configurable timeout to prevent runaway jobs:
study = HPOStudy(
trial_timeout_minutes=180, # 3 hours per trial (default)
poll_interval_seconds=30, # How often to check job status
...
)
If a trial exceeds the timeout, it will be cancelled and marked as pruned.
The orchestrator prints progress reports to the console every 5 minutes (configurable):
study = HPOStudy(
report_interval_seconds=300, # Report every 5 minutes (default)
...
)
Progress reports include:
For programmatic monitoring, provide a callback function:
def my_progress_handler(progress: dict):
# progress contains: trials_completed, trials_active, best_value, total_cost_usd, etc.
send_slack_notification(f"HPO progress: {progress['trials_completed']}/{n_trials} done")
study = HPOStudy(
progress_callback=my_progress_handler,
...
)
For interactive monitoring, launch the Gradio dashboard in a separate terminal:
python scripts/gradio_dashboard.py --study ./optuna_studies/my-study.db --name my-study
The dashboard auto-refreshes and provides visualisations of optimisation progress.
The architecture is extensible. See references/adding_methods.md to add new training methods.
scripts/hpo_orchestrator.py - Main orchestrator classscripts/hf_jobs_runner.py - HuggingFace Jobs backendscripts/backend_interface.py - Backend protocol definitionscripts/trial_script_template.py - Trial execution templatescripts/gradio_dashboard.py - Visualisation appscripts/hub_sync.py - Hub persistence utilitiesscripts/search_spaces.py - Search space templates"HuggingFace Hub authentication failed"
# Solution: Log in to HuggingFace
huggingface-cli login
"Token lacks write permissions"
huggingface-cli login"Could not verify HuggingFace authentication"
python -c "from huggingface_hub import whoami; print(whoami())"study.get_trial_logs(trial_id)n_startup_trials (default 10) to gather more data before pruningSuccessiveHalvingPruner for more conservative pruningpruner=Nonename is usedwarn_at_percent lower for earlier warningsbudget_usd as a hard limitSee references/troubleshooting.md for complete troubleshooting guide.
references/search_spaces.md - Search space templates and customisationreferences/backends.md - Backend configuration and custom runnersreferences/pruning_strategies.md - Optuna pruner selection guidereferences/troubleshooting.md - Common issues and solutionsAskUserQuestion to confirm configurationGuides collaborative design exploration before implementation: explores context, asks clarifying questions, proposes approaches, and writes a design doc for user approval.
Creates structured, bite-sized implementation plans from specs or requirements before writing code. Useful for breaking down multi-step tasks into testable steps with file structure and task boundaries.
Implements work from a spec or tickets using TDD at agreed seams, with regular typechecking and test runs, followed by code review.
npx claudepluginhub chrisvoncsefalvay/skills --plugin optuna-hpo