UI はローカライズ済みです。スキルの紹介と説明文書は選択した言語へ AI が自動翻訳し、コード・コマンド・パス・専門用語は原文のままです。原作者の文書が優先されます。
スキルを探す
開発・技術 · SKILL.md

ml-hyperparameter-tuning

ml-hyperparameter-tuning

Use this skill when performing hyperparameter tuning, optimizing model performance via search strategies, or configuring tuning frameworks (Optuna, Ray Tune, Hyperopt). This skill enforces: search space definition, strategy selection (grid/random/Bayesian), framework configuration, pruning/early stopping, distributed execution, multi-objective optimization.…

Independent GitHub snapshot · Automated classification · Original documentation governs

スキルの説明

GitHub

name: ml-hyperparameter-tuning description: > Use this skill when performing hyperparameter tuning, optimizing model performance via search strategies, or configuring tuning frameworks (Optuna, Ray Tune, Hyperopt). This skill enforces: search space definition, strategy selection (grid/random/Bayesian), framework configuration, pruning/early stopping, distributed execution, multi-objective optimization. Do NOT use for: model architecture search (NAS), feature selection, threshold tuning for classification, experiment tracking (use ml-experiment-tracking). version: "2.0.0" author: "j4flmao" license: "MIT" compatibility: claude-code: true cursor: true codex: true windsurf: true tags: [ml, hyperparameter, optimization, phase-11]

ML Hyperparameter Tuning

Purpose

Design hyperparameter tuning pipelines with structured search spaces, optimal strategy selection, and production-ready framework configuration.

Architecture/Decision Trees

Strategy Selection Decision Tree

How long does each trial take?
  ├── <10 seconds
  │   ├── <4 hyperparameters → Grid Search (exhaustive)
  │   └── >4 hyperparameters → Random Search (better coverage)
  ├── 10 seconds - 1 minute
  │   └── Bayesian Optimization (TPE or GP)
  ├── 1 minute - 1 hour
  │   ├── Bayesian + ASHA Pruning (aggressive early stopping)
  │   └── Hyperband (adaptive resource allocation)
  └── >1 hour
      ├── Population-Based Training (evolve HP + weights)
      └── Bayesian with aggressive pruning + checkpoint reuse

Search Space Scale Decision Tree

How many trials can you afford?
  ├── <20 trials → Manual tuning or grid on 2-3 key params
  ├── 20-100 trials → Random Search or Bayesian (TPE)
  ├── 100-1000 trials → Bayesian (GP/SMAC) + Median Pruner
  ├── 1000-10000 trials → Hyperband + Distributed (Ray)
  └── >10000 trials → Population-Based Training

Parameter Scale Decision Tree

What type of hyperparameter?
  ├── Learning rate, regularization, weight decay
  │   └── Log-uniform scale (trial.suggest_float(..., log=True))
  ├── Tree depth, number of layers, batch size, hidden units
  │   └── Integer uniform (trial.suggest_int)
  ├── Optimizer, activation, pooling type
  │   └── Categorical (trial.suggest_categorical)
  └── Conditional (momentum only when optimizer==sgd)
      └── if/else branching in objective function

Pruning Strategy Decision Tree

├── <10 trials budget → No pruning (too few to prune reliably)
├── 10-100 trials → MedianPruner (n_warmup_steps=10)
├── 100-1000 trials → ASHA (async, distributed-friendly)
└── >1000 trials → Hyperband (multi-bracket resource allocation)

Agent Protocol

Trigger

User request includes: hyperparameter tuning, Optuna, Ray Tune, Hyperopt, grid search, random search, Bayesian optimization, pruning, early stopping.

Input Context

Before activating, verify:

  • Model type and training pipeline.
  • Approximate training time per trial.
  • Available compute resources (single machine, multi-core, multi-GPU, cluster).
  • Which hyperparameters are known to matter.
  • Whether cross-validation is needed inside each trial.
  • Whether the objective function is deterministic or stochastic.

Output Artifact

Hyperparameter tuning setup with search space design, strategy selection, framework configuration, pruning policy, and distributed execution plan.

Response Format

## Tuning Config
### Search Space
Param: {name} | Type: {float/int/categorical} | Range: [{min}, {max}] | Scale: {linear/log}

### Strategy
Method: {grid / random / Bayesian / TPE / CMA-ES}
Budget: {N trials} | Parallel: {N workers} | Timeout: {duration}

