diff --git a/README.md b/README.md index fb81a7c..b4fdd7e 100644 --- a/README.md +++ b/README.md @@ -10,3 +10,4 @@ - [Компонент MLComponent](./pages/mlcmp.md) - [Компонент ExperimentPipeline](./pages/pipeline.md) - [Терминология](./pages/terminology.md) + - [Пайплайны MLDev](./pages/complex-pipeline.md) diff --git a/pages/complex-pipeline.md b/pages/complex-pipeline.md new file mode 100644 index 0000000..e5bdc75 --- /dev/null +++ b/pages/complex-pipeline.md @@ -0,0 +1,260 @@ +# Сложный пайплайн (mldev) + +[MLDev](https://gitlab.com/mlrep/mldev) - программное обеспечение, упрощающее запуск воспроизводимых вычислительных экспериментов. + +С помощью MLDev можно создавать более сложные вычислительные эксперименты (пайплайны) для использования на фреймворке, а также более удобно работать с обычными пайплайнами. + +Принцип работы с фреймворком при помощи MLDev: + +1. Разработчик создаёт манифест эксперимента, аналогичный манифесту пайплайна. +2. На основе манифеста эксперимента с помощью mldev автоматически генерируется манифест пайплайна и собираются docker-образы. +3. Разработчик добавляет сгенерированный манифест пайплайна и docker-образы к модулю как при обычном создании пайплайна. + +## Подготовка элементов mldev + +В примере далее рассматривается следующая структура файлов, относящихся к MLDev. + +``` +experiments +├── .mldev +│   ├── config.yaml +│   └── stages.py +├── experiment-unip-pipeline.yaml +└── unip-pipeline.yaml +``` + +- `config.yaml` - настройки MLDev, такие как параметры запуска, уровень логирования и шаблон названия docker-образов. +- `stages.py` - загрузка используемых компонентов MLDev с помощью выражения `import`. +- `experiment-unip-pipeline.yaml` - mldev-пайплайн сборки эксперимента, запускаемый разработчиком. +- `unip-pipeline.yaml` - основной пайплайн, используется пайплайном сборки для построения вычислительного эксперимента. + +## config.yaml + +Пример содержимого `config.yaml`: + +```yaml +logger: + level: DEBUG +extras: + base: mldev.experiment_objects + containers: mldev_containers.containers +environ: + IMAGE_TAG_TEMPLATE: 'platform-reg.stratpro.hse.ru/myproject/${name}:${revision}' + UNIP_MLDEV_PIPELINE_OUTPUT: ./build/pipelines + EXPERIMENT_FILE: ./experiments/unip-pipeline.yaml + FORCE_RUN: True +``` + +- `logger.level` - уровень логирования, `DEBUG`, `INFO` или `ERROR`. +- `extras` - дополнительные модули MLDev, необходимые для взаимодействия с фреймворком. +- `environ` - переменные окружения + - `IMAGE_TAG_TEMPLATE` - шаблон названия образа Docker. `${name}` будет автоматически заменено на название этапа из файла эксперимента, а `${revision}` - на короткий хэш последнего коммита в репозитории проекта + - `UNIP_MLDEV_PIPELINE_OUTPUT` - путь, куда будет сохранён сгенерированный манифест пайплайна для фреймворка + - `EXPERIMENT_FILE` - файл с вычислительным экспериментом + - `FORCE_RUN` - параметр, заставляющий MLDev собирать эксперимент заново, даже если не найдено изменений с прошлой сборки + + +## stages.py + +Пример файла `stages.py`: + +```python +from mldev_containers import ContainerStage, StageCode, StageVar +from unip.mldev_pipeline.unip_pipeline import UnipPipeline +from unip.mldev_pipeline.dataloader import StageSrc +``` + +- `UnipPipeline`, `StageCode`, `StageVar`, `ContainerStage` - классы компонентов, необходимых для работы с пайплайнами +- `StageSrc` - класс подключения модуля данных + +Аналогично модулю данных в примере, для подключения других специализированных этапов, таких как подключение к суперкомпьютеру cHARISMa и автоматическая генерация отчёта об эксперименте, необходимо добавлять соответствующие классы в `stages.py`. + + +## experiment-unip-pipeline.yaml + +В этом файле определяется небольшой вычислительный эксперимент MLDev, в результате которого должен быть собран полноценный эксперимент из файла `unip-pipeline.yaml`. + +Здесь определяются параметры запуска и переменные окружения полноценного эксперимента, а также добавляются вспомогательные команды. + +Пример файла `experiment-unip-pipeline.yaml` + +```yaml +pipeline: !GenericPipeline + runs: + - !BasicStage + name: experiment + env: + PYTHONPATH: './experiments/.mldev' + MLDEV_CONFIG_PATH: './experiments/.mldev/config.yaml' + PIP_EXTRA_INDEX_URL: '${env.PIP_EXTRA_INDEX_URL}' + script: + - mkdir -p ./build/containers + - mkdir -p ./build/pipelines + - rm -rf ./build/containers/* + - rm -rf ./build/pipelines/* + - mldev run pipeline -f ./experiments/unip-pipeline.yaml + +``` + +- `pipeline: !GenericPipeline` - указывает, что это объект класса `GenericPipeline` - стандартный пайплайн библиотеки MLDev, не относящийся к фреймворку. +- `runs` - список этапов, которые будут выполнены в ходе выполнения пайплайна GenericPipeline +- `!BasicStage` - указывает, что текущий этап - это объект класса `BasicStage`, стандартного класса этапа пайплайна в MLDev. +- `name: experiment` - имя этапа. Не используется далее +- `env:` - переменные окружения, с которыми запускается этап сборки полноценного пайплайна, + - `PYTHONPATH` - чтобы модули MLDev, импортированные в файле `stages.py` были загружены, нужно указать в PYTHONPATH путь к папке с этим файлом. В данном примере, `./experiments/.mldev` + - `MLDEV_CONFIG_PATH` - путь к используемому в полноценном эксперименте файлу настроек MLDev. + - `PIP_EXTRA_INDEX_URL` - дополнительные индексы для установки пакетов Python. Используются для установки пакетов фреймворка, которые не размещены в индексе PyPI. Значение `'${env.PIP_EXTRA_INDEX_URL}'` означает, что переменная окружения `PIP_EXTRA_INDEX_URL` копирует содержимое такой же переменной из среды, которая запускает эксперимент. +- `script` = команды, которые выполяются в текущем этапе `experiment`. В данном примере это создание папок `./build/containers` и `./build/pipelines`, удаление их содержимого и запуск сборки полноценного пайплайна. + + +Подробнее про переменную окружения `PIP_EXTRA_INDEX_URL` и запуск сборки. + +Дополнительные индексы пакетов Python указываются в формате + +```sh +export PIP_EXTRA_INDEX_URL="https://my_username:mypassword_or_token_abcdef1234@platform-forgejo.stratpro.hse.ru/api/packages/my_org/pypi/simple https://my_username:mypassword_or_token_abcdef1234@platform-forgejo.stratpro.hse.ru/api/packages/another_org/pypi/simple" +``` + +Чтобы не сохранять реквизиты доступа в сам репозиторий, используется подход с копированием их из внешней среды. Общий набор команд для сборки эксперимента mldev может быть примерно такой: + +```sh +cd /home/my_username/MLOPS/my_project/ +export PIP_EXTRA_INDEX_URL="https://my_username:mypassword_or_token_abcdef1234@platform-forgejo.stratpro.hse.ru/api/packages/my_org/pypi/simple https://my_username:mypassword_or_token_abcdef1234@platform-forgejo.stratpro.hse.ru/api/packages/another_org/pypi/simple" +source venv/bin/activate +mldev run -f experiments/experiment-unip-pipeline-paper.yaml +``` + +## unip-pipeline.yaml + +Этот файл полностью описывает вычислительный эксперимент, аналогично компоненту `ExperimentPipeline`. + +### UnipPipeline + +Основной объект вычислительного эксперимента, который должен быть определён в файле `unip-pipeline.yaml` - это объект `pipeline:!UnipPipeline` со следующей структурой: + +```yaml +pipeline: !UnipPipeline + name: my-project + namespace: pu-username-pa-appname + variables: + - name: preparation_data + - name: internal_state + - name: user_report + runs: + - *container_stage_prepare + - *container_stage_complete + connected_boxes: + - name: user-box + path: /userdata/ + default: true + mount_s3_box: + s3_box_name: users +``` + +- `name: my-project` - имя, которое будет использовано далее при генерации имени компонента `ExperimentPipeline`. +- `namespace` - пространство имён приложения, аналогично с остальными манифестами компонентов. +- `variables` - список переменных, которые используются во всех этапах, вместе взятых. Аналогично `ExperimentPipeline`. Для каждой переменной помимо поля `name` можно указать поля `path` (точка монтирования в контейнере) и `mountFrom` (DataBox и точка монтирования в хранилище S3). +- `runs` - список этапов пайплайна. В примере - ссылки на отдельно определённые компоненты. +- `connected_boxes` - раздел, полностью аналогичный такому же разделу в `ExperimentPipeline` - определение связанных компонентов `DataBox`, указание пути монтирования по умолчанию и внутреннего имени DataBox, которое используется в этом файле далее. В отличие от `ExperimentPipeline`, где используется camelCase, здесь используется snake_case. + +### ContainerStage + +Отдельный этап пайплайна `UnipPipeline` - это `ContainerStage`. Такой этап соответствует этапу `ExperimentPipeline`. + + +```yaml +container_stage_prepare: !ContainerStage &container_stage_prepare + name: my-prepare + base_image: platform-reg.stratpro.hse.ru/my_org/myproject-prepare:718e2b0 + stage: *stage_prepare + build: + <<: *build + code: *code + env: + MY_CUSTOM_FLAG: "True" + resource_limits: + cpu: "1" + memory: 500M +``` + +- `container_stage_prepare: !ContainerStage &container_stage_prepare`: + - `container_stage_prepare:` - корень объекта этапа + - `!ContainerStage` - класс, на основе которого будет создан этап + - `&container_stage_prepare` - имя, по которому на этот этап можно ссылаться. Это имя используется в `UnipPipeline` +- `my-prepare` - название этапа. Используется как название этапа в `ExperimentPipeline` и в названии Docker-образа +- `base_image` - образ Docker, на основе которого будет создан этап. Это может быть как стандартный образ вида `python:3.11.10-slim-bullseye`, так и образ, загруженный в реестр фреймворка вида `platform-reg.stratpro.hse.ru/my_org/myproject-prepare:718e2b0`. +- `stage: *stage_prepare` - объект вида `BasicStage`, в котором определяются входные, выходные данные и точка входа, запускающая вычисления. +- `build` - параметры сборки Docker-образа, здесь определяются отдельным объектом. +- `code` - объект типа `StageCode`, который определяет устанавливаемые через `requirements.txt` зависимости, а также файл с исходным кодом проекта, добавляемые в контейнер. +- `env` - набор произвольных переменных окружения, которые будут установлены при запуске контейнера. +- `resource_limits` - лимиты ресурсов в терминах Kubernetes, аналогично `ExperimentPipeline`. + +#### build + +В данном определении используется несколько ссылок на внешние объекты. Объект `build` имеет структуру + +```yaml +build_params: &build + root: . + output: build/containers +``` + +Это означает, что корнем сборки Docker-контейнера выступает корень репозитория, а сгенерированные скрипты сборки и Dockerfile-ы появятся в папке `build/containers` в репозитории. + +#### code + +Объект `code` имеет структуру + +```yaml +code: !StageCode &code + requirements: ./requirements.txt + src: + - ./myproject/ + - ./my_static_config.json + - ./.mldev/ + - ./experiments/unip-pipeline-paper.yaml +``` + +- Это объект типа `StageCode`, на который далее можно ссылаться по имени `code`. +- `requirements` - файл с зависимостями, который будет скопировать в контейнер и использован для установки зависимостей +- `src` - список файлов или папок, которые будут скопированы в рабочую директорию контейнера. По умолчанию это `/unip`. + +Объект `StageCode` предполагает единый проект, даже если в его рамках запускаются разные файлы. Если несколько контейнеров объединяются в один пайплайн с разными `StageCode`, то в результате сгенерируется несколько объектов `ExperimentPipeline`, составляющих цепочку запуска. + +#### BasicStage + +Объект `BasicStage` определяет входные, выходные данные этапа пайплайна, а также точку входа. + +```yaml +stage_prepare: !BasicStage &stage_prepare + name: stage_prepare + inputs: + - !StageVar + name: preparation_data + path: ./data/preparation + outputs: + - !StageVar + name: internal_state + script: + - python src/prepare.py +``` + +- `stage_prepare: !BasicStage &stage_prepare` - аналогично предыдущим компонентам, объект `stage_prepare` класса `BasicStage`, на который можно ссылаться как `stage_prepare`. +- `name` - название этапа +- `inputs` - список входных переменных + - `!StageVar` - переменная типа `StageVar`. Это обычный тип переменных по умолчанию, аналогичный переменным `ExperimentPipeline`. + - `name` - название переменной, используется далее в пайплайне и в контейнере. + - `path` - путь монтирования переменной внутри контейнера. Если передан относительный путь, он рассчитывается от рабочей директории контейнера - `/unip`. +- `outputs` - аналогично `inputs` +- `script` - точка входа в этап эксперимента. + - При этом точкой входа в контейнер будет команда MLDev `mldev run stage_prepare -f /unip/experiments/unip-pipeline.yaml`, а уже запущенный ей процесс выполнит команду из поля `script`. + +## MLOps-модули + +MLOps-модули фреймворка и некоторые другие дополнительные возможности работают за счёт замены стандартных классов `ContainerStage`, `BasicStage` и `StageVar` на свои варианты. + +Например: + +- `CharismaStage` вместо `ContainerStage` позволяет отправить вычислительную задачу на суперкомпьютер cHARISMa. +- `StageSrc` вместо `StageVar` позволяет использовать данные из модуля данных. +- `Report` вместо `BasicStage` позволяет сгенерировать отчёт о проведённом вычислительном эксперименте.