Запустить процесс обучения

Модели можно обучать следующими способами:

  • Напрямую из Jupyter Server на выделенных GPU.

  • С помощью функции client_lib, отправляя задачи на исполнение.

  • С помощью public API.

  • С помощью Пайплайнов.

В этой инструкции подробно рассмотрено обучение из Jupyter Server и при помощи функции client_lib.

У каждого из этих способов есть свои особенности. Они отражены в таблице ниже.

Особенность

Обучение из Jupyter Server на выделенных GPU

Обучение посредством отправки задач на исполнение

Количество GPU

При обучении из Jupyter Server максимальное количество выделенных GPU — 16 (для Christofari.V100), 8 (для Christofari.A100 и Cloud.Region.A100 (GPU Tesla A100)).

Подробнее про регионы для запуска обучения.

При отправке задачи обучения на исполнение можно задействовать несколько GPU (подробнее см. в разделе Обучение моделей на большом количестве GPU).

Особенности работы

Не требуется работать с библиотеками Horovod/dask/blink/PyTorch.distributed, командами client_lib, не требуется реализовывать распределенные вычисления в коде.

Возможно исполнение каждой ячейки Jupyter Notebook по отдельности.

Требуется реализовать распределенные вычисления при помощи библиотек Horovod/dask/blink/PyTorch.distributed.

Логи выводятся единовременно для всего скрипта обучения.

Тарификация

Если обучение происходит из Jupyter Server на выделенных GPU, оплата взимается с момента создания Jupyter Server до удаления, даже если он не используется.

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

Подготовительные действия до начала обучения

  1. Создайте Jupyter Server или подключитесь к уже существующему.

    Примечание

    Если планируете запускать обучение через client_lib, используйте Jupyter Server в любом регионе, кроме SR.01 (CPU, V100). В нем client_lib недоступна.

  2. Выберите инструмент для работы.

    Можно обучать модели из Jupyter Notebook или JupyterLab. JupyterLab существенно обогащает возможности классического Jupyter Notebook, поскольку поддерживает работу одновременно с несколькими ноутбуками, текстовыми файлами, датасетами, терминалами и позволяет упорядочить документы в рабочей области.

  3. Переместите данные из объектного хранилища S3 в локальное хранилище NFS требуемого региона.

    Подробная информация о перемещении данных в NFS приведена в разделе Работа с данными сервиса Data Catalog.

Обучение из Jupyter Server с GPU

Установка необходимых библиотек

Установка или обновление библиотек требуется, если в базовых образах нет необходимых пользователю зависимостей.

В каждом базовом образе есть предустановленный набор библиотек (см. Образы для Jupyter Server). Если для работы необходимо установить дополнительные модули python или обновить существующие, выполните блок:

pip install --user <package_name> == <version>

Где package_name — наименование модуля, который предполагается установить, а version — версия устанавливаемого модуля. Подробная информация об использовании pip приведена в документации менеджера пакетов.

Запуск обучения модели

Пример обучения модели из Jupyter Server c GPU приведен ниже. Для запуска используйте образ, в котором версия tensorflow выше 2.0.0, например, jupyter-cuda11.0-tf2.4.0-pt1.7.1-gpu:0.0.82-1.

from __future__ import absolute_import, division, print_function

import tensorflow as tf
from tensorflow.keras import Model, layers
import numpy as np

tf.compat.v1.enable_eager_execution()

num_classes = 10
num_features = 784

learning_rate = 0.1
training_steps = 2000
batch_size = 256
display_step = 100

n_hidden_1 = 128
n_hidden_2 = 256

from tensorflow.keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = np.array(x_train, np.float32), np.array(x_test, np.float32)
x_train, x_test = x_train.reshape([-1, num_features]), x_test.reshape([-1, num_features])
x_train, x_test = x_train / 255., x_test / 255.

train_data = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_data = train_data.repeat().shuffle(5000).batch(batch_size).prefetch(1)