### Framework
Tool: {Optuna / Ray Tune / Hyperopt}
Study: {name} | Direction: {minimize / maximize}
Metric: {metric_name} | Pruning: {none / median / Hyperband / ASHA}

No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output.

Completion Criteria

  • Search space defined with all tunable parameters, distributions, and scales.
  • Search strategy selected based on trial budget and dimensionality.
  • Tuning framework configured with study/trial lifecycle.
  • Pruning policy set with minimum budget checks.
  • Distributed execution configured with fault tolerance.
  • Result persistence configured.
  • Random seed set for reproducibility.
  • Parameter importance analysis planned after tuning.

Workflow

Step 1: Search Space Definition

Use log-uniform for positive-valued parameters spanning orders of magnitude (learning rate 1e-5 to 1e-1, regularization 1e-4 to 1e-1). Use uniform for bounded additive parameters (depth 3-15, units 32-512). Use categorical for discrete options (optimizer, activation). Define conditional spaces where parameters depend on other choices.

import optuna

def create_search_space(trial, model_type="xgboost"):
    if model_type == "xgboost":
        return {
            "n_estimators": trial.suggest_int("n_estimators", 50, 1000),
            "max_depth": trial.suggest_int("max_depth", 3, 15),
            "learning_rate": trial.suggest_float("learning_rate", 1e-3, 0.3, log=True),
            "subsample": trial.suggest_float("subsample", 0.5, 1.0),
            "colsample_bytree": trial.suggest_float("colsample_bytree", 0.3, 1.0),
            "min_child_weight": trial.suggest_int("min_child_weight", 1, 10),
            "reg_alpha": trial.suggest_float("reg_alpha", 1e-4, 10.0, log=True),
            "reg_lambda": trial.suggest_float("reg_lambda", 1e-4, 10.0, log=True),
        }
    elif model_type == "neural_network":
        n_layers = trial.suggest_int("n_layers", 1, 5)
        params = {
            "learning_rate": trial.suggest_float("learning_rate", 1e-5, 1e-1, log=True),
            "batch_size": trial.suggest_categorical("batch_size", [16, 32, 64, 128, 256]),
            "dropout": trial.suggest_float("dropout", 0.0, 0.5),
            "weight_decay": trial.suggest_float("weight_decay", 1e-6, 1e-2, log=True),
            "optimizer": trial.suggest_categorical("optimizer", ["Adam", "AdamW", "SGD"]),
        }
        params["hidden_units"] = [
            trial.suggest_int(f"hidden_units_{i}", 32, 512, log=True)
            for i in range(n_layers)
        ]
        return params

Step 2: Strategy Selection

Grid search: only for <4 dims AND budget covers full grid. Random search: always prefer over grid when >4 dims. Bayesian optimization: GP for <10 continuous params, TPE for mixed/categorical/high-dim. CMA-ES: for 5-20 continuous parameters, non-convex landscapes.

def select_strategy(n_params, trial_time_seconds, budget):
    dims_continuous = sum(1 for p in n_params if p["type"] in ("float", "int") and p.get("log", False))
    dims_discrete = len(n_params) - dims_continuous

    if trial_time_seconds < 10:
        if dims_discrete < 4:
            return "grid"
        return "random"
    elif budget < 100:
        return "random" if dims_discrete > 0 else "bayesian_gp"
    elif trial_time_seconds < 300:
        return "bayesian_tpe"
    else:
        return "bayesian_tpe_with_pruning"

Step 3: Framework Configuration

Optuna: trial.suggest_float/log/int/categorical. Study with TPE sampler + MedianPruner. Storage for persistence. Ray Tune: tune.{loguniform/uniform/randint/choice} with ASHA scheduler. Hyperopt: hp.{loguniform/uniform/quniform/choice}.

# Optuna complete example
import optuna
from optuna.samplers import TPESampler
from optuna.pruners import MedianPruner
import xgboost as xgb
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import cross_val_score

