Machine learning pipeline — основы. Cookiecutter и Hydra

В курсах по Data Science домашние работы и проекты делаются в Jupyter Notebooks и студентов не учат писать пайплайны. Дело в том, что работа в Jupyter Notebooks не смотря на удобство несет в себе в том числе и недостатки. Например, вы строите несколько типов моделей с несколькими вариантами заполнения пропусков (среднее, медиана), генерируете набор feature engineering и применяете разные варианты разбиения выборки.

Можно разместить весь этот код в один Jupyter Notebooks и логгировать метрики и конфиги. Код получится громоздкий и не поворотливый. Для запуска экспериментов надо будет или перескакивать или комментировать ячейки, которые не нужно запускать.

Для решения этих проблем рекомендую использовать pipeline для автоматизации рабочих процессов машинного обучения. Основная цель создания пайплайна — это контроль. Хорошо организованный пайплайн делает реализацию более гибкой.

По ссылке можно ознакомиться с примером кода для пайплайна. Структура следующая:

  • config — папка с файлами конфигурации в формате yaml
  • data — папка с файлами данных
  • external — данные из внешних источников
  • interim — разделение данных
  • raw — необработанные данные
  • processed — данные после обработки
  • experiments — конфиги, логи результатов модели
  • models — папка для хранения моделей
  • src — исходный код. Содержит код для работы с данными, оценки моделей, вспомогательные функции, обучения моделей, работы с признаками

Работа с пайплайном состоит из следующих этапов

  1. Создание конфигов
  2. Подготовка признаков
  3. Разбиение на выборки
  4. Обучение модели
  5. Измерение качества модели

Cookiecutter Data Science

Давайте теперь попробуем посмотреть на разные практики разработки пайплайнов.

Первое, что я бы советовал это попробовать структуру проекта: cookiecutter

Эта структура достаточно логичная, стандартизированная и гибкая. Всё что вам нужно, установить его и стартануть проект:

pip install cookiecutter
cookiecutter -c v1 https://github.com/drivendata/cookiecutter-data-science

Структура каталогов вашего нового проекта выглядит следующим образом:

├── LICENSE
├── Makefile           <- Makefile with commands like `make data` or `make train`
├── README.md          <- The top-level README for developers using this project.
├── data
│   ├── external       <- Data from third party sources.
│   ├── interim        <- Intermediate data that has been transformed.
│   ├── processed      <- The final, canonical data sets for modeling.
│   └── raw            <- The original, immutable data dump.
│
├── docs               <- A default Sphinx project; see sphinx-doc.org for details
│
├── models             <- Trained and serialized models, model predictions, or model summaries
│
├── notebooks          <- Jupyter notebooks. Naming convention is a number (for ordering),
│                         the creator's initials, and a short `-` delimited description, e.g.
│                         `1.0-jqp-initial-data-exploration`.
│
├── references         <- Data dictionaries, manuals, and all other explanatory materials.
│
├── reports            <- Generated analysis as HTML, PDF, LaTeX, etc.
│   └── figures        <- Generated graphics and figures to be used in reporting
│
├── requirements.txt   <- The requirements file for reproducing the analysis environment, e.g.
│                         generated with `pip freeze > requirements.txt`
│
├── setup.py           <- makes project pip installable (pip install -e .) so src can be imported
├── src                <- Source code for use in this project.
│   ├── __init__.py    <- Makes src a Python module
│   │
│   ├── data           <- Scripts to download or generate data
│   │   └── make_dataset.py
│   │
│   ├── features       <- Scripts to turn raw data into features for modeling
│   │   └── build_features.py
│   │
│   ├── models         <- Scripts to train models and then use trained models to make
│   │   │                 predictions
│   │   ├── predict_model.py
│   │   └── train_model.py
│   │
│   └── visualization  <- Scripts to create exploratory and results oriented visualizations
│       └── visualize.py
│
└── tox.ini            <- tox file with settings for running tox; see tox.readthedocs.io

Для своих проектов вы можете немного переделал структуру, например: мне в проектах Computer Vision не нужна папки src/features, reports и references.

Более подробно можно почитать тут:

Добавляем Hydra

