Как автоматизировать рабочий процесс машинного обучения с помощью Kubeflow Pipelines

Почему конвейеры машинного обучения?

Сейчас большое внимание уделяется идее конвейеров машинного обучения, которые предназначены для автоматизации и координации различных этапов обучения модели машинного обучения; однако не всегда ясно, каковы преимущества моделирования рабочих процессов машинного обучения как автоматизированных конвейеров.

Получив задание обучить новую модель машинного обучения, большинство специалистов по данным и инженеров машинного обучения, вероятно, начнут с разработки новых скриптов Python или интерактивных записных книжек, которые выполняют извлечение данных и предварительную обработку, необходимые для создания чистого набора данных для обучения модели. Затем они могут создать несколько дополнительных скриптов или записных книжек, чтобы опробовать разные типы моделей или разные фреймворки машинного обучения. И, наконец, они соберут и изучат метрики, чтобы оценить, как каждая модель работает с тестовым набором данных, а затем определят, какую модель развернуть в производственной среде.

Очевидно, что это чрезмерное упрощение настоящего рабочего процесса машинного обучения, но ключевым моментом является то, что этот общий подход требует большого количества ручного труда и не может быть повторно использован или легко воспроизведен кем-либо, кроме инженера (инженеров), который изначально разработал его.

Мы можем использовать конвейеры машинного обучения для решения этих проблем. Вместо того, чтобы рассматривать подготовку данных, обучение модели, проверку модели и развертывание модели как единую базу кода, предназначенную для конкретной модели, над которой мы работаем, мы можем рассматривать этот рабочий процесс как последовательность отдельных модульных шагов, каждый из которых сосредоточен на конкретная задача.

Моделирование наших рабочих процессов машинного обучения в виде конвейеров машинного обучения дает ряд преимуществ:

  • Автоматизация: устраняя необходимость ручного вмешательства, мы можем запланировать наш конвейер для переобучения модели с определенной частотой, убедившись, что наша модель адаптируется к дрейфу обучающих данных с течением времени.
  • Повторное использование. Поскольку шаги конвейера отделены от самого конвейера, мы можем легко повторно использовать один шаг в нескольких конвейерах.
  • Повторяемость: любой специалист по данным или инженер может повторно запустить конвейер, тогда как при ручном рабочем процессе теперь всегда может быть ясно, в каком порядке нужно запускать различные сценарии или записные книжки. .
  • Разделение среды. Сохраняя разделение этапов конвейера машинного обучения, мы можем выполнять разные этапы в разных типах сред. Например, некоторые этапы подготовки данных могут потребоваться для выполнения на большом кластере машин, тогда как этап развертывания модели, вероятно, может выполняться на одном компьютере.

Если вас интересует более глубокое погружение в конвейеры машинного обучения и их преимущества, в Google Cloud есть отличная статья, в которой описывается естественный прогресс в направлении более эффективных и автоматизированных практик (включая конвейеры машинного обучения), которые команды могут использовать для совершенствования своих рабочих процессов машинного обучения: MLOps: конвейеры непрерывной доставки и автоматизации в машинном обучении

Что такое Kubeflow?

Kubeflow - это платформа с открытым исходным кодом, построенная на Kubernetes, цель которой упростить разработку и развертывание систем машинного обучения. Описанный в официальной документации как набор инструментов машинного обучения для Kubernetes, Kubeflow состоит из нескольких компонентов, охватывающих различные этапы жизненного цикла разработки машинного обучения. Эти компоненты включают среды разработки записных книжек, настройку гиперпараметров, управление функциями, обслуживание моделей и, конечно же, конвейеры машинного обучения.

В этой статье мы сосредоточимся только на компоненте Pipelines в Kubeflow.

Среда

Для запуска примера конвейера я использовал кластер Kubernetes, работающий на «голом железе», но вы можете запустить пример кода в любом кластере Kubernetes, где установлен Kubeflow.

Единственная локальная зависимость - это пакет SDK Kubeflow Pipelines. Вы можете установить SDK с помощью pip:

pip install kfp

Трубопроводы Kubeflow

Конвейеры в Kubeflow состоят из одного или нескольких компонентов, которые представляют отдельные этапы конвейера. Каждый компонент выполняется в собственном контейнере Docker, что означает, что каждый шаг в конвейере может иметь свой собственный набор зависимостей, независимо от других компонентов.