def objective(trial):
    params = {
        "n_estimators": trial.suggest_int("n_estimators", 100, 1000),
        "max_depth": trial.suggest_int("max_depth", 3, 12),
        "learning_rate": trial.suggest_float("learning_rate", 1e-3, 0.3, log=True),
        "subsample": trial.suggest_float("subsample", 0.6, 1.0),
        "colsample_bytree": trial.suggest_float("colsample_bytree", 0.4, 1.0),
        "min_child_weight": trial.suggest_int("min_child_weight", 1, 10),
        "reg_alpha": trial.suggest_float("reg_alpha", 1e-4, 10.0, log=True),
        "reg_lambda": trial.suggest_float("reg_lambda", 1e-4, 10.0, log=True),
        "eval_metric": "logloss",
        "random_state": 42 + trial.number,
    }
    model = xgb.XGBClassifier(**params, use_label_encoder=False)

    score = cross_val_score(model, X_train, y_train, cv=3, scoring="roc_auc", n_jobs=-1).mean()

    for epoch in range(10):
        trial.report(score, epoch)
        if trial.should_prune():
            raise optuna.TrialPruned()

    return score

study = optuna.create_study(
    direction="maximize",
    sampler=TPESampler(n_startup_trials=10, seed=42),
    pruner=MedianPruner(n_startup_trials=10, n_warmup_steps=5, n_min_trials=3),
    storage="sqlite:///optimization.db",
    study_name="xgboost_tuning",
    load_if_exists=True,
)
study.optimize(objective, n_trials=100, timeout=3600, n_jobs=4)

print(f"Best value: {study.best_value}")
print(f"Best params: {study.best_params}")

# Visualization
optuna.visualization.plot_param_importances(study)
optuna.visualization.plot_parallel_coordinate(study)
optuna.visualization.plot_optimization_history(study)

Step 4: Pruning & Early Stopping

Median pruner: stop if intermediate value falls below median of completed trials at same step. Requires n_startup_trials, n_warmup_steps. Hyperband: adaptive resource allocation. ASHA: distributed variant for parallel settings.

# Ray Tune with ASHA pruning
from ray import tune
from ray.tune.schedulers import ASHAScheduler

def train_fn(config):
    model = xgb.XGBClassifier(**config)
    for epoch in range(100):
        model.fit(X_train, y_train, eval_set=[(X_val, y_val)], verbose=0)
        preds = model.predict_proba(X_val)[:, 1]
        score = roc_auc_score(y_val, preds)
        tune.report(metrics={"auc": score, "epoch": epoch})

scheduler = ASHAScheduler(
    max_t=100,
    grace_period=10,
    reduction_factor=3,
    brackets=1,
)

tuner = tune.Tuner(
    train_fn,
    param_space={
        "learning_rate": tune.loguniform(1e-3, 0.3),
        "max_depth": tune.randint(3, 12),
        "subsample": tune.uniform(0.5, 1.0),
    },
    tune_config=tune.TuneConfig(
        metric="auc",
        mode="max",
        num_samples=100,
        scheduler=scheduler,
    ),
)
results = tuner.fit()

Step 5: Distributed Execution

Local multi-core: n_jobs=-1. Dask distributed: wrap Optuna with DaskStorage. Ray distributed: tune.run(resources_per_trial). Kubernetes: each trial as K8s job. Fault tolerance: checkpoint to shared filesystem.

# Optuna distributed with PostgreSQL
study = optuna.create_study(
    storage="postgresql://user:pass@host/db",
    study_name="distributed_tuning",
    load_if_exists=True,
)
# Each worker runs: study.optimize(objective, n_trials=100)

Step 6: Post-Tuning Analysis

Parameter importance (fANOVA-based), parallel coordinate plot, contour plot (top 2 params interaction), optimization history, failure analysis.

def analyze_study(study):
    importances = optuna.visualization.plot_param_importances(study)
    parallel = optuna.visualization.plot_parallel_coordinate(study)
    history = optuna.visualization.plot_optimization_history(study)

    df = study.trials_dataframe()
    failed = df[df["state"] == "FAIL"]
    print(f"Total trials: {len(df)}, Failed: {len(failed)}")
    if len(failed) > 0:
        print(f"Failure reasons: {failed['user_attrs'].value_counts()}")

    return importances, parallel, history

Anti-Patterns

  • Grid search for >4 dimensions: exponential cost, random search covers more distinct values per parameter.
  • No pruning for expensive trials: wastes budget on clearly bad configurations.
  • Pruning too aggressively: stopping before minimum budget leads to incorrect early termination.
  • Learning rate too high: loss diverges to NaN. Too low: training stalls.
  • Not fixing CV splits: high variance in objective values, misleading comparisons.
  • Overly narrow search space: best optimum outside bounds.
  • Overly wide search space: wasted budget exploring irrelevant regions.
  • Ignoring conditional params: momentum sampled even for Adam, wasting budget.
  • Not setting random seed per trial: non-deterministic results, can't reproduce best trial.

