
Исходники: https://github.com/facebookresearch/hydra
Документация: https://hydra.cc/docs/intro/
Hydra это фреймворк для управления файлами конфигурации, заточенный под ML-проекты.
Преимущества конфигурационных файлов очевидны: вы можете изменять параметры вашего проекта не затрагивая основную бизнес-логику. Но не все так гладко. Работа с моделями машинного обучения несколько отличается от классической разработки:
-
Вам потребуется проводить множество экспериментов с гиперпараметрами и их нужно как-то отслеживать. Вы можете просто каждый раз делать копию файла с новым именем. Но это не удобно и очень скоро ваша папка с проектом превратится в мусорку.
-
Вы можете захотеть попробовать различные библиотеки, для которых потребуются разные настройки. Как вариант, их можно поместить в один конфиг-файл, но это сделает его трудно читаемым. Или вы можете разнести их на отдельные конфиги, с дублированием общей части, что вскоре станет не удобно поддерживать. Помимо этого вам потребуется как-то разделить их вызов в коде.
Все эти проблемы (и даже больше) позволяет решить Hydra. Ее ключевые особенности:
-
Иерархическая конфигурация
-
Автоматическое логирование
-
Переопределение параметров из командной строки
-
Запуск множества заданий с помощью одной команды
-
И пр.
А теперь посмотрим как все это работает. Но начнем как обычно с установки…
Установка
Тут все просто. Выполняем в командной строке:
pip install hydra-core --upgrade
Вместе с гидрой будет установлена библиотека OmegaConf, которая в фоне используется гидрой. OmegaConf предназначена для доступа и управления файлам конфигурации на основе YAML. Именно в YAML задаются все конфиги для гидры.
Начало
Сначала самым простым образом задействуем гидру.
Создадим ML-проект с такой структурой:
config - config.yaml app.py df.csv
Содержимое файлов:
#config.yaml model: lgbm params: learning_rate: 0.05 n_estimators: 100 max_depth: 9
Здесь мы указываем модель, для которой этот конфиг, и задаем параметры ее обучения.
#app.py import hydra import pandas as pd from omegaconf import OmegaConf, DictConfig from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error from lightgbm import LGBMRegressor import logging log = logging.getLogger(__name__) @hydra.main(version_base=None, config_path='config', config_name='config') def my_app(cfg: DictConfig): print(OmegaConf.to_yaml(cfg)) # Загружаем данные df = pd.read_csv('df.csv') X = df.drop(columns='target') y = df['target'] # Формируем трейн/тест X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.30, random_state=42) # Обучение модели model = LGBMRegressor() lgbm_params = OmegaConf.to_container(cfg['params']) model.set_params(**lgbm_params) model.fit(X_train, y_train) # Оценка y_pred = model.predict(X_test) mse = round(mean_squared_error(y_test, y_pred), 2) log.info(f'MSE: {mse}') if __name__ == "__main__": my_app()
Что мы тут делаем:
-
Посредством декоратора @hydra.main подгружаем конфиги из указанной папки.
-
Загружаем датасет и делим его на трейн и тест.
-
Обучаем модель на параметрах, указанных в конфиге. При этом мы конвертируем исходный конфиг DictConfig в стандартный Dict, поскольку именно его принимает метод set_params.
-
Делаем предсказание и подсчитываем скор. Обратите внимание, что вывод информации осуществляется через модуль logger − это важно − только данные выведенные через logger будут логироваться гидрой (увидим дальше). Обычный print останется только в консоли.
З.Ы. Вы можете использовать одни параметры конфига как значения для других:
server: ip: "127.0.0.1" port: "8080" address: "${server.ip}:${server.port}"
Дальше мы будем модифицировать части этого кода, исследуя разные возможности гидры…
Конфигурация загружается как объект DictConfig, который похож на стандартный словарь питона, но предоставляет несколько дополнительных функций (например, вложенность). Подробнее о возможносях DictConfig можно прочитать в официальной документации: https://omegaconf.readthedocs.io/en/latest/usage.html#access-and-manipulation
Логирование
Запустим приложение:
> python3 app.py model: lgbm params: learning_rate: 0.05 n_estimators: 100 max_depth: 9 [2022-10-31 19:53:29,960][main][INFO] - MSE: 2948.27
Сразу после запуска появится папка outputs, в которой hydra будет логировать все запуски. Каждый запуск сохраняется в отдельной папке: outputs/YYYY-mm-dd/HH-MM-SS

Теперь пойдем поменяем в конфиге какой-нибудь гиперпараметр и заново запустим программу. Вуаля − у нас новая папка со всеми логами.