class NeuralNet(Model):
   def __init__(self):
      super(NeuralNet, self).__init__()
      self.fc1 = layers.Dense(n_hidden_1, activation=tf.nn.relu)
      self.fc2 = layers.Dense(n_hidden_2, activation=tf.nn.relu)
      self.out = layers.Dense(num_classes)

   def call(self, x, is_training=False):
      x = self.fc1(x)
      x = self.fc2(x)
      x = self.out(x)
      if not is_training:
            x = tf.nn.softmax(x)
      return x

neural_net = NeuralNet()

def cross_entropy_loss(x, y):
   y = tf.cast(y, tf.int64)
   loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=x)
   return tf.reduce_mean(loss)

def accuracy(y_pred, y_true):
   correct_prediction = tf.equal(tf.argmax(y_pred, 1), tf.cast(y_true, tf.int64))
   return tf.reduce_mean(tf.cast(correct_prediction, tf.float32), axis=-1)

optimizer = tf.keras.optimizers.SGD(learning_rate)

def run_optimization(x, y):
   with tf.GradientTape() as g:
      pred = neural_net(x, is_training=True)
      loss = cross_entropy_loss(pred, y)
   trainable_variables = neural_net.trainable_variables
   gradients = g.gradient(loss, trainable_variables)
   optimizer.apply_gradients(zip(gradients, trainable_variables))

for step, (batch_x, batch_y) in enumerate(train_data.take(training_steps), 1):
   run_optimization(batch_x, batch_y)

   if step % display_step == 0:
      pred = neural_net(batch_x, is_training=True)
      loss = cross_entropy_loss(pred, batch_y)
      acc = accuracy(pred, batch_y)
      print("step: %i, loss: %f, accuracy: %f" % (step, loss, acc))

pred = neural_net(x_test, is_training=False)
print("Test Accuracy: %f" % accuracy(pred, y_test))

Другие примеры обучения моделей приведены на Github.

Обучение путем отправки задач на исполнение в регион с помощью client_lib

Сборка кастомного образа с необходимыми библиотеками

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

Ниже представлен пример, как дополнить базовый образ cr.msk.sbercloud.ru/aicloud-base-images/horovod-cuda10.0-tf1.15.0-pt1.3.0 нужными зависимостями. Обратите внимание на то, что путь к файлу с зависимостями указывается полностью.

job = client_lib.ImageBuildJob(
                     from_image='cr.msk.sbercloud.ru/aicloud-base-images/horovod-cuda10.0-tf1.15.0-pt1.3.0',   # Base image
                     requirements_file='/home/jovyan/quick-start/job_launch/requirements.txt'    # File with dependencies for custom image
)

Файл requirements.txt должен находиться на NFS-хранилище Christofari.V100.

Для запуска сборки кастомного образа выполните:

job.submit()
job.new_image     # id of the custom image

Посмотреть логи сборки образа в интерактивном режиме можно с помощью команды:

job.logs()

Запуск задачи обучения в регионе

Запустить задачу обучения в регионе можно средствами client_lib.

Примечание

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

Пример 1. Задача обучения модели в регионе с использованием базового образа.
mnist_tf_run = client_lib.Job(base_image='cr.msk.sbercloud.ru/aicloud-base-images/horovod-cuda10.0-tf1.15.0-pt1.3.0:0.0.31',
                              script='/home/jovyan/quick-start/job_launch/scripts/horovod/tensorflow_mnist_estimator.py',
                              instance_type='your instance_type',
                              job_desc='your_job_description')

Обязательный параметр base_image указывает на образ, в рамках которого выполнится задача; script — на скрипт, который запустится. Опциональный параметр job_desc позволяет пользователю задавать описание для задач обучения. Описание отображается в таблице статистики.

Дополнительный параметр processes_per_worker задает количество процессов на один рабочий узел региона, если не подходит количество процессов, равное количеству GPU.

job = Job(base_image='cr.msk.sbercloud.ru/aicloud-base-images/horovod-cuda10.0-tf1.15.0-pt1.3.0',
         script='/home/jovyan/my-script.py',
         n_workers = 1,
         region = A100-MT,
         instance_type = a100.1gpu,      # 1 GPU Tesla A100 80GB, 16 CPU-cores, 243 GB RAM
         processes_per_worker = 1,    # Start one process
         type = 'horovod')