Для каждого разрабатываемого нами компонента мы создадим отдельный образ Docker, который принимает некоторые входные данные, выполняет операцию, а затем предоставляет некоторые выходные данные. У нас также будет отдельный скрипт python, pipeline.py, который создает компоненты конвейера из каждого образа Docker, а затем создает конвейер с использованием этих компонентов.

Всего мы создадим четыре компонента:

  • preprocess-data: этот компонент загрузит набор данных Boston Housing из sklearn.datasets, а затем разделит набор данных на обучающий и тестовый наборы.
  • train-model: этот компонент обучит модель прогнозированию средней стоимости домов в Бостоне с использованием набора данных Boston Housing.
  • test-model: этот компонент будет вычислять и выводить среднеквадратичную ошибку модели в тестовом наборе данных.
  • deploy-model: в этой статье мы не будем сосредотачиваться на развертывании или обслуживании модели, поэтому этот компонент просто регистрирует сообщение о развертывании модели. В реальном сценарии это может быть общий компонент для развертывания любой модели в среде контроля качества или в производственной среде.

Если все эти разговоры о компонентах и ​​образах Docker звучат сбивающе с толку: не волнуйтесь, все это станет более понятным, когда мы перейдем к коду.

Компонент: данные предварительной обработки

Первый компонент в нашем конвейере будет использовать sklearn.datasets для загрузки в набор данных Boston Housing. Мы разделим этот набор данных на обучающие и тестовые наборы с помощью функции train_test_split Sci-kit learn, а затем будем использовать np.save для сохранения нашего набора данных на диск, чтобы его можно было повторно использовать в последующих компонентах.

Пока это просто простой скрипт Python. Теперь нам нужно создать образ Docker, который выполняет этот скрипт. Мы напишем Dockerfile для создания образа:

Начиная с python:3.7-slim базового образа, мы установим необходимые пакеты с помощью pip, скопируем сценарий Python предварительной обработки с нашего локального компьютера в контейнер, а затем укажем сценарий preprocess.py в качестве точки входа контейнера, что означает, что при запуске контейнера он выполнит наш скрипт.

Строительство трубопровода

Теперь мы приступим к конвейеру. Во-первых, вам нужно убедиться, что образ Docker, который мы определили выше, доступен из вашего кластера Kubernetes. Для этого примера я использовал Действия GitHub, чтобы создать образ и отправить его в Docker Hub.

Теперь давайте определим компонент. Каждый компонент определяется как функция, возвращающая объект типа ContainerOp. Этот тип взят из kfp SDK, который мы установили ранее. Вот определение компонента для первого компонента в нашем конвейере:

Обратите внимание, что для аргумента image мы передаем имя образа Docker, определенного указанным выше файлом Docker, а для аргумента file_outputs мы указываем пути к четырем .npy файлам, которые сохраняются на диск нашим компонентом Python. сценарий.

Указав эти четыре файла как выходные файлы, мы сделаем их доступными для других компонентов конвейера.

Примечание. Жесткое кодирование путей к файлам в наших компонентах - не очень хорошая практика, поскольку, как видно из приведенного выше кода, для этого требуется, чтобы лицо, создающее определение компонента, знало конкретные детали о реализация компонента (то есть реализация, содержащаяся в образе Docker). Было бы намного проще, если бы наш компонент принимал пути к файлам в качестве аргументов командной строки. Таким образом, лицо, определяющее компонент, имеет полный контроль над тем, где ожидать выходные файлы. Я оставил его жестко запрограммированным таким образом, чтобы, надеюсь, было легче увидеть, как все эти части сочетаются друг с другом.

Определив наш первый компонент, мы можем создать конвейер, который использует компонент preprocess-data.

Определение конвейера - это функция Python, украшенная аннотацией @dsl.pipeline. Внутри функции мы можем использовать компонент, как любую другую функцию.

Чтобы выполнить конвейер, мы создаем объект kfp.Client и вызываем функцию create_run_from_pipeline_func, передавая функцию, которая определяет наш конвейер.

Если мы выполним этот скрипт, а затем перейдем к представлению «Эксперименты» в разделе «Конвейеры» на центральной панели управления Kubeflow, мы увидим выполнение нашего конвейера. Мы также можем увидеть четыре выходных файла из компонента preprocess-data, щелкнув компонент в графическом представлении конвейера.