Production Considerations

Monitoring

  • Track objective value trend — if new runs produce worse results, data may have drifted.
  • Monitor trial failure rate (>10% indicates pipeline issues).
  • Track search space utilization — best params at boundary means space too narrow.
  • Log study metadata: number of trials, best params, optimization history.
  • Track compute cost (GPU hours, wall time) for budget forecasting.

Deployment

  • Embed best HP configuration in model artifact metadata.
  • Store study database durably for future warm-starting.
  • Automate retuning when dataset grows >2x or new features added.
  • Pin random seed per trial for exact reproduction.
  • Archive best configs for each deployment target (accuracy, speed, size).
  • Validate best config with 3-5 different seeds before production.

Search Space Design — Per Algorithm

Decision Trees / Random Forest

space = {
    "n_estimators": (10, 500),            # integer, uniform
    "max_depth": (3, 50),                 # integer, log scale
    "min_samples_split": (2, 50),         # integer, log scale
    "min_samples_leaf": (1, 50),          # integer, log scale
    "max_features": (0.1, 1.0),           # float, logit scale
    "ccp_alpha": (1e-5, 1e-1),            # float, log scale
}

# Conditional: if max_features == "sqrt", don't tune proportion

XGBoost / LightGBM

space = {
    "learning_rate": (1e-4, 0.3),         # float, LOG scale
    "max_depth": (3, 15),                 # integer, log scale
    "min_child_weight": (1, 100),         # integer, log scale
    "subsample": (0.5, 1.0),              # float
    "colsample_bytree": (0.3, 1.0),       # float
    "gamma": (0, 10),                     # float, log scale
    "reg_alpha": (1e-5, 10),              # float, LOG scale
    "reg_lambda": (1e-5, 10),             # float, LOG scale
    "n_estimators": (50, 1000),           # integer
}
# LightGBM specific:
# "num_leaves": (15, 255), log scale
# "min_data_in_leaf": (10, 100), log scale

Neural Networks

space = {
    "learning_rate": (1e-5, 1e-2),        # LOG scale
    "batch_size": [16, 32, 64, 128, 256], # categorical (discrete jump)
    "weight_decay": (1e-6, 1e-2),         # LOG scale
    "dropout": (0.0, 0.5),                # float
    "hidden_layers": [1, 2, 3, 4],        # categorical
    "hidden_dim": (32, 512),              # integer, log scale
    "activation": ["relu", "gelu", "swish"],  # categorical
    "gradient_clip": (0.1, 10.0),         # float, log scale
    "label_smoothing": (0.0, 0.3),        # float
}
# Adam-specific:
# "beta1": (0.8, 0.95),
# "beta2": (0.99, 0.999),

Transformers (NLP)

space = {
    "learning_rate": (1e-6, 5e-4),        # LOG scale
    "warmup_ratio": (0.0, 0.3),           # float
    "weight_decay": (0.0, 0.1),           # float
    "dropout": (0.0, 0.3),                # float
    "batch_size": [8, 16, 32, 64],        # categorical
    "gradient_accumulation": [1, 2, 4, 8],# categorical
    "num_train_epochs": (2, 10),          # integer
    "max_seq_length": [128, 256, 512],    # categorical
    "lora_r": [4, 8, 16, 32],             # if using LoRA
    "lora_alpha": [8, 16, 32, 64],        # if using LoRA
}
# Rule: lora_alpha = lora_r * 2 usually works well

Conditional Search Spaces

# When parameter B is only valid if parameter A has a specific value
space = {
    "optimizer": ["adam", "sgd", "adamw"],
    # Conditional: if optimizer == "adamw":
    "weight_decay": (0.0, 0.1),           # only for AdamW
    "scheduler": ["cosine", "linear", "constant"],
    # Conditional: if scheduler in ["cosine", "linear"]:
    "warmup_steps": (0, 1000),            # only if scheduler != constant
}

Pruning Patterns

Median Stopping Rule (Optuna)

import optuna

def objective(trial):
    # Suggest hyperparameters
    lr = trial.suggest_float("lr", 1e-5, 1e-2, log=True)

    # Train model epoch by epoch
    for epoch in range(100):
        loss = train_one_epoch(lr)

        # Report intermediate value to Optuna
        trial.report(loss, epoch)

        # Handle pruning based on median stopping rule
        if trial.should_prune():
            raise optuna.TrialPruned()

    return loss

