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

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

  1. SSH на наш сервер GPU.
  2. Запустите блокнот Jupyter внутри контейнера Docker.
  3. Загружайте данные с помощью таких инструментов, как rsync.
  4. Экспериментируйте с моделями, оценивайте и сохраняйте лучшую модель.
  5. Загрузите обученную модель и упакуйте ее для производства.

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

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

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

Я покажу вам, как это сделать в Gitlab CI/CD.

GitLab CI/CD — это встроенный инструмент непрерывной интеграции, непрерывной доставки и непрерывного развертывания, входящий в состав GitLab. Это позволяет разработчикам автоматизировать процесс создания, тестирования и развертывания своего кода. Кроме того, разработчики могут выявлять и устранять проблемы на ранних этапах процесса разработки, а также быстрее и надежнее вносить изменения в код.

Подобно традиционной разработке программного обеспечения, некоторые задачи обучения модели также могут быть интегрированы в CI/CD. После настройки репозитория gitlab и подключения gitlab-runners для выполнения задач становится довольно легко определить конвейеры CI/CD. Вот общие шаги для начала:

  1. Создайте файл YAML с именем .gitlab-ci.yml.
  2. Определите этапы и задания, которые будут выполняться всякий раз, когда код отправляется в источник. Этап – это группа заданий, которые представляют этап конвейера. Например, общие этапы конвейера включают: сборка, тестирование, развертывание. Задания — это отдельные задачи на этапе, которые выполняют определенные действия, такие как компиляция кода, выполнение тестов или развертывание в определенной среде.
  3. Для каждого задания определите необходимые сценарии и команды для запуска.
  4. Отправьте свой код в репозиторий и запустите конвейер. GitLab автоматически запустит конвейер и выполнит шаги, определенные в файле .gitlab-ci.yml.
  5. По мере запуска конвейера GitLab будет отображать ход и результаты каждого этапа конвейера.
  6. Если конвейер завершится успешно, модель будет развернута в производственной среде.

Например, у вас может быть:

  • этап сборки, на котором выполняется ваш сценарий обучения;
  • этап тестирования, который запускает вашу модель в соответствии с проверочным набором;
  • этап развертывания, на котором модель развертывается в производственной среде.
stages:
  - build
  - test
  - deploy

train_model:
  stage: build
  script:
    - python main.py train --data path/to/train/data --write-model path/to/best.model

eval_model:
  stage: test
  script:
    - python main.py test --data path/to/test/data --model path/to/best.model

upload_model:
  stage: deploy
  script:
    - python main.py --model path/to/best.model

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

В нашем случае у нас есть сервер DGX с несколькими графическими процессорами, и нам нужно обучать наши глубокие нейронные сети на удаленном сервере с доступом ssh. У каждого пользователя есть права docker на этой машине, тогда как gitlab-runners устанавливаются на отдельную машину, но подключены в одной сети с DGX.

Учитывая все эти нюансы, мы решили разделить обучение модели на три этапа:

  1. Создать — создать образ Docker и отправить его в реестр Docker. На этом этапе добавляются сценарии обучения и оценки, зависимости pip и другие файлы конфигурации. При обновлении кода образ докера будет перестроен, чтобы содержать все последние изменения. Пример dockerfile может выглядеть так:
FROM nvidia/cuda:11.3-devel-ubuntu20.04

COPY requirements.txt .
RUN pip install --no-cache -r requirements.txt

COPY . /app
WORKDIR /app

2. Pull — войдите в систему с графическим процессором, извлеките последний образ Docker и загрузите обучающие данные.

3. Обучить — войдите в систему с графическим процессором и запустите обучение модели. Этот шаг включает в себя оценку модели, экспорт лучшей модели в onnx и регистрацию метрик и модели на сервере отслеживания MLflow.

Это наш .gitlab-ci.yml:

variables:
  # docker image name will correspond to a gitlab project name
  # and docker tag will be a git branch name
  DOCKER_IMAGE_TAG: $CI_REGISTRY$CI_PROJECT_NAME:$CI_COMMIT_REF_SLUG
  # we assume that a user pushing a commit to git is able 
  # to log in to the DGX server
  USER: $CI_USERNAME
  # DGX host can be a domain address or IP
  DGX_HOST: 10.10.10.10
  MLFLOW_TRACKING_URI: https://my-mlflow-url.com/

before_script:
  # this script will run before each job
  - export DGX="-i path/to/private.key -l $USER $DGX_HOST"

stages:
  - build
  - pull
  - train

.trigger-training-commit-message:
  rules:
    # model training steps will be only launched if
    # a commit message starts with "train ..."
    - if: "$CI_COMMIT_MESSAGE =~ /^train.*/"

build:
  extends:
    # we will use the same `rules` for each job
    - .trigger-training-commit-message
  stage: build
  script:
    # we'll pull and existing docker image if it exists
    # to save some time during a docker-build process
    - docker pull -q $DOCKER_IMAGE_TAG || true
    - >-
      docker build --cache-from $DOCKER_IMAGE_TAG
      -t $DOCKER_IMAGE_TAG
      .
    - docker push $DOCKER_IMAGE_TAG

pull:
  extends:
    - .trigger-training-commit-message
  stage: pull
  script:
    # this script logs in to the DGX machine,
    # pulls a docker images built during the previous stage
    # and downloads a training dataset to ./data folder.
    - >-
      ssh $DGX <<EOF
        set -e
        docker pull --quiet $DOCKER_IMAGE_TAG
        docker run --rm \
          -v "$PROJECT_DIR/data/:/app/data/" 
          $DOCKER_IMAGE_TAG bash get_data.sh
        EOF

train:
  extends:
    - .trigger-training-commit-message
  stage: train
  timeout: 24h
  script:
    # this runs model training, evaluation and logging 
    # inside an nvidia docker container with a GPU #2
    - >-
      ssh $DGX <<EOF
        set -e
        docker run --rm \
          -v "$PROJECT_DIR/data/:/app/data/" \
          -e "NVIDIA_VISIBLE_DEVICES=1" \
          --runtime nvidia \
          --shm-size 10g \
          $DOCKER_IMAGE_TAG python train.py --data data/processed/dataset
        EOF

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

  1. Будет трудно непреднамеренно обучить модель, и мы можем использовать стратегию git-flow, чтобы поддерживать наш код в актуальном состоянии.
  2. Для обучения модели требуется сообщение фиксации, которое всегда будет полезно, чтобы вернуться в историю и просмотреть изменения, которые приводят к улучшению или снижению показателей.

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

В дополнение к улучшению вашего собственного набора навыков, ваши товарищи по команде и вы сами через несколько месяцев в будущем получите большую пользу от автоматизированного CI / CD для обучения моделей. Преимущества очевидны:

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

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