documentation/pages/complex-pipeline.md

21 KiB
Raw Permalink Blame History

Сложный пайплайн (mldev)

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:

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:

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

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, удаление их содержимого и запуск сборки полноценного пайплайна.

unip-pipeline.yaml

Этот файл полностью описывает вычислительный эксперимент, аналогично компоненту ExperimentPipeline.

UnipPipeline

Основной объект вычислительного эксперимента, который должен быть определён в файле unip-pipeline.yaml - это объект pipeline:!UnipPipeline со следующей структурой:

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.

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 имеет структуру

build_params: &build
  root: .
  output: build/containers

Это означает, что корнем сборки Docker-контейнера выступает корень репозитория, а сгенерированные скрипты сборки и Dockerfile-ы появятся в папке build/containers в репозитории.

code

Объект StageCode имеет структуру

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 определяет входные, выходные данные этапа пайплайна, а также точку входа.

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.

Сборка пайплайна

Запуск сборки

Чтобы запустить сборку пайплайна нужно выполнить следующие условия:

  1. В PATH доступна команда mldev. Например, в используемой виртуальной среде установлен пакет Python mldev.
  2. В переменной окружения PIP_EXTRA_INDEX_URL указаны ссылки на используемые реестры пакетов Python фреймворка с указанием рекизитов.

Дополнительные индексы пакетов Python указываются в формате

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 может быть примерно такой:

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

После завершения сборки в корневой папке проекта должна появиться следующая структура:

/build
├── containers
│   ├── my-complete.dockerfile
│   ├── my-complete.dockerignore
│   ├── my-complete.sh
│   ├── my-prepare.dockerfile
│   ├── my-prepare.dockerignore
│   └── my-prepare.sh
└── pipelines
    └── my-project-qzv828.yaml

Здесь в containers хранятся файлы, используемые для сборки контейнеров отдельных этапов, а в pipelines - сгенерированный манифест ExperimentPipeline. Сами собранные образы этапов должны быть доступны в Docker, например, видны в команде docker images.

Подключение к базовому модулю

Чтобы подключить сгенерированный манифест ExperimentPipeline к фреймворку, следует его переименовать, изменить в содержимом манифеста поле name на желаемое название пайплайна и поместить манифест в папку app в репозитории.

Компонент APIComponent не генерируется при сборке пайплайна MLDev, его нужно создать вручную.

Собранные образы Docker нужно загрузить в репозиторий образов в Harbor.

Разделение на несколько пайплайнов

ExperimentPipeline поддерживает последовательный запуск нескольких пайплайнов - подробнее здесь.

Во время сборки эксперимента через MLDev этапы с разными объектами StageCode считаются достаточно сильно отличными друг от друга. Такие этапы разделяются в отдельные объекты ExperimentPipeline и связываются через continueWith.

MLOps-модули

MLOps-модули фреймворка и некоторые другие дополнительные возможности работают за счёт замены стандартных классов ContainerStage, BasicStage и StageVar на свои варианты.

Например:

  • CharismaStage вместо ContainerStage позволяет отправить вычислительную задачу на суперкомпьютер cHARISMa.
  • StageSrc вместо StageVar позволяет использовать данные из модуля данных.
  • Report вместо BasicStage позволяет сгенерировать отчёт о проведённом вычислительном эксперименте.