MLComponent подходит для быстрых вычислений, в частности, запуска предобученной модели машинного обучения на небольшой выборке данных. Например, распознавание объекта на одном изображении, классификация таблицы текстовых данных. Если алгоритм работает за доли секунды, то он подходит для создания MLComponent.
На основе образа Docker, указанного в манифесте MLComponent, создаётся Docker-контейнер. В него устанавливается системная библиотека платформы, которая создаёт веб-сервис. При получении запроса от пользователя веб-сервис вызывает функцию inference, указанную в манифесте MLComponent, и передаёт вывод функции пользователю.
Формат самих входных данных, которые передаются в аргументе `inputs` при вызове функции, и выходных данных, возвращаемых функцией, должен быть указан в docstring-спецификации.
Для каждой переменной нужно указать следующее:
1. Информация о переменной в формате `:param inputs.NAME: DESCRIPTION`
2. Информация о типе данных в формате `:type inputs.NAME: TYPE1 or TYPE2 or TYPE3[, optional]`
Здесь,
-`inputs` - тип переменной. Для входных это `inputs`, для выходных - `outputs`.
-`NAME` - имя переменной. Должно в точности соответствовать имени, которое передаётся через API.
-`DESCRIPTION` - описание переменной. Может содержать несколько строк текста. Используется в OpenAPI спецификации и каталоге.
-`TYPE1`, `TYPE2`, `TYPE3` - типы данных и типы содержимых, которые могут быть присвоены переменной.
- Типы содержимого (content_type) указываются так же, как типы данных - `FP32 or text/csv`.
- Если указано хотя бы один `content_type`, то тип `FILE` добавляется автоматически, даже если не указан - `:type inputs.mydata: text/csv`.
- Если не указан `content_type`, предполагается, что поддерживается любой.
- Если про тип переменной не указано ничего, предполагается, что поддерживается любой тип и любое содержимое.
-`[, optional]` в конце типа переменной может быть указано `, optional` (без квадратных скобок), что означает, что переменная опциональная, вызов функции должен работать и без неё.
В этом разделе описаны особенностей реализации функции inference и рекомендации. Технически функция может быть реализована любым способом, который соответствует интерфейсу, но при возможности желательно следовать рекомендациям ниже.
За передачу модели в функцию отвечают два параметра: `model_key` и `model`.
-`model_key` - строка-ключ, позволяющая загрузить модель с диска или хранилища файлов. Обычно, это путь. Например, `/home/project/data/model_weights.pth` для загрузки файла или `/home/project/google-bert/bert-base-uncased` для загрузки модели из папки.
-`model` - уже загруженный объект модели. Этот же объект возвращается функцией в кортеже `outputs, model`.
Так как ML-компонент предназначен для быстрых вычислений, предполагается, что время выполнения одного запроса гораздо меньше времени загрузки модели или запуска контейнера. Поэтому один и тот же контейнер и один и тот же объект модели используется для множества запросов.
Если модель `model` не передана в функцию inference (например, это первый запрос к новому модулю), веса ИИ-модели загружаются с использованием ключа `model_key`. Эти загруженные веса возвращаются в конце работы функции и передаются в аргумент `model` при каждом следующем вызове.
Тип объекта `model` может быть любым, то есть это может быть и набор моделей, если это соответствует логике вычислений.
Для передачи данных на вход функции inference и возвращения результатов вычислений пользователю есть два основных метода:
1. Передача данных напрямую, как объектов Python.
2. Передача данных через файл.
При объектной передаче поле `datatype` для соответствующей переменной устанавливается в одно из значений `"FP32", "FP64", "INT32", "str", "dict"`. В таком случае данные передаются напрямую в функцию inference в поле `data` одного из элементов списка `inputs`. Аналогично, для выходных данных (список `outputs` в примере ниже) ожидается, что данные будут переданы напрямую в поле `data`.
- Типы `FP32`, `FP64`, `INT32` означают, что сами данные переданы одномерным списком соответствующего типа. Если для вычислений важна размерность данных, она должна быть передана отдельно в параметре `shape`.
- Например, если указать `"data": [1, 2, 3, 4], "shape", [2, 2]`, это будет означать, что данные должны быть обработаны как массив 2x2, а не вектор из 4 элементов. За обработку данных отвечает код модуля, вызываемый из функции inference, автоматического преобразования размерности при обработке запросов не происходит.
- Тип `str` означает, что данные передаются в виде одной строки.
- Тип `dict` означает произвольный словарь, аналогичный объекту JSON.
- Например, `{"x": 10, "y": [1, 2]}`, но не `{"x": numpy.array([1,2])}`
При передаче данных через файл сами данные сохраняются в файл, а в поле `data` указывается путь к этому файлу. К контейнеру, в котором исполняется код модуля и функции inference, монтируется раздел файлового хранилища фреймворка. Входные файлы, переданные пользователем при вызове сервиса, сохраняются в этот раздел и становятся доступны внутри контейнера сервиса.
Если поле `datatype` установлено в значение `FILE`, то считывания входных данных необходимо считать содержимое файла, путь к которому передан в поле `data`. Поле `content_type` в таком случае позволяет уточнить тип переданного файла.
Чтобы вывести результат работы функции inference пользователю в виде файла, можно записать его в произвольное место в локальной файловой системе контейнера и указать путь к этому файлу в поле `data` элемента списка выходных данных. Во входной переменной `output_fields` среди информации об ожидаемых выходных данных в поле `data` передаётся путь во временной папке. Вместо произвольного места для сохранения файла можно использовать этот переданный путь
-`spec/env` - переменные окружения, передаваемые в контейнер. Вы можете вынести настройки контейнера в переменные окружения и задавать их в ML-компоненте, чтобы не нужно было изменять код и пересоздавать образ Docker с новыми значениями.
-`spec/mlService`
-`packageRegistryName` - название репозитория с пакетами Python, должно соответствовать "внешнему" названию из компонента приложения в пункте **7.6**. Название может быть другим, главное - соответствие названий друг другу.
-`inference/fileExchange` - соединение файловой системы контейнера (сервиса) и ящика S3 для обмена файлами.
-`fileBox` - название ящика S3 для файлов пользователей.
-`inferenceFilesPath` - путь, куда будут записываться создаваемые файлы внутри контейнера. К этому пути должны быть права доступа у пользователя контейнера. С этой папкой фреймворк взаимодействует автоматически, то есть лучше сделать уникальный путь, который нигде больше не используется.
-`inference/model` - соединение файловой системы контейнера (сервиса) и ящика S3 для подключения модели расчётов.
-`modelBox` - название ящика S3, где размещена модель.
*Здесь используется путь `users/developer/file_groups/models` как решение проблемы загрузки весов моделей и прочих данных в хранилище S3. При загрузке через файловый API от лица пользователя USER файлы загружаются по пути `users/USER/file_groups/...`. То есть в текущем примере веса можно загрузить от лица пользователя `developer` в папку `models/`, и через ML-компонент дать к ним доступ всем остальным пользователям.*