Пример 2. Задача обучения модели в регионе с использованием базового образа и дополнительных библиотек.
mnist_tf_run = client_lib.Job(base_image=job.new_image,     # Built custom image
                              script='/home/jovyan/quick-start/job_launch/scripts/horovod/tensorflow_mnist_estimator.py',
                              instance_type='your instance_type',
                              n_workers=2)

Для получения значения instance_type воспользуйтесь инструкцией.

Для отправки задачи на исполнение в регион необходимо вызвать команду submit():

mnist_tf_run.submit()

Должно появиться сообщение вида:

Job 'lm-mpi-job-7a7aa4e0-8a8c-46a3-96fa-7116cc392336' created

После запуска задачи обучения вы можете просматривать ее логи в режиме реального времени. Для этого используется функция client_lib. Логи отображаются для задач в статусе «Выполняется» и «Завершена». Есть два варианта просмотра логов:

mnist_tf_run.logs()     # View logs by task

или

client_lib.logs("lm-mpi-job-7a7aa4e0-8a8c-46a3-96fa-7116cc392336")      # View logs by task name

Примечание

Уровень детализации логов управляется параметром verbose. Он позволяет исключить из лога информацию о выполнении служебных процессов.

Если вызвать logs() после отправки задачи с помощью метода submit(), логи могут не отобразиться сразу, поскольку региону нужно время на запуск.

Логи по требуемой задаче можно скачать с помощью кнопки Выгрузить логи в меню Кнопка с тремя вертикальными точками на вкладке Задачи и окружения → Задачи.

При необходимости задачу обучения можно остановить в любой момент методом kill():

mnist_tf_run.kill()

Или так:

client_lib.kill('lm-mpi-job-7a7aa4e0-8a8c-46a3-96fa-7116cc392336')      # For region V100
client_lib.kill(<job name>, region = 'A100')    # For region A100

Обратите внимание на то, что остановить задачу также можно, нажав Остановить на вкладке Задачи и окружения → Задачи. Это действие приведет к потере всех несохраненных результатов обучения (см. Сохранить промежуточные результаты обучения (checkpoints)).

Подсказка

Внутри кода задачи обучения также можно сохранять метрики модели с помощью MLflow. См. подробнее в примере.

Пример 3. Задача кросс-компиляции с использованием образов ML Space.

Важно

Импорт функции client_lib недоступен в Jupyter Server, который развернут в регионе SR.01 (CPU, V100).

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

import client_lib

binary_run = client_lib.Job(
base_image="cr.msk.sbercloud.ru/aicloud-base-images/horovod-cuda10.1-tf2.2.0-pt1.5.0",
script='/home/jovyan/NSC/mpi_omp_test',
 n_workers = 2,
 instance_type = v100.16gpu,
 type = 'binary'
 )

binary_run.submit()

binary_run.logs()

binary_run.kill()

Можно собрать бинарный файл mm из примера с GitHub. Для этого используется код:

git clone https://github.com/hpc/MPI-Examples.git
cd ~/MPI-Examples/matrix-multipy
make linux
./test.sh

Обучение моделей на большом количестве GPU

Количество рабочих узлов определяется параметром n_workers. Максимальное количество GPU на одном рабочем узле — 16 (для Christofari.V100) и 8 (для Christofari.A100).

Если пользователь указал, что для обучения ему необходимы один рабочий узел региона на 16 GPU, под задачу аллоцируется один DGX. Чем больше GPU требуется пользователю для обучения модели, тем выше вероятность того, что вычислительных ресурсов может не хватить. На практике эта ситуация возникает, если задано количество GPU от 64 до 128. В результате планировщик (scheduler) ставит задачу в очередь исполнения, и она может достаточно долго находиться в статусе «pending».

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

Обратите внимание на то, что увеличение количества рабочих узлов региона может повлиять на скорость обмена данными, поскольку внутри одного DGX она выше, чем между несколькими DGX. Кроме того, может уменьшиться объем оперативной памяти. Подробнее см. в разделе Использование ресурсов.

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

Для всех этих случаев есть общая рекомендация: постарайтесь обращаться к данным оптимально. Например, файлы можно читать частями, элементы списка можно заменить на массивы numpy.