Таким образом, мы можем выполнить наш конвейер и визуализировать его в графическом интерфейсе, но конвейер с одним шагом не так уж и интересен. Создадим оставшиеся компоненты.

Остальные компоненты

Для компонента train-model мы создадим простой скрипт на Python, который обучает регрессионную модель с помощью Sci-kit learn. Это должно быть похоже на скрипт python для компонента препроцессора. Большая разница в том, что здесь мы используем argparse, чтобы принимать пути к файлам обучающих данных в качестве аргументов командной строки.

Dockerfile также очень похож на тот, который мы использовали для первого компонента. Мы начинаем с базового образа, устанавливаем необходимые пакеты, копируем скрипт python в контейнер, затем выполняем скрипт.

Два других компонента, test-model и deploy-model, следуют этому же шаблону. Фактически, они настолько похожи на два компонента, которые мы уже реализовали, что для краткости я не буду их здесь показывать. Если вам интересно, вы можете найти весь код конвейера в этом репозитории GitHub: https://github.com/gnovack/kubeflow-pipelines

Как и ранее в случае с компонентом preprocess-data, мы создадим образы Docker из этих трех компонентов и отправим их в Docker Hub:

  • модель поезда: gnovack / boston_pipeline_train
  • тестовая модель: gnovack / boston_pipeline_test
  • модель развертывания: gnovack / boston_pipeline_deploy

Полный конвейер

Пришло время создать полный конвейер машинного обучения.

Сначала мы создадим определения компонентов для компонентов train-model, test-model и deploy-model.

Единственное существенное различие между определением компонента train-model и определением компонента preprocess-data, приведенным ранее, заключается в том, что train-model принимает два аргумента, x_train и y_train, которые будут переданы в контейнер в качестве аргументов командной строки и будут проанализированы в реализации компонента с использованием модуля argparse.

Теперь определения компонентов test-model и deploy-model:

Определив четыре компонента конвейера, мы вернемся к функции boston_pipeline, описанной ранее, и будем использовать все наши компоненты вместе.

Давайте разберемся с этим:

  • Обратите внимание на строку 6, когда мы вызываем функцию preprocess_op(), мы сохраняем вывод функции в переменной с именем _preprocess_op. Чтобы получить доступ к выходным данным компонента preprocess-data, мы вызываем _preprocess_op.outputs['NAME_OF_OUTPUT'].
  • По умолчанию, когда мы обращаемся к file_outputs из компонента, мы получаем содержимое файла, а не путь к нему. В нашем случае, поскольку это не простые текстовые файлы, мы не можем просто передать содержимое файла в контейнеры Docker компонентов в качестве аргументов командной строки. Чтобы получить доступ к пути к файлу, мы используем dsl.InputArgumentPath() и передаем вывод компонента.

Теперь, если мы создадим запуск из конвейера и перейдем к пользовательскому интерфейсу конвейеров на центральной панели управления Kubeflow, мы должны увидеть все четыре компонента, отображаемые на графике конвейера.

Заключение

В этой статье мы создали очень простой конвейер машинного обучения, который загружает некоторые данные, обучает модель, оценивает ее на удерживаемом наборе данных, а затем «развертывает» ее. Используя Kubeflow Pipelines, мы смогли инкапсулировать каждый шаг этого рабочего процесса в компоненты конвейера, каждый из которых запускается в своей собственной изолированной среде контейнера Docker.

Эта инкапсуляция способствует слабой связи между этапами нашего рабочего процесса машинного обучения и открывает возможность повторного использования компонентов в будущих конвейерах. Например, в нашем учебном компоненте не было ничего, относящегося к набору данных Boston Housing. Мы могли бы повторно использовать этот компонент в любое время, когда захотим обучить регрессионную модель с помощью Sci-kit learn.

Мы лишь слегка коснулись того, что возможно с Kubeflow Pipelines, но, надеюсь, эта статья помогла вам понять основы компонентов и то, как мы можем использовать их вместе для создания и выполнения конвейеров.

Если вам интересно изучить всю кодовую базу, используемую в этой статье, вы можете найти ее в репозитории GitHub: https://github.com/gnovack/kubeflow-pipelines

Ссылки

Спасибо за прочтение! Не стесняйтесь обращаться с любыми вопросами или комментариями.