documentation/pages/pipeline.md

375 lines
23 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ExperimentPipeline
- [ExperimentPipeline](#experimentpipeline)
- [Переменные](#переменные)
- [Взаимодействие кода модуля с фреймворком](#взаимодействие-кода-модуля-с-фреймворком)
- [Манифест ExperimentPipeline](#манифест-experimentpipeline)
- [Манифест APIComponent отдельного пайплайна](#манифест-apicomponent-отдельного-пайплайна)
- [Манифест APIComponent пайплайнов в целом](#манифест-apicomponent-пайплайнов-в-целом)
- [Изменение пути монтирования отдельных переменных (необязательно).](#изменение-пути-монтирования-отдельных-переменных-необязательно)
Компонент ExperimentPipeline позволяет создать сервис для асинхронной обработки запросов пользователей.
ExperimentPipeline подходит для вычислительных задач, для которых выполняется хотя бы один из критериев:
- Требуют много вычислительных ресурсов (от нескольких секунд времени и больше, большие объёмы данных)
- Не могут быть оформлены как Python-функция
- Используют MLOps-модули
Для добавления ExperimentPipeline к модулю разработчику нужно:
- Добавить в модуль манифест YAML компонента ExperimentPipeline
- Добавить в модуль манифест YAML компонента APIComponent для обращения к ExperimentPipeline
- Добавить в модуль манифест YAML компонента APIComponent для работы с пайплайнами в целом (один на все пайплайны)
Также в модуле уже должен присутствовать APIComponent для работы с файлами и хотя бы один компонент DataBox.
Общий принцип работы пайплайнов следующий.
1. Пользователь отправляет запрос с путями к входным и выходным данным в файловом хранилище.
2. Пользователь получает ответ, содержащий идентификатор запуска, по которому можно отслеживать статус выполнения пайплайна.
3. Создаётся и запускается контейнер Docker.
4. Файловое хранилище монтируется к файловой системе контейнера согласно полям манифеста ExperimentPipeline
5. Указанные в запросе пути преобразуются в пути в локальной файловой системе и передаются в контейнер как переменные окружения.
6. Запускается расчёт, который считывает входные данные из локальных путей, переданных в переменных окружения.
7. Результат расчёта записывается в локальный путь, считанный из переменной окружения.
8. Пользователь периодически проверяет статус расчёта. Если расчёт завершён, его результаты можно получить через запросы к файловому хранилищу.
## Переменные
```mermaid
graph TB
request["Запрос"]
S3[("Хранилище S3")]
var1[/"var1"/]
var2[/"var2"/]
request --> var1
request --> var2
S3 --> var1
S3 --> var2
subgraph stage1
UNIP_PIPELINE_VAR1[/"UNIP_PIPELINE_VAR1"/]
UNIP_PIPELINE_VAR2[/"UNIP_PIPELINE_VAR2"/]
UNIP_PIPELINE_VAR3_first[/"UNIP_PIPELINE_VAR3"/]
docker1[/"docker container 1"/]
end
var3[/"var3"/]
subgraph stage2
UNIP_PIPELINE_VAR3[/"UNIP_PIPELINE_VAR3"/]
UNIP_PIPELINE_VAR4[/"UNIP_PIPELINE_VAR4"/]
docker2[/"docker container 2"/]
end
docker1 --> UNIP_PIPELINE_VAR3_first
UNIP_PIPELINE_VAR1 --> docker1
UNIP_PIPELINE_VAR2 --> docker1
UNIP_PIPELINE_VAR3_first --> var3
var4[/"var4"/]
UNIP_PIPELINE_VAR3 --> docker2
docker2 --> UNIP_PIPELINE_VAR4
UNIP_PIPELINE_VAR4 --> var4
var1 --> UNIP_PIPELINE_VAR1
var2 --> UNIP_PIPELINE_VAR2
var3 --> UNIP_PIPELINE_VAR3
```
Данные передаются на вход пайплайна, а также между его отдельными этапами через *переменные*. Эти переменные используются в нескольких частях работы пайплайна:
- Внутри контейнера эти переменные доступны как переменные окружения.
- При вызове пайплайна через API эти переменные отвечают за входные и выходные данные.
- При создании манифеста пайплайна переменные указываются в манифесте самого пайплайна и его API-компонента.
Есть три типа переменных:
* входные - могут быть переданы в поле `inputs` при вызове пайплайна через API.
* выходные - могут быть переданы в поле `output_vars` при вызове пайплайна через API. Значением может быть только путь к папке, не к файлу.
* внутренние - не передаются через API, но могут быть указаны как входные и выходные переменные отдельных этапов пайплайна, и в таком случае данные будут автоматически переданы между этапами.
## Взаимодействие кода модуля с фреймворком
Для каждого контейнера, созданного во время выполнения пайплайна, будут определены переменные окружения в формате `UNIP_PIPELINE_<VAR_NAME>`. Значения таких переменных - пути к файлами и папкам в локальной файловой системе контейнера, с которыми должен работать код модуля.
Например, модуль обучает ML-модель на наборе данных "xy_train". Чтобы передать этот набор данных в этап обучения внутри пайплайна и использовать его при обучении, нужно:
1. Добавить входной параметр `xy_train` в манифест пайплайна в пункт `spec.stages.[].input`.
2. При создании контейнера, считывать переменную окружения `UNIP_PIPELINE_XY_TRAIN` - её название автоматически создаётся добавлением префикса `UNIP_PIPELINE` и переводом названия входного параметра в верхний регистр.
3. При вызове пайплайна через API, в теле вызова передавать путь к данным `xy_train` (файлу или папке).
Если поле называется `example_field`, то внутри контейнера будет переменная `UNIP_PIPELINE_EXAMPLE_FIELD`.
Пример такой работы:
```python
def main():
model_path = os.getenv("UNIP_PIPELINE_MODEL")
model = load_model(model_path)
data_path = os.getenv("UNIP_PIPELINE_DATASET")
data = load_data(data_path)
metrics = compute_metrics(model, data)
metrics_file_name = os.path.join(os.getenv("UNIP_PIPELINE_METRICS"), "metrics.txt")
write_metrics_to_file(metrics, metrics_file_name)
```
Здесь используются две входные переменные, `model` и `dataset` и одна выходная - `metrics`.
Входные переменные могут быть файлами или папками, а выходная - только папкой, поэтому в примере добавляется путь к файлу.
## Манифест ExperimentPipeline
Пример манифеста самого пайплайна.
```yaml
apiVersion: "unified-platform.cs.hse.ru/v1"
kind: ExperimentPipeline
metadata:
name: my-pipeline
namespace: pu-username-pa-bm99
spec:
vars:
- name: xy_train
- name: xy_test
- name: extra_parameters
- name: testing_results
- name: model
stages:
- name: stage1
image:
existingImageName: registry-platform-dev-cs-hse.objectoriented.ru/lab-name/bm99-somename-train:4f4621b
inputs:
- name: xy_train
- name: extra_parameters
outputs:
- name: model
env:
- name: MY_VARIABLE
value: "some_value"
- name: ANOTHER_VAR
value: "2"
entryPoint:
cmd:
- python3
- src/training.py
resourceLimits:
cpu: 500m
memory: 256M
- name: stage2
image:
existingImageName: example.com/lab-name/bm99-somename-test:4f4621b
inputs:
- name: model
- name: xy_test
- name: extra_parameters
outputs:
- name: testing_results
entryPoint:
cmd:
- python3
- src/testing.py
resourceLimits:
cpu: 500m
memory: 256M
connectedBoxes:
- name: data-box
path: /path/to/mountpoint
default: true
mountS3Box:
s3BoxName: mymodule-data-box
```
Здесь,
- `metadata.name` - название компонента пайплайна. Это название используется в API-компоненте и при выполнении запросов к пайплайну.
- `spec.vars` - список всех переменных, используемых в пайплайне, включая входные, выходные и внутренние.
- `spec.stages` - список этапов пайплайна. Этапы вызываются последовательно при вызове пайплайна.
- `spec.stages.[0].name` - название этапа
- `spec.stages.[0].image.existingImageName` - название образа, который используется для выполнения этапа пайплайна.
- `spec.stages.[0].inputs` - список входных переменных этапа.
- `spec.stages.[0].outputs` - список выходных переменных этапа.
- `spec.stages.[0].entryPoint` - точка входа в контейнер, консольная команда.
- Отдельные элементы списка будут соединены пробелами и преобразованы в единую команду.
- Команда из примера станет `python3 src/training.py` в терминале.
- `spec.stages.[0].env` - список переменных окружения, которые будут установлены в контейнере при запуске пайплайна.
- Переменные окружения могут принимать только строковые значения.
- `spec/connectedBoxes` - подключенные компоненты `DataBox` (ящики)
- В примере используется только один ящик, где хранятся данные, обучаемые модели, результаты и прочее
- `name` - внутреннее имя ящика, может использоваться для явного указания, откуда монтировать данные переменной.
- `path` - путь внутри контейнера, к которому монтируется ящик. Например, `/data`.
- `mountS3Box/s3BoxName` - название компонента `DataBox` из соответствующего файла манифеста.
- `default` - если указано `true`, то данные переменные автоматически монтируются из этого ящика.
Каждый этап работает с собственными входными и выходными данными. Переменные, определённые здесь, в пайплайне, но не определённые в API, считаются внутренними переменными. Например, в примере выше такой переменной является `model`.
## Манифест APIComponent отдельного пайплайна
Пример манифеста API пайплайна
```yaml
apiVersion: "unified-platform.cs.hse.ru/v1"
kind: APIComponent
metadata:
name: api-pipeline-train-validate
namespace: pu-username-pa-bm99
spec:
published: true
experimentPipeline:
name: my-pipeline
restfulApi:
auth:
basic:
credentials: bm99-apis-cred
identityPassThrough: true
apiSpec:
inputs:
- name: xy_train
description: "Данные для обучения. Таблица, включающая целевую переменную."
type:
datatypes: [ "FILE" ]
contentTypes: [ "text/csv" ]
- name: xy_test
description: "Данные для тестирования. Таблица, включающая целевую переменную."
type:
datatypes: [ "FILE" ]
contentTypes: [ "text/csv" ]
- name: extra_parameters
description: "Метаданные для обучения. Словарь с параметрами, передаваемыми в аргумент 'param_grid' при инициализации sklearn.model_selection.GridSearchCV."
required: false
type:
datatypes: [ "FILE" ]
contentTypes: [ "application/json" ]
outputs:
- name: testing_results
description: "Таблица со значениями метрик качества модели на тестовых данных xy_test."
type:
contentTypes: [ "text/csv" ]
```
Здесь,
- `metadata.name` - название компонента API.
- `spec.published` - "включен" ли API.
- `spec.experimentPipeline` - название компонента `ExperimentPipeline`, запуску которого соответствует этот компонент API.
- `spec.apiSpec` - определение интерфейса вызова пайплайна. То есть, определение самого API. В частности,
- `inputs` - входные переменные, со следующими полями
- `name` - имя переменной, используется при вызове API, при доступе в контейнере и при определении самого пайплайна в `ExperimentPipeline`.
- `description` - описание переменной, выводится при запросе спецификации API, необязательно.
- `required` - обязательно ли передавать переменную для вызова пайплайна? По умолчанию `true`.
- `type.datatypes` - список возможных типов передаваемых данных. Подробнее ниже.
- `type.contentTypes` - список возможных типов файлов, если тип передаваемых данных - `FILE`.
- `outputs` - выходные данные. Используют те же поля, что `inputs`, со следующими отличиями:
- `required` не указывается и всегда считается `false`. Все выходные переменные необязательны.
- `type.datatypes` почти всегда равно `[ "FILE" ]`. Выходные данные передаются только файлами. Для модуля построения отчётов используется тип `website`.
Подробнее о типах файлов в **spec.type.datatypes**.
Потенциальные возможные типы данных - `["FP32", "FP64", "INT32", "dict", 'str', "FILE"]`. Если типа данных не указан, считаются разрешёнными все типы данных. Аналогично с типами файлов. Если `contentTypes` - это пустой список `[]`, считаются допустимыми любые типы файлов.
Образы в любом случае должны работать **только с файлами**. Передача данных в другом виде, например в `FP32`, означает формат при вызове пайплайна через API, но не при работе с данными в контейнере модуля.
Если данные переданы не в виде файла, они сохраняются в файл, и этот файл уже передаётся в контейнер. Данные сохраняются простым преобразованием в строку и сохранением этой строки в текстовый файл.
## Манифест APIComponent пайплайнов в целом
```yaml
apiVersion: "unified-platform.cs.hse.ru/v1"
kind: APIComponent
metadata:
name: api-pipelines
namespace: pu-username-pa-bm99
spec:
published: true
pipelines:
enabled: true
restfulApi:
auth:
basic:
credentials: bm99-apis-cred
identityPassThrough: true
```
Главное отличие от других APIComponent - пункт `pipelines.enabled: true`. Изменяемые разработчиком пункты - имя приложения `metadata.namespace`, имя самого компонента `metadata.name` и имя секрета с реквизитами `spec.restfulApi.auth.basic.credentials`
## Изменение пути монтирования отдельных переменных (необязательно).
Иногда необходимо подключить к модулю данные из файлового хранилища, которые не загружал пользователь. Частый пример - веса ИИ-моделей, которые используются при запросах всех пользователей. В таких случаях для входной переменной можно указать конкретный путь, к которому она будет подключена, вместо того, чтобы выводить её в API.
Пример:
```yaml
...
name: my-pipeline
namespace: pu-username-pa-bm99
spec:
vars:
- name: xy_train
path: /data
mountFrom:
box:
name: global-data-box
boxPath: users/developer/file_groups/my_dataset
- name: xy_test
- name: extra_parameters
- name: testing_results
- name: model
stages:
- name: stage1
image:
...
```
В этом примере переменная `xy_train` монтируется напрямую к пути в ящике `global-data-box`. Таким образом, данные из этого ящика будут всегда доступны при запуске пайплайна, как будто они были переданы во входой переменной `xy_train`.
В этом примере данные размещены в ящике `global-data-box`. Чтобы можно было к ним обратиться, нужно описать подключение ящика `global-data-box` в разделе `connectedBoxes`.
```yaml
connectedBoxes:
- name: data-box
path: /path/to/mountpoint
default: true
mountS3Box:
s3BoxName: mymodule-data-box
- name: global-data-box
path: /path/to/another/mountpoint
default: false
mountS3Box:
s3BoxName: my-global-data-box
```
Общая структура примера выше:
- В приложении есть DataBox с названием `my-global-data-box`.
- В `connectedBoxes` этот компонент подключается к пайплайну с именем `global-data-box`. Теперь во всём остальном файле для него используется имя `global-data-box`.
- Также в `connectedBoxes` указано, куда ящик `global-data-box` монтируется по умолчанию - в `/path/to/another/mountpoint`. Это означает, что по умолчанию переменные, монтируемые к ящику `global-data-box` будут соответствовать путям `/path/to/another/mountpoint/...` в файловой системе контейнера.
- Для более точного контроля в любом указании переменной (вход/выход или список `vars`) можно добавить поля:
- `path` - глобальный путь внутри контейнера. Например `/home/user/data` или просто `/data`.
- `mountFrom` - блок, указывающий расположение монтируемого пути в хранилище S3.
- `box.name` - имя ящика из раздела `connectedBoxes`, к которому монтируется переменная.
- `box.boxPath` - путь внутри S3, к которому монтируется переменная.
При указании `boxPath` путь начинается с системных папок `users/<username>/file_groups/`, которые недоступны пользователям при запросах через файловый API. Когда пользователь `USERNAME` загружает через API файл с путём `myfolder/FILE.txt`, то в S3 этот файл появляется в `users/USERNAME/file_groups/myfolder/FILE.txt`. Соответственно, можно выделить специального системного пользователя (в примерах - `developer`), который загружает "общие" файлы, и указать в манифестах путь к этим общим файлам. Например, это могут быть файлы с весами ИИ-модели, которую использует модуль.