mldev, харизма, связанные пайплайны

This commit is contained in:
Georgii Zhulikov 2025-04-24 15:57:15 +03:00
parent f873584c15
commit 8ccdaa87c3
3 changed files with 281 additions and 24 deletions

View file

@ -90,17 +90,34 @@ my_container_stage: !ContainerStage &my_container_stage
<<: *build
code: *code
resource_limits:
time: 0-01:00:00 # ЧЧ:ММ:СС
time: 0-01:00:00 # Д-ЧЧ:ММ:СС
nodes: 1
cpus: 4
gpus: 1
node_type: type_a # или type_b, type_c, ...
my_charisma_stage: !CharismaStage &charisma_stage_train
my_charisma_stage: !CharismaStage &my_charisma_stage
name: my-charisma-stage
sif_input: charisma_sif
stage: *my_container_stage
pipeline: !UnipPipeline
name: my-project
namespace: pu-username-pa-appname
variables:
- name: my_data
- name: my_result
- name: charisma_sif
runs:
- *my_charisma_stage
connected_boxes:
- name: user-box
path: /userdata/
default: true
mount_s3_box:
s3_box_name: users
```
## Сборка MLDev-пайплайна
@ -123,8 +140,24 @@ environ:
- Для доступа к пайплайну нужен соответствующий API-компонент. При создании API-компонента в пространстве имён приложения также создаются несколько объектов Secret с разными видами реквизитов. Их имена генерируются автоматически на основе имени API-компонента.
После этого сборку можно начать такой же командой, как для обычного MLDev эксперимента: `mldev run -f experiments/experiment-unip-pipeline.yaml`
После этого сборку можно начать такой же командой, как для обычного MLDev эксперимента: `mldev run -f experiments/experiment-unip-pipeline.yaml`. Сборка SIF-образа может занимать много вычислительных ресурсов и оперативной памяти.
Сборка SIF-образа может занимать много вычислительных ресурсов и оперативной памяти.
После завершения сборки в корневой папке проекта должна появиться следующая структура:
```
/build
├── containers
│   ├── my-charisma-stage.dockerfile
│   ├── my-charisma-stage.dockerignore
│   └── my-charisma-stage.sh
├── pipelines
│   └── my-project-qzv828.yaml
└── sif
   └── platform-reg.stratpro.hse.ru_my_lab_my-stage_0d00739.sif