study = optuna.create_study(
    direction="minimize",
    pruner=optuna.pruners.MedianPruner(
        n_startup_trials=10,    # don't prune first 10 trials
        n_warmup_steps=5,       # don't prune first 5 epochs
        interval_steps=1,       # check every epoch
    ),
)

ASHA (Asynchronous Successive Halving Algorithm)

from optuna.pruners import HyperbandPruner

study = optuna.create_study(
    direction="minimize",
    pruner=HyperbandPruner(
        min_resource=1,          # minimum epochs
        max_resource=100,        # maximum epochs
        reduction_factor=3,      # elim fraction = 1/3
    ),
)

# How it works:
# 1. Start many trials with small budget (1 epoch)
# 2. Keep top 1/reduction_factor after each rung
# 3. Double budget for survivors
# 4. Repeat until max_resource reached

Custom Pruning Criteria

def custom_prune_callback(study, trial):
    """Prune if loss hasn't improved for 10 epochs."""
    intermediate_values = trial.intermediate_values
    if len(intermediate_values) < 10:
        return

    recent = list(intermediate_values.values())[-10:]
    best_recent = min(recent)
    best_overall = min(intermediate_values.values())

    if best_recent > best_overall * 1.1:  # 10% worse than best
        raise optuna.TrialPruned()

Bayesian Optimization Strategies

StrategyAcquisition FunctionBest ForProsCons
GP (Gaussian Process)EI, UCB, PI< 20 dimsSample-efficientDoesn't scale with dims
TPE (Tree Parzen)EI10-100 dimsHandles mixed typesMore overhead per trial
Random Forest SMACEICategorical heavyWorks well on treesSlower with many cats
CMA-ESPopulation-basedContinuous onlyNo surrogate modelNot for mixed spaces
BOHB (Bayesian + HB)TPE + HyperBandAnyBest of bothComplex setup

Hyperparameter Tuning Anti-Patterns

  1. Grid search for > 4 dimensions: Exponential blowup Fix: Random search or Bayesian optimization
  2. No log scale for learning rate: Linear grid misses useful range Fix: Always log-scale positive parameters spanning orders of magnitude
  3. Pruning too early: Best configs sometimes start slow Fix: n_warmup_steps >= 5, n_startup_trials >= 10
  4. Overfitting to validation set: Tuning on same split as evaluation Fix: Nested CV or separate hold-out for final evaluation
  5. Too few trials: Bayesian optimization needs 10-20 initial random trials Fix: Total trials = 10 * dimensionality (minimum)
  6. Non-deterministic trials: Randomness masks true HP effects Fix: Fix seed per trial, use same seed across HP comparisons

Framework Comparison

FrameworkParallelDistributedPruningEarly StoppingVisualization
OptunaYesYesYesYesBuilt-in dashboard
Ray TuneYesYesYes (ASHA, Median)YesTensorBoard integrated
HyperoptYesYes (MongoDB)NoManualLimited
SMAC3NoNoNoManualBuilt-in
Katib (K8s)YesYesYes (Median, ASHA)YesK8s-native
Weights & BiasesYesNoYesYesRich sweeps UI

Rules

  • Random search beats grid when >4 dims.
  • Bayesian optimization needs 10-20 initial random trials.
  • Pruning needs minimum budget (e.g., 10% of total epochs).
  • Log-scale learning rate, regularization, and all positive params spanning OoM.
  • Use conditional search spaces for hierarchical params.
  • Each trial must be deterministic given its seed.
  • Never prune too aggressively: min 5-10 trials before pruning.
  • Distributed tuning requires shared filesystem for checkpoints.
  • Multi-objective: always show Pareto front, not single point.
  • Store study database durably.

References

  • references/automl-tuning.md — AutoML Tuning
  • references/hyperparameter-tuning-advanced.md — Hyperparameter Tuning Advanced Topics
  • references/hyperparameter-tuning-fundamentals.md — Hyperparameter Tuning Fundamentals
  • references/multi-fidelity.md — Multi-Fidelity Optimization
  • references/optimization-methods.md — Hyperparameter Optimization
  • references/search-strategies.md — Search Strategies
  • references/tuning-at-scale.md — Distributed Tuning
  • references/tuning-frameworks.md — Tuning Frameworks

Handoff

Pass tuned hyperparameters to ml-experiment-tracking for logging. Hand off to ml-model-evaluation for evaluating tuned model.

保存したスキル