Таким образом вам больше не нужно помнить, какую скорость обучения вы использовали для запуска этой модели и прочие подробности экспериментов, так как у вас всегда будет храниться резервная копия файла конфигурации и результаты выполнения.
Вы можете при запуске изменить папку выгрузки логов:
> python3 app.py hydra.run.dir=my_folder
Что содержат эти логи:
-
config.yaml — копия файла конфигурации, переданного через декоратор в функцию.
-
hydra.yaml — копия служебного конфигурационного файла Hydra.
-
overrides.yaml — копия параметров, заданных через командную строку и которые изменяют одно из значений из конфиг файла (посмотрим как это делается далее).
-
<file_name>.log — здесь будут храниться данные переданные через модуль logging.
По умолчанию в лог попадают категории info и warning, но посредством параметров можно логировать и debug:
> python3 app.py hydra.verbose=true
Переопределение
При запуске мы можем переопределить значения конфига в командной строке:
> python3 app.py params.max_depth=11 model: lgbm params: learning_rate: 0.05 n_estimators: 100 max_depth: 11 [2022-10-31 21:40:13,889][main][INFO] - MSE: 2943.45
Переопределенные таким образом параметры будут сохранены в отдельном логе — overrides.yaml.
Помимо этого вы можете добавить новые параметры, которых нет в конфиге, или удалить существующие:
> python3 app.py +params.reg_alpha=0.5 > python3 app.py ~params.n_estimators
Группы конфигураций
Предположим, что мы изобрели ряд фичей для нашей задачи и хотим протестировать разные их комбинации. И тут нам помогут Группы конфигурации. По сути это отдельные конфиги для каждой “подсистемы” вашего приложения.
Изменим структуру проекта, добавим в папку config подпапку для управления параметрами датасета:
config - dataset - ds1.yaml - ds2.yaml - config.yaml app.py df.csv
config.yaml останется прежнем, а вот два новых YAML файла содержит следующую информацию:
#ds1.yaml name: ds1 columns: [age,bp,s1,s2,s5,s6] target: target
#ds2.yaml name: ds2 columns: [sex,bmi,s3,s4] target: target
Также изменим способ загрузки датасета, чтобы брал информацию о колонках из конфига:
# Загружаем данные df = pd.read_csv('df.csv') X = df[cfg['dataset']['columns']] y = df[cfg['dataset']['target']]
К значениям конфига можно обращаться двумя способами:
cfg.dataset.image.channels
cfg['dataset']['classes']
Далее при запуске нам нужно указать какой конфиг использовать:
> python3 app.py dataset=ds1 dataset: name: ds1 columns: - age - bp - s1 - s2 - s5 - s6 target: target model: lgbm params: learning_rate: 0.05 n_estimators: 100 max_depth: 9 [2022-10-31 22:15:28,575][main][INFO] - MSE: 3580.19
Как вы можете видеть, хотя наши конфиги разделены, в приложение приходит один целостный конфиг.
Но каждый раз запускать может быть неудобно, поэтому вы можете в основном конфиге определить дефолтные модули:
#config.yaml defaults: - dataset: ds1 model: lgbm params: learning_rate: 0.05 n_estimators: 100 max_depth: 9
Аналогичным образом мы можем определить настройки для других типовых “модулей”, для машинного обучения:
-
Разные архитектуры моделей (AlexNet, ResNet50).
-
Разные наборы данных.
-
Разные оптимизаторы.
-
Разные планировщики.
-
Разные способы заполнения пропусков.
-
Разные функции потерь.
-
И куча всего другого…
Multirun
Допустим мы таким образом наизобретали кучу “модулей”. Запускать каждый из них по отдельности, перебирая всевозможные варианты − утомительно. Поэтому гидра может это делать в автоматическом режиме с помощью функции multirun:
> python3 app.py -m dataset=ds1,ds2 [2022-10-31 22:48:22,886][HYDRA] Launching 2 jobs locally [2022-10-31 22:48:22,886][HYDRA] #0 : dataset=ds1 ... [2022-10-31 22:48:25,233][main][INFO] - MSE: 3580.19 [2022-10-31 22:48:25,234][HYDRA] #1 : dataset=ds2 ... [2022-10-31 22:48:25,656][main][INFO] - MSE: 3414.61
Здесь мы попросили перебрать два конфига для датасета. Каждый из них будет объединен с базовым конфигом и передан на вход приложению. Если бы у нас были еще конфиги, например для моделей, то гидра бы сформировала и выполнила все их возможные комбинации.
В отличии от одиночного запуска при мульти-запуске логи сохраняются в папке multirun, в которой помимо даты и времени также создается отдельная подпапка (с порядковым номером) для каждого отдельного задания.

