
Привет, Хабр!
Гиперпараметры — это параметры, которые не учатся в процессе обучения модели. Они задаются заранее. От выбора гиперпараметров напрямую зависит качество и эффективность модели, а их оптимизация может улучшить результаты предсказаний.
Традиционный подход к оптимизации гиперпараметров включает в себя grid search и random search, иногда они могут быть неэффективными и времязатратными, особенно когда пространство гиперпараметров велико.
Когда я впервые столкнулся с необходимостью настроить сотни параметров в своей нейросети, задача показалась мне Optuna, которая позоволила оптимизировать этот процесс.
Optuna решает проблему оптимизации гиперпараметров, предоставляя легковесный фреймворк для автоматизации поиска оптимальных гиперпараметров. Она использует алгоритмы, такие как TPE, CMA-ES, и даже поддерживает пользовательские алгоритмы.
Optuna полностью написана на Python и имеет мало зависимостей. В этой статье рассмотрим её основной функционал.
Установим:
pip install optuna
Создание первого исследования и определение функции цели
Исследование в Optuna представляет собой оптимизационную сессию, в рамках которой происходит поиск оптимальных гиперпараметров.
Объект Study
Для начала работы с Optuna необходимо создать объект Study. Это можно сделать с помощью функции optuna.create_study(), которая имеет два основных параметра: study_name и direction.
study_name — это имя исследования, уникальный идентификатор, который можно использовать для ссылки на исследование в будущем.
direction указывает, что вы пытаетесь сделать: максимизировать "maximize" или минимизировать "minimize" целевую метрику.
Пример создания исследования для максимизации метрики:
import optuna study = optuna.create_study(study_name="my_first_study", direction="maximize")
Функция цели
Функция цели — это основа процесса оптимизации в Optuna. Эта функция принимает объект trial в качестве аргумента и возвращает числовое значение метрики, которую нужно максимизировать или минимизировать. Optuna автоматически вызывает эту функцию, передавая ей различные комбинации гиперпараметров для поиска оптимального решения
Optuna имеет несколько методов suggest_* для определения диапазона значений каждого гиперпараметра, их нужно использовать внутри функции цели.
suggest_float используется для предложения float значений гиперпараметров в заданном диапазоне. Можно также указать шаг для дискретизации.
learning_rate = trial.suggest_float('learning_rate', 1e-5, 1e-1, log=True)
Ищем значение learning_rate в диапазоне от 1e-5 до 1e-1 с логарифмическим масштабом
suggest_categorical используется для выбора из списка категориальных значений, т.е строки, числа или другие неупорядоченные категории:
codeoptimizer = trial.suggest_categorical('optimizer', ['adam', 'sgd', 'rmsprop'])
Optuna выберет один из трех оптимизаторов для оценки.
Для целочисленных гиперпараметров используется suggest_int. Можно также использовать параметр step, если нужны целочисленные значения, изменяющиеся не на каждый следующий инкремент, а с определенным шагом.
codenum_layers = trial.suggest_int('num_layers', 1, 10)
Optuna будет исследовать количество слоев от 1 до 10 включительно.
suggest_loguniform используется для предложения гиперпараметра из логарифмического распределения. Метод подходит для гиперпараметров, которые изменяются в широком диапазоне и где меньшие значения являются более чувствительными.
learning_rate = trial.suggest_loguniform('learning_rate', 1e-5, 1e-2)
suggest_discrete_uniform предлагает вещественное число, которое изменяется на фиксированный шаг:
feature_fraction = trial.suggest_discrete_uniform('feature_fraction', 0.4, 1.0, 0.1)
suggest_uniform предлагает вещественное число из равномерного распределения. Он похож на suggest_float, но всегда предполагает равномерное распределение без возможности логарифмического масштаба.
coef = trial.suggest_niform('coef', -1.0, 1.0)
Также можно юзать условную логику при определении гиперпараметров:
optimizer = trial.suggest_categorical('optimizer', ['adam', 'rmsprop', 'sgd']) if optimizer == 'adam': learning_rate = trial.suggest_float('adam_lr', 1e-5, 1e-1, log=True) elif optimizer == 'rmsprop': learning_rate = trial.suggest_float('rmsprop_lr', 1e-5, 1e-2, log=True) else: learning_rate = trial.suggest_float('sgd_lr', 1e-4, 1e-1, log=True)
Рассмотрим простой пример с моделью линейной регрессии, где постараемся минимизировать среднеквадратичную ошибку на валидационном наборе данных:
import numpy as np from sklearn.datasets import make_regression from sklearn.linear_model import Ridge from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error # датасет X, y = make_regression(n_samples=1000, n_features=20, noise=0.1) X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.2) def objective(trial): # предложение гиперпараметров alpha = trial.suggest_float('alpha', 1e-10, 1.0, log=True) # создание и обучение модели model = Ridge(alpha=alpha) model.fit(X_train, y_train) # предсказание и вычисление MSE y_pred = model.predict(X_valid) mse = mean_squared_error(y_valid, y_pred) return mse study.optimize(objective, n_trials=100)
Optuna исследует значения alpha в указанном диапазоне с логарифмическим масштабом log=True.
Алгоритмы оптимизации
TPE работает на основе моделирования вероятностей гиперпараметров, используя информацию из предыдущих испытаний. В отличие от случайного поиска или поиска по сетке, TPE способен учиться на истории испытаний и направлять поиск в наиболее перспективные области пространства гиперпараметров:
import optuna def objective(trial): x = trial.suggest_float('x', -10, 10) return (x - 2) ** 2 study = optuna.create_study(sampler=optuna.samplers.TPESampler()) study.optimize(objective, n_trials=100) print(study.best_params) # находит оптимальное значение 'x'
Мнимизируем квадратное отклонение от 2, используя TPE для предложения значений ‘x’. TPE адаптируется после каждого испытания, быстро сужая поиск к оптимальному значению.
CMA-ES является стратегией эволюционной оптимизации, которая особенно хороша когда пространство параметров велико или когда отсутствует явная градиентная информация. CMA-ES адаптирует def objective(trial): x = trial.suggest_float('x', -10, 10) y = trial.suggest_float('y', -10, 10) return (x - 2) ** 2 + (y + 3) ** 2 study = optuna.create_study(sampler=optuna.samplers.CmaEsSampler()) study.optimize(objective, n_trials=100) print(study.best_params) # находит оптимальные значения 'x' и 'y'
Минимизируем функцию двух переменных, используя CMA-ES для предложения значений ‘x’ и ‘y’. CMA-ES эффективно исследует пространство, адаптируя стратегию поиска на основе предыдущих результатов. В Optuna для решения задач многокритериальной оптимизации используется класс Допустим, хочется оптимизировать классификатор так, чтобы максимизировать точность на валидационном наборе данных и одновременно минимизировать количество используемых признаков для предотвращения переобучения: Используем классификатор Пользовательские атрибуты можно добавить к испытанию через метод Добавляем два пользовательских атрибута: После завершения оптимизации пользовательские атрибуты можно извлечь из объекта Optuna CLI предлагает несколько команд для работы с исследованиями. Чтобы создать новое исследование через CLI есть После выполнения этой команды будет создано новое исследование с указанным именем и направлением оптимизации, и вы будет получено сообщение: Чтобы просмотреть список всех доступных исследований — Выведет список всех исследований, доступных в текущей БД Optuna. Чтобы добавить испытания в существующее исследование, можно использовать команду Затем сообщите Optuna результаты этого испытания: Optuna CLI предлагает визуализацию результатов через команду Более подробно с Optuna можно ознакомиться на их гитхабе. А узнать практические инструменты аналитики можно в рамках онлайн-курсов от ведущих экспертов рынка. С полным каталогом курсов OTUS можно ознакомиться по ссылке.Многокритериальная оптимизация
optuna.study.create_study с аргументом directions, который принимает список, указывающий направление оптимизации minimize или maximize для каждой из целей. Затем, функция цели должна возвращать кортеж значений, соответствующих каждой из оптимизируемых метрик.import optuna import sklearn.datasets import sklearn.linear_model import sklearn.model_selection def objective(trial): iris = sklearn.datasets.load_iris() x, y = iris.data, iris.target # выбор гиперпараметров alpha = trial.suggest_float('alpha', 1e-5, 1e-1, log=True) ratio = trial.suggest_float('l1_ratio', 0, 1) # модель classifier = sklearn.linear_model.ElasticNet(alpha=alpha, l1_ratio=ratio) # разделение данных x_train, x_valid, y_train, y_valid = sklearn.model_selection.train_test_split(x, y, test_size=0.25) # обучение модели classifier.fit(x_train, y_train) accuracy = sklearn.model_selection.cross_val_score(classifier, x_valid, y_valid, n_jobs=-1, cv=3).mean() # колво выбранных признаков (ненулевых коэффициентов) n_features = sum(coef != 0 for coef in classifier.coef_) return accuracy, n_features study = optuna.create_study(directions=['maximize', 'minimize']) study.optimize(objective, n_trials=100) print("Number of finished trials: ", len(study.trials)) print("Best trial:") trial = study.best_trial print(" Values: ", trial.values) print(" Params: ") for key, value in trial.params.items(): print(f" {key}: {value}")ElasticNet из sklearn.linear_model, который сочетает в себе L1 и L2 регуляризацию. Оптимизируем два гиперпараметра: alpha и l1_ratio, и две цели: точность классификации accuracy и кол-во используемых признаков n_features.Пользовательские атрибуты
set_user_attr объекта trial. Эти атрибуты сохраняются вместе с результатами испытания и могут быть извлечены для анализа в любой момент после завершения испытания:import optuna def objective(trial): x = trial.suggest_float("x", -10, 10) trial.set_user_attr("description", "This is a trial to optimize x.") trial.set_user_attr("iteration", 1) return (x - 2) ** 2 study = optuna.create_study() study.optimize(objective, n_trials=10) # извлечение и печать пользовательских атрибутов для всех испытаний for trial in study.trials: print(f"Trial {trial.number} Attributes:", trial.user_attrs)description, который описывает цель испытания, и iteration, который может отслеживать номер итерации в серии экспериментов. trial. Это делается через свойство user_attrs объекта trial, которое возвращает словарь атрибутов, добавленных во время испытания:best_trial = study.best_trial print("Best Trial Description:", best_trial.user_attrs["description"]) print("Best Trial Iteration:", best_trial.user_attrs["iteration"])CLI
optuna create-study. Можно указать имя исследования и направление оптимизацииoptuna create-study --study-name "my_study" --direction "minimize"[I 2024-10-10 10:00:00,000] A new study created in RDB with name: my_studyoptuna studies.optuna studiesoptuna ask-and-tell:optuna ask --study-name "my_study"optuna tell --study-name "my_study" --trial-number 0 --values 0.5optuna dashboard. Эта команда запускает локальный сервер с веб-интерфейсом, где и будут результаты:optuna dashboard --study-name "my_study" --storage "sqlite:///example.db"
ссылка на оригинал статьи https://habr.com/ru/articles/801463/
Добавить комментарий