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

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

В результате специалисты по данным должны поддерживать код на многих языках (Python, Java, Scala, C ++ и т. Д.) И даже в большем количестве фреймворков.

Это сделало контейнеризацию все более привлекательной техникой в ​​сообществе. Предлагая гибкость внутри контейнера, он предоставляет отполированный интерфейс для платформы.

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

Что такое Kubeflow?

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

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

  • Как мы настраиваем нашу рабочую среду
  • Как мы разработали наш конвейер для масштабирования
  • Как мы ускоряем разработку с помощью разработки через тестирование

Настройка вашей рабочей среды Kubeflow

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

С помощью конвейеров Kubeflow мы обрабатываем две основные категории активов: конвейеры и компоненты. Конвейеры содержат логику оркестровки, в то время как компоненты представляют собой контейнерные действия, выполняемые конвейером.

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

В разделе components по одному каталогу на компонент со следующей структурой:

- Dockerfile
- src
  - main
  - test
- resources

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

В разделе pipelines один файл python на конвейер и несколько утилит и модулей конфигурации:

my_kubeflow_pipeline.py
- utils
  - container_operations.py
  - kubernetes_utils.py
- config
  - config_holder.py
  - site_config.json
  ...
  • container_operations.py содержит все классы компонентов для нашего проекта. Мы сочли необходимым отделить определения классов от конвейеров для обеспечения удобочитаемости. Это также делает компоненты более пригодными для повторного использования, поскольку предоставляет интерфейс вне контекста конвейера.
  • kubernetes_utils.py содержит все общие функции, связанные с Kubernetes (монтирование тома, настройка пула ресурсов и т. д.)
  • config_holder.py содержит механизм загрузки и проверки конфигурации. Наличие файла site_config сэкономило нам много времени, поскольку мы могли определять параметры конфигурации в одном месте.

Дизайн для масштаба

При работе с большими наборами данных масштабирование часто достигается за счет параллелизма. В сообществе Big Data обычно используется паттерн Map-Reduce (см. Hadoop или Spark). Хотя эти фреймворки предлагают отличную производительность, они требуют написания кода с использованием их библиотек. Как объяснялось выше, мы имеем дело с кодом во многих формах, поэтому мы не всегда можем использовать эти технологии. Тем не менее конвейеры Kubelow могут быть спроектированы так, чтобы следовать той же общей архитектуре.

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

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

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

Преимущества разработки через тестирование

При разработке компонентов мы обнаружили, что разработка через тестирование имеет решающее значение и позволяет значительно сэкономить время.

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

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

Тем не менее, создание модульных тестов для многоступенчатого конвейера с различными инструментами, форматами ввода-вывода и т. Д. Может оказаться сложной задачей.

Чтобы повысить нашу эффективность, мы приняли несколько соглашений:

  • Для сложных задач наши компоненты обертывают кодовые базы, которые хранятся в отдельных репозиториях. Это отделяет тестирование функциональности от тестирования оркестровки. Тестирование компонентов сводится к тому, чтобы убедиться, что код выполняется без ошибок.
  • Наши модульные тесты используют функцию SetUp, предлагаемую большинством фреймворков, для последовательного создания синтетических сред (структура каталогов, переменные среды, синтетические данные и т. Д.). Иногда мы просто заархивируем выходные данные модульных тестов с предыдущего шага и используем их в качестве стартовой среды для следующего.
  • Наконец, большую экономию времени можно было сэкономить за счет использования многоэтапных сборок докеров для выполнения наших модульных тестов. Это делает выполнение модульных тестов единообразным для всех компонентов. Он также обеспечивает выполнение тестов в производственной среде (не более того: «Я не понимаю, это сработало на моей локальной машине…»). И это делает рабочие образы более чистыми, поскольку вы можете создавать их, не добавляя набор для тестирования.
    Вот пример нашего Dockerfile:
FROM python:3.6-slim as builder
RUN mkdir /app
COPY src/main/<your_script>.py /app
FROM builder as testrunner
RUN pip install nose
COPY src/test /
RUN nosetests -with-xunit -xunit-file /unit_test_results.xml /<your_unit_test>.py || true

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

Чтобы эффективно использовать эту структуру, мы организовали наш код, следуя нескольким простым соглашениям. Мы обеспечиваем масштабируемость нашего конвейера с помощью схемы реализации Map-Reduce. Наконец, мы обнаружили, что TDD необходим для ускорения процесса разработки.

В следующем посте мы подробнее рассмотрим некоторые или наши реализации и то, как мы интегрировались с IBM Cloud и Watson Machine Learning.