Базовая функция multirun запускает задания локально и последовательно, но используя специальные плагины вы можете запускать код параллельно или даже удаленно на нескольких узлах:
Инициализация объектов
Усложним задачу. Допустим мы хотим попробовать различные модели. Но мы не хотим это как-то обыгрывать в коде − мы просто хотим задавать необходимый класс в конфиге. Добавим в нашу структуру новую папку model и два конфига под две модели:
config - dataset - ds1.yaml - ds2.yaml - model - catboost.yaml - lgbm.yaml - config.yaml app.py df.csv
Содержимое новых YAML-файлов:
#catboost.yaml _target_: catboost.CatBoostRegressor learning_rate: 0.05 n_estimators: 100 max_depth: 9 silent: True
#lgbm.yaml _target_: lightgbm.LGBMRegressor learning_rate: 0.05 n_estimators: 100 max_depth: 9 verbose: -1
Обратите внимание на специальный ключ _target_ в котором указываем класс, который хотим задействовать для создания модели (при этом отдельно импортировать этот класс в приложении не нужно). Все остальные значения, указанные на том же уровне, будут использованы как параметры при инициализации класса.
Поменяем код обучения модели:
... from hydra.utils import instantiate ... # Обучение модели model = instantiate(cfg['model']) model.fit(X_train, y_train) ...
Здесь мы использовали специальную функцию hydra.utils.instantiate, через которую создали экземпляр модели, заданный в конфиге. В остальном код остался прежнем.
Укажем дефолтную модель в базовом конфиге и уберем оттуда все предыдущие настройки модели:
defaults: - dataset: ds1 - model: lgbm
Запускаем:
> python3 app.py dataset: name: ds1 columns: - age - bp - s1 - s2 - s5 - s6 target: target model: target: lightgbm.LGBMRegressor learning_rate: 0.05 n_estimators: 100 max_depth: 9 [2022-11-01 11:30:49,648][main][INFO] - MSE: 3580.19
Тут гидра взяла дефолтные конфиги. И попробуем запустить мультиран на всем, что у нас есть:
> python3 app.py -m dataset=ds1,ds2 model=catboost,lgbm [2022-11-01 12:33:16,334][HYDRA] Launching 4 jobs locally [2022-11-01 12:33:16,334][HYDRA] #0 : dataset=ds1 model=catboost ... [2022-11-01 12:33:17,089][main][INFO] - MSE: 3295.95 [2022-11-01 12:33:17,090][HYDRA] #1 : dataset=ds1 model=lgbm ... [2022-11-01 12:33:17,652][main][INFO] - MSE: 3580.19 [2022-11-01 12:33:17,653][HYDRA] #2 : dataset=ds2 model=catboost ... [2022-11-01 12:33:17,863][main][INFO] - MSE: 3330.02 [2022-11-01 12:33:17,864][HYDRA] #3 : dataset=ds2 model=lgbm ... [2022-11-01 12:33:18,124][main][INFO] - MSE: 3414.61
Как видите, гидра перебрала все возможные комбинации.
Jupyter Notebook
Иногда нам может понадобиться загрузить наш конф-файл отдельно, а не передавать в основную функцию программы. Например, в ячейке Jupyter Notebook. Делается это посредством Compose API − альтернативного способа загрузки файлов конфигурации. Делается это примерно так:
from hydra import initialize, compose from omegaconf import OmegaConf with initialize(version_base=None, config_path="cloud_app/conf"): cfg = compose(overrides=["+db=mysql"]) print(cfg)
Более подробно читайте в документации: https://hydra.cc/docs/advanced/compose_api/
Прочее
В этой статье содержится лишь часть возможностей гидры. С остальными вы сможете ознакомится в документации. Например:
-
Автоматическое закрытие вкладок: https://hydra.cc/docs/0.11/tutorial/tab_completion/
-
Тонкая настройка логирования: https://hydra.cc/docs/0.11/configure_hydra/logging/
-
Добавить конфиги гидры в ваш питон пакет: https://hydra.cc/docs/advanced/app_packaging/
-
Настройка рабочего каталога: https://hydra.cc/docs/configure_hydra/intro/
-
И много чего еще…
—————
Код из статьи: https://github.com/slivka83/article/tree/main/hydra
ссылка на оригинал статьи https://habr.com/ru/post/696820/
Добавить комментарий