```
Как и для обычного MLDev-пайплайна, создаются наборы файлов сборки контейнеров для каждого этапа в `build/containers/` и сгенерированный манифест `ExperimentPipeline` в `build/pipelines/`.
Помимо этого, собранный SIF-контейнер помещается в `build/sif/`.
Если в пайплайне предполагается несколько этапов, например, "обучить модель на суперкомпьютере, а затем построить отчёт о результатах обучения на обычных вычислительных ресурсах фреймворка", пайплайн будет разбит на [несколько частей](./split-pipeline.md). Этап с `CharismaStage` будет выделен в одну часть, а остальные этапы - в другую.

View file

@ -10,7 +10,21 @@
2. На основе манифеста эксперимента с помощью mldev автоматически генерируется манифест пайплайна и собираются docker-образы.
3. Разработчик добавляет сгенерированный манифест пайплайна и docker-образы к модулю как при обычном создании пайплайна.
## Подготовка элементов mldev
- [Подготовка элементов mldev](#подготовка-элементов-mldev)
- [config.yaml](#configyaml)
- [stages.py](#stagespy)
- [experiment-unip-pipeline.yaml](#experiment-unip-pipelineyaml)
- [unip-pipeline.yaml](#unip-pipelineyaml)
- [UnipPipeline](#unippipeline)
- [ContainerStage](#containerstage)
- [build](#build)
- [code](#code)
- [BasicStage](#basicstage)
- [MLOps-модули](#mlops-модули)
# Подготовка элементов mldev
В примере далее рассматривается следующая структура файлов, относящихся к MLDev.
@ -107,23 +121,6 @@ pipeline: !GenericPipeline
- `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`.
@ -203,7 +200,7 @@ build_params: &build
#### code
Объект `code` имеет структуру
Объект `StageCode` имеет структуру
```yaml
code: !StageCode &code
@ -249,7 +246,63 @@ stage_prepare: !BasicStage &stage_prepare
- `script` - точка входа в этап эксперимента.
- При этом точкой входа в контейнер будет команда MLDev `mldev run stage_prepare -f /unip/experiments/unip-pipeline.yaml`, а уже запущенный ей процесс выполнит команду из поля `script`.
## MLOps-модули
# Сборка пайплайна
## Запуск сборки
Чтобы запустить сборку пайплайна нужно выполнить следующие условия:
1. В `PATH` доступна команда `mldev`. Например, в используемой виртуальной среде установлен пакет Python `mldev`.
2. В переменной окружения `PIP_EXTRA_INDEX_URL` указаны ссылки на используемые реестры пакетов Python фреймворка с указанием рекизитов.
Дополнительные индексы пакетов 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
```
После завершения сборки в корневой папке проекта должна появиться следующая структура:
```
/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` поддерживает последовательный запуск нескольких пайплайнов - [подробнее здесь](./split-pipeline.md).
Во время сборки эксперимента через MLDev этапы с разными объектами `StageCode` считаются достаточно сильно отличными друг от друга. Такие этапы разделяются в отдельные объекты `ExperimentPipeline` и связываются через `continueWith`.
# MLOps-модули
MLOps-модули фреймворка и некоторые другие дополнительные возможности работают за счёт замены стандартных классов `ContainerStage`, `BasicStage` и `StageVar` на свои варианты.

171
pages/split-pipeline.md Normal file
View file

@ -0,0 +1,171 @@
# Последовательный запуск пайплайнов
Несколько компонентов `ExperimentPipeline` возможно соединить друг с другом в последовательную серию запусков, где запуск первого пайплайна предполагает последовательный запуск второго.
Если два пайплайна связаны таким образом, у пользователя появляется два варианта запуска:
- Запустить цепочку из первого и второго пайплайна вместе
- Запустить только второй пайплайн
Для соединения двух пайплайнов необходимо:
1. Добавить в манифест `ExperimentPipeline` первого пайплайна пункт `continueWith`.
2. Создать совмещённый `APIComponent` запуска цепочки, который будет соответствовать запуску первого пайплайна.
При этом второй пайплайн создаётся обычным способом, и может запускаться отдельно от цепочки.
## Пример ExperimentPipeline
Например, в модуле есть два пайплайна. Пайплайн `part-1` содержит этап обучения модели машинного обучения. Пайплайн `part-2` содержит этап построения отчёта по обученной модели.
Первый пайплайн:
```yaml
apiVersion: "unified-platform.cs.hse.ru/v1"
kind: ExperimentPipeline
metadata:
name: part-1
namespace: pu-username-pa-appname
spec:
vars:
- name: data
- name: model
stages:
- name: train
image:
existingImageName: platform-reg.stratpro.hse.ru/my_org/myproject-train:718e2b0
inputs:
- name: data
outputs:
- name: model
entryPoint:
cmd:
- python
- main.py
resourceLimits:
cpu: "500m"
memory: "512M"
connectedBoxes:
- name: user-box
path: /userdata/
default: true
mountS3Box:
s3BoxName: users
continueWith:
name: part-2
```
Второй пайплайн:
```yaml
apiVersion: "unified-platform.cs.hse.ru/v1"
kind: ExperimentPipeline
metadata:
name: part-2
namespace: pu-username-pa-appname
spec:
vars:
- name: report_document
- name: trained_model
stages:
- name: report
image:
existingImageName: platform-reg.stratpro.hse.ru/my_org/myproject-report:718e2b0
inputs:
- name: trained_model
outputs:
- name: report_document
resourceLimits:
cpu: "1"
memory: "2G"
connectedBoxes:
- name: user-box
path: /userdata/
default: true
mountS3Box:
s3BoxName: users
```
Пункт `continueWith` в первом пайплайне указывает, что после завершения работы первого пайплайна автоматически вызывается второй.
## Пример APIComponent
Для запуска пайплайна нужно передать все входные данные в запросе. Но если второй пайплайн вызывается сам автоматически, то нет запроса на его вызов. Поэтому все входные данные для всех пайплайнов цепочки должны быть переданы в изначальном запросе запуска первого пайплайна.
Сначала, пример `APIComponent` второго пайплайна:
```yaml
apiVersion: "unified-platform.cs.hse.ru/v1"
kind: APIComponent
metadata:
name: api-part-2
namespace: pu-username-pa-appname
spec:
published: true
experimentPipeline:
name: part-2
restfulApi:
auth:
basic:
credentials: appname-apis-cred
identityPassThrough: true
apiSpec:
inputs:
- name: trained_model
type:
datatypes: [ "FILE" ]
contentTypes: [ "pth" ]
outputs:
- name: report_document
type:
datatypes: [ "FILE" ]
contentTypes: [ "text/html" ]
```
Для второго пайплайна `APIComponent` создаётся как для обычного независимого пайплайна и содержит данные, которые пользователь передаёт на вход и получает на выход.
`APIComponent` первого пайплайна содержит данные как для себя, так и для второго пайплайна:
```yaml
apiVersion: "unified-platform.cs.hse.ru/v1"
kind: APIComponent
metadata:
name: api-part-1
namespace: pu-username-pa-appname
spec:
published: true
experimentPipeline:
name: part-1
restfulApi:
auth:
basic:
credentials: appname-apis-cred
identityPassThrough: true
apiSpec:
inputs:
- name: data
description: "Набор данных для обучения."
type:
datatypes: [ "FILE" ]
contentTypes: [ "text/csv" ]
- name: trained_model
description: "Готовая модель и её конфигурация."
type:
datatypes: [ "FILE" ]
contentTypes: [ "pth" ]
outputs:
- name: model
description: "Готовая модель и её конфигурация."
type:
datatypes: [ "FILE" ]
contentTypes: [ "pth" ]
- name: report_document
type:
datatypes: [ "FILE" ]
contentTypes: [ "text/html" ]
```
Здесь `trained_model` и `report_document` - это переменные второго пайплайна, которые не входят в первый. При получении запроса на запуск, фреймворк анализирует, какую последовательность пайплайнов нужно запустить, и отделяет переменные из `APIComponent` второго пайплайна от общего списка переменных.
На практике, если нужно передать данные из первого пайплайна во второй, для этого нужно создать две переменные с разными именами, и в запросе на запуск передать в них один и тот же путь. Тогда первый пайплайн запишет по этому пути выходные данные, а второй сможет их прочитать. В примере выше такими переменными являются `model` и `trained_model`.