Изборът на правилните хиперпараметри често е разликата между моделен прототип и решение, готово за продукция. Звучи драматично, нали? Истината обаче е, че съм виждал XGBoost модел да скочи от 0.82 на 0.91 AUC само заради смислено tuning — без нито един нов feature. Точно тук на сцената излиза Optuna.
Тя се превърна в де факто стандарт за автоматизирана оптимизация на хиперпараметри в Python, и то с пълно основание — благодарение на интуитивния си define-by-run API, ефективния TPE алгоритъм и вградените механизми за pruning.
В това ръководство ще преминем през всичко, което ви трябва, за да започнете с Optuna 4.x през 2026 г.: от инсталация и базови концепции до интеграция със Scikit-learn и XGBoost, multi-objective оптимизация и нови samplers като GPSampler и AutoSampler. Хайде да започваме.
Какво е Optuna и защо изобщо да я ползваме?
Optuna е framework за автоматизирана оптимизация на хиперпараметри, проектиран специално за машинно обучение. За разлика от класическите подходи като GridSearchCV и RandomizedSearchCV в Scikit-learn, Optuna използва Байесова оптимизация, за да насочи търсенето към най-обещаващите комбинации.
С други думи — вместо сляпо да изпробва всичко (или произволни точки), тя „учи" от резултатите на предишните trials и предлага следващите по-умно.
Основни предимства спрямо GridSearch
- Define-by-run API — пространството за търсене се дефинира динамично в Python код, а не статично в речник.
- TPE (Tree-structured Parzen Estimator) — интелигентно sampling, което се подобрява с всеки опит.
- Pruning — Optuna автоматично прекратява безперспективните trials, спестявайки часове изчислителна мощност.
- Multi-objective — оптимизирайте едновременно accuracy и inference time, например.
- Persistence — съхранение на резултатите в SQLite, PostgreSQL или MySQL за продължаване по-късно.
- Framework-agnostic — работи със Scikit-learn, XGBoost, LightGBM, PyTorch, TensorFlow и т.н.
Инсталация и подготовка на средата
Към май 2026 г. последната стабилна версия е Optuna 4.7. Инсталацията е стандартна с pip — никакви изненади тук:
pip install optuna optuna-dashboard scikit-learn xgboost
За работа с напреднали samplers като GPSampler и AutoSampler от OptunaHub ще ви трябва още един пакет:
pip install optunahub
Първият ви Optuna study: минимален пример
Преди да минем към реални ML модели, нека разгледаме каноничния минимален пример — намиране на минимума на квадратична функция. Това е „hello world"-ът на Optuna:
import optuna
def objective(trial):
x = trial.suggest_float("x", -10, 10)
return (x - 2) ** 2
study = optuna.create_study(direction="minimize")
study.optimize(objective, n_trials=100)
print(f"Best value: {study.best_value:.4f}")
print(f"Best params: {study.best_params}")
Ключовите концепции тук са три:
- Trial — едно изпълнение на
objectiveфункцията с конкретен набор параметри. - Study — колекция от trials, които заедно формират процеса на оптимизация.
- suggest_* методите — те съобщават на Optuna какви параметри да предложи.
Типове suggest методи
Optuna поддържа доста широк набор от типове параметри. На практика тези четири покриват около 95% от случаите:
# Числови параметри
trial.suggest_float("learning_rate", 1e-5, 1e-1, log=True)
trial.suggest_int("n_estimators", 50, 1000, step=50)
# Категориални параметри
trial.suggest_categorical("optimizer", ["adam", "sgd", "rmsprop"])
# Дискретни числа с фиксирана стъпка
trial.suggest_float("dropout", 0.1, 0.5, step=0.05)
Малка, но важна подробност: аргументът log=True е особено важен за параметри като learning rate или regularization. Те почти винаги се изследват най-добре в логаритмична скала — иначе хабите trials в нерелевантни области.
Optuna със Scikit-learn: реален пример
Сега ще оптимизираме RandomForestClassifier върху Breast Cancer датасета. Това е често срещан baseline за класификация и (честно казано) перфектен пример, на който да видите Optuna в действие:
import optuna
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
X, y = load_breast_cancer(return_X_y=True)
def objective(trial):
params = {
"n_estimators": trial.suggest_int("n_estimators", 100, 1000, step=50),
"max_depth": trial.suggest_int("max_depth", 3, 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", None]
),
"bootstrap": trial.suggest_categorical("bootstrap", [True, False]),
}
model = RandomForestClassifier(**params, random_state=42, n_jobs=-1)
score = cross_val_score(model, X, y, cv=5, scoring="roc_auc").mean()
return score
study = optuna.create_study(direction="maximize", study_name="rf_tuning")
study.optimize(objective, n_trials=50, n_jobs=-1)
print(f"Best AUC: {study.best_value:.4f}")
print(f"Best params: {study.best_params}")
Забележете два важни детайла:
direction="maximize"— защото искаме да максимизираме AUC.n_jobs=-1вstudy.optimizeпозволява паралелно изпълнение на trials.
Optuna с XGBoost и pruning
За градиентни boosting модели като XGBoost pruning е просто незаменим. Тя позволява на Optuna да спре trial по средата на тренировката, ако той не показва обещаващи резултати — нещо като „виждам накъде вървиш, спести ми още 400 boost rounds":
import optuna
import xgboost as xgb
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
X, y = load_breast_cancer(return_X_y=True)
X_train, X_val, y_train, y_val = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y
)
dtrain = xgb.DMatrix(X_train, label=y_train)
dvalid = xgb.DMatrix(X_val, label=y_val)
def objective(trial):
params = {
"objective": "binary:logistic",
"eval_metric": "auc",
"booster": trial.suggest_categorical("booster", ["gbtree", "dart"]),
"lambda": trial.suggest_float("lambda", 1e-8, 1.0, log=True),
"alpha": trial.suggest_float("alpha", 1e-8, 1.0, log=True),
"subsample": trial.suggest_float("subsample", 0.5, 1.0),
"colsample_bytree": trial.suggest_float("colsample_bytree", 0.5, 1.0),
"max_depth": trial.suggest_int("max_depth", 3, 10),
"learning_rate": trial.suggest_float("learning_rate", 1e-3, 0.3, log=True),
}
# Pruning callback — спира trial-а ако не се представя добре
pruning_callback = optuna.integration.XGBoostPruningCallback(
trial, "validation-auc"
)
booster = xgb.train(
params,
dtrain,
num_boost_round=500,
evals=[(dvalid, "validation")],
callbacks=[pruning_callback],
early_stopping_rounds=20,
verbose_eval=False,
)
return booster.best_score
study = optuna.create_study(
direction="maximize",
pruner=optuna.pruners.HyperbandPruner(min_resource=10, max_resource=500),
sampler=optuna.samplers.TPESampler(seed=42),
)
study.optimize(objective, n_trials=100, timeout=600)
print(f"Best AUC: {study.best_value:.4f}")
print(f"Number of pruned trials: {len([t for t in study.trials if t.state == optuna.trial.TrialState.PRUNED])}")
Комбинацията TPESampler + HyperbandPruner е сред най-ефективните според официалните бенчмаркове на Optuna. В мой неотдавнашен проект с tabular data това намали времето за tuning от около 4 часа до под един — без забележим спад в крайното качество.
Samplers: коя стратегия да изберем?
Optuna предлага няколко стратегии за sampling. Изборът зависи от вашия проблем — няма универсален победител.
TPESampler (по подразбиране)
Tree-structured Parzen Estimator е стандартният избор за повечето задачи. От версия 4.x подобрението multivariate=True допълнително повишава ефективността:
sampler = optuna.samplers.TPESampler(
multivariate=True,
group=True,
seed=42,
)
GPSampler (Gaussian Process)
За много скъпи objective функции (например тренировка на дълбоки невронни мрежи), GPSampler постига по-висока sample-efficiency. В Optuna 4.6 беше сериозно оптимизиран и работи около 8 пъти по-бързо от предишните версии:
sampler = optuna.samplers.GPSampler(seed=42)
study = optuna.create_study(direction="maximize", sampler=sampler)
AutoSampler (OptunaHub)
Не сте сигурни кой sampler да изберете? AutoSampler автоматично избира най-подходящия алгоритъм според характеристиките на задачата. Прекрасен default за неопитни потребители:
import optunahub
module = optunahub.load_module("samplers/auto_sampler")
sampler = module.AutoSampler()
study = optuna.create_study(direction="maximize", sampler=sampler)
Multi-objective оптимизация
В реални продукционни системи често искате да оптимизирате повече от една метрика — например да постигнете висока точност и ниско време за inference. (Особено ако моделът ви ще обслужва трафик в реално време.) Optuna поддържа това директно:
import time
import optuna
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import cross_val_score
X, y = load_breast_cancer(return_X_y=True)
def objective(trial):
n_estimators = trial.suggest_int("n_estimators", 50, 1000, step=50)
max_depth = trial.suggest_int("max_depth", 3, 30)
model = RandomForestClassifier(
n_estimators=n_estimators,
max_depth=max_depth,
n_jobs=-1,
random_state=42,
)
accuracy = cross_val_score(model, X, y, cv=3, scoring="roc_auc").mean()
start = time.perf_counter()
model.fit(X, y)
_ = model.predict(X[:100])
inference_time = time.perf_counter() - start
return accuracy, inference_time
study = optuna.create_study(directions=["maximize", "minimize"])
study.optimize(objective, n_trials=50)
# Pareto-оптимални trials
for trial in study.best_trials:
print(f"Trial {trial.number}: AUC={trial.values[0]:.4f}, Time={trial.values[1]:.4f}s")
Резултатът е Pareto front — множество решения, при които не можете да подобрите едната метрика без да влошите другата. Изборът кое от тях да отиде в продукция вече е чисто бизнес решение.
Визуализация на резултатите
Optuna идва с богат набор от вградени визуализации, базирани на Plotly. Лично аз винаги започвам с plot_param_importances — често разкрива, че половината от параметрите, които tune-вам, реално не влияят на нищо:
import optuna.visualization as vis
# История на оптимизацията
vis.plot_optimization_history(study).show()
# Важност на параметрите
vis.plot_param_importances(study).show()
# Parallel coordinate plot
vis.plot_parallel_coordinate(study).show()
# Slice plot за всеки параметър
vis.plot_slice(study).show()
# Contour plot за двойки параметри
vis.plot_contour(study, params=["learning_rate", "max_depth"]).show()
За интерактивно проследяване на оптимизацията в реално време, използвайте Optuna Dashboard:
# Запазете study-то в SQLite
study = optuna.create_study(
storage="sqlite:///optuna_study.db",
study_name="my_experiment",
load_if_exists=True,
)
# В отделен терминал:
# optuna-dashboard sqlite:///optuna_study.db
Persistence и разпределена оптимизация
За дълги оптимизации (часове или дни) винаги използвайте persistent storage. Това позволява:
- Продължаване след прекъсване на процеса.
- Паралелна оптимизация от множество машини.
- Анализ на резултатите по-късно.
# Първа машина
study = optuna.create_study(
storage="postgresql://user:pass@db-server/optuna",
study_name="distributed_run",
direction="maximize",
load_if_exists=True,
)
study.optimize(objective, n_trials=100)
# Втора машина — същият study_name и storage
study = optuna.load_study(
storage="postgresql://user:pass@db-server/optuna",
study_name="distributed_run",
)
study.optimize(objective, n_trials=100)
Най-добри практики през 2026 г.
- Никога не tunirайте върху test set. Разделете данните на train / validation / test. Optuna използва само validation set.
- Използвайте cross-validation в objective функцията за по-стабилни резултати, особено при малки датасети.
- Винаги фиксирайте seed на sampler-а за репродуцируеми експерименти:
TPESampler(seed=42). - Активирайте pruning за iterative модели (XGBoost, LightGBM, neural networks) — спестява 50–80% от изчислителното време.
- Дефинирайте search space логаритмично за параметри като learning rate, regularization (
log=True). - Започнете с малък брой trials (20–30), анализирайте важността на параметрите и след това стеснете обхвата.
- Използвайте
n_jobsвнимателно — паралелизмът на ниво trial може да влоши TPE sampling-а; често е по-добре да паралелизирате вътре в самия модел.
Често срещани грешки
Грешка 1: Прекалено широк search space
Дефинирането на learning_rate между 0 и 1 без log scale води до загуба на trials в нерелевантни области. Винаги използвайте domain knowledge за разумни граници — няма нужда да изпробвате learning rate от 0.5, освен ако нямате много специфична причина.
Грешка 2: Игнориране на pruning при iterative модели
Без pruning, XGBoost trial с 1000 boost rounds може да отнеме минути дори когато първите 50 rounds ясно показват слаб резултат. Това е директно изгорено време и електричество.
Грешка 3: Объркване на directions при multi-objective
При multi-objective задайте directions (множествено число) — списък със стойности като ["maximize", "minimize"], а не единичното direction. Малка разлика в правописа, голяма разлика в поведението.
Сравнение: Optuna vs алтернативи
| Инструмент | API | Pruning | Multi-objective | Distributed |
|---|---|---|---|---|
| Optuna 4.x | Define-by-run | ✅ | ✅ | ✅ |
| Hyperopt | Define-and-run | Ограничено | ❌ | Spark |
| Ray Tune | Define-and-run | ✅ | ✅ | ✅ (Ray) |
| Scikit-learn GridSearch | Define-and-run | ❌ | ❌ | joblib |
Optuna остава най-добрият baseline за повечето Python ML проекти през 2026 г., особено когато се търси баланс между производителност, простота и гъвкавост.
Често задавани въпроси (FAQ)
Колко trials са нужни за добра оптимизация с Optuna?
Зависи от размера на search space-а. Като общо правило: 20–50 trials за прост модел с 3–5 параметъра, 100–300 trials за по-сложни модели с 8–15 параметъра, и 500+ за neural network архитектури. С активирано pruning може да се пуснат значително повече trials за същото време.
Каква е разликата между TPE и Bayesian optimization в Optuna?
TPE (Tree-structured Parzen Estimator) е форма на Bayesian optimization. Той моделира p(x|y) вместо p(y|x), което го прави особено ефективен при високомерни и категориални search space-ове. GPSampler е алтернативна Bayesian имплементация, която използва Gaussian Processes — по-добра за непрекъснати ниско-измерни задачи.
Мога ли да използвам Optuna с PyTorch или TensorFlow?
Да. Optuna има вградени integration callbacks за PyTorch Lightning, TensorFlow/Keras, FastAI и др. За PyTorch Lightning например използвайте optuna.integration.PyTorchLightningPruningCallback за автоматичен pruning по validation метриката.
Как се прекратява Optuna study преждевременно?
Има три основни начина: задаване на timeout в study.optimize(timeout=3600) (в секунди), задаване на конкретен брой trials с n_trials, или дефиниране на собствен callback, който извиква study.stop() при определено условие (например достигане на целева метрика).
Защо Optuna е по-добра от Scikit-learn's GridSearchCV?
GridSearchCV изпробва всяка комбинация в дефинирания мрежов формат (exhaustive search), което експоненциално нараства с броя параметри. Optuna използва Bayesian методи, за да насочи интелигентно търсенето към обещаващи области, плюс предлага pruning и multi-objective оптимизация — функционалност, която GridSearchCV изобщо няма.
Заключение
Optuna 4.x предлага най-зрелия и гъвкав инструмент за оптимизация на хиперпараметри в Python екосистемата за 2026 г. Комбинацията от define-by-run API, ефективен TPE sampler, pruning и multi-objective поддръжка я прави идеален избор както за начинаещи, така и за продукционни ML системи.
Започнете с базовия пример, добавете pruning при iterative модели и постепенно разширявайте към multi-objective и distributed optimization, когато проектът ви го изисква. Резултатът ще бъдат по-добри модели за по-малко време — а кой не би искал такава сделка?