В этой части заметки мы поговорим библиотеки для работы конфигурациями для проектов машинного обучения — Hydra.

В чем собственно проблема и почему я стал использовать Hydra? При запуске Python скриптов добавляют много аргументов, хотя иногда их можно и сгруппировать. Вот пример такого скрипта:

parser.add_argument('data', metavar='DIR',
                    help='path to dataset')
parser.add_argument('-a', '--arch', metavar='ARCH', default='resnet18',
                    choices=model_names,
                    help='model architecture: ' +
                        ' | '.join(model_names) +
                        ' (default: resnet18)')
parser.add_argument('-j', '--workers', default=4, type=int, metavar='N',
                    help='number of data loading workers (default: 4)')
parser.add_argument('--epochs', default=90, type=int, metavar='N',
                    help='number of total epochs to run')
parser.add_argument('--start-epoch', default=0, type=int, metavar='N',
                    help='manual epoch number (useful on restarts)')
parser.add_argument('-b', '--batch-size', default=256, type=int,
                    metavar='N',
                    help='mini-batch size (default: 256), this is the total '
                         'batch size of all GPUs on the current node when '
                         'using Data Parallel or Distributed Data Parallel')
parser.add_argument('--lr', '--learning-rate', default=0.1, type=float,
                    metavar='LR', help='initial learning rate', dest='lr')
parser.add_argument('--momentum', default=0.9, type=float, metavar='M',
                    help='momentum')
parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float,
                    metavar='W', help='weight decay (default: 1e-4)',
....

Распространенным решением, позволяющим контролировать растущую сложность, является переход на файлы конфигураций. Файлы конфигурации могут быть иерархическими и могут помочь уменьшить сложность кода, определяющего аргументы командной строки. Но и у них есть свои недостатки.

Например:

  • Во время экспериментов вам может понадобиться запустить приложение с различными параметрами конфигурации. Сначала вы можете просто изменять файл конфигурации перед каждым запуском, но вскоре вы поймете, что трудно отслеживать изменения, связанные с каждым запуском.
  • Файлы конфигурации становятся монолитными. Но если вы, например, хотите, чтобы ваш код использовал разные параметры конфигурации, скажем, один для набора данных ImageNet и один для набора данных CIFAR-10, у вас есть два варианта: поддерживать два файла конфигурации или поместить оба параметра в один файл конфигурации и каким-то образом используйте только то, что вам нужно во время выполнения.

Ну, а решение всех вышеперечисленных неудобств — Hydra.

Hydra — позволяет создавать композицию конфигураций. Композиция может работает как с файлом конфигурации, так и в командной строке. При этом всё в составленной конфигурации также может быть переопределено через командную строку.

Пример использования:

Файл conf/config.yaml

defaults:
  - dataset: cifar10

Файл conf/dataset/imagenet.yaml

dataset:
  name: imagenet
  path: /datasets/imagenet

Файл app.py

import hydra
from omegaconf import DictConfig


@hydra.main(config_path="conf/config.yaml")
def my_app(cfg: DictConfig) -> None:
    ...


if __name__ == "__main__":
    my_app()

При запуске будет использоваться параметр dataset по умолчанию. Но вы так же передать параметр и консоле:

 python app.py dataset.path = /datasets/cifar10

Еще одна крутая фишка: multirun — это возможность Hydra запускать вашу функцию несколько раз, каждый раз создавая новый объект конфигурации. Это очень удобно для проверки параметров без написания дополнительных функций. Например, мы можем просмотреть все 4 комбинации (2 набора данных X 2 оптимизатора):

python app.py —multirun dataset= imagenet, cifar10 optimizer=adam, nesterov

А так это выглядит у меня в проекте

hydra config folders

Чтобы узнать больше о Hydra предлагаю почитать и посмотреть:

Заключение

В этой мини заметке я постарался описать подхода к разработке пайплайнов, проблемы с конфигами, с которым сталкиваешься при написании пайплайнов и часть функций, которые предлагает Hydra.

Share it

Если вам понравилась заметка - подписывайтесь на мой канал в телеграме https://t.me/renat_alimbekov или вы можете поддержать меня Become a Patron!


Интересные записи в этой рубрике: