Распределенное обучение

Распределенное обучение — это тип обучения модели, при котором требования к вычислительным ресурсам (например, ЦП, ОЗУ) распределяются между несколькими компьютерами. Распределенное обучение позволяет обучаться быстрее и на больших наборах данных (до нескольких миллиардов примеров).

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

Из этого документа вы узнаете, как:

  • Обучите модель TF-DF с помощью распределенного обучения.
  • Настройте гиперпараметры модели TF-DF с помощью распределенного обучения.

Ограничения

На данный момент распределенное обучение поддерживается для:

  • Обучение моделей деревьев с градиентным усилением с помощью tfdf.keras.DistributedGradientBoostedTreesModel . Модели распределенных деревьев с усилением градиента эквивалентны своим нераспределенным аналогам.
  • Поиск гиперпараметров для любого типа модели TF-DF.

Как включить распределенное обучение

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

Область действия Параметрсерверстратеги

Модель и набор данных определяются в области ParameterServerStrategy .

strategy = tf.distribute.experimental.ParameterServerStrategy(...)
with strategy.scope():
  model = tfdf.keras.DistributedGradientBoostedTreesModel()
  distributed_train_dataset = strategy.distribute_datasets_from_function(dataset_fn)
model.fit(distributed_train_dataset)

Формат набора данных

Как и в случае нераспределенного обучения, наборы данных могут быть предоставлены в виде

  1. Конечный распределенный набор данных тензорного потока или
  2. путь к файлам набора данных в одном из совместимых форматов набора данных .

Использование сегментированных файлов значительно проще, чем использование подхода с распределенным набором данных конечного тензорного потока (1 строка против ~ 20 строк кода). Однако только подход к набору данных TensorFlow поддерживает предварительную обработку TensorFlow. Если ваш конвейер не содержит предварительной обработки, рекомендуется использовать вариант сегментированного набора данных.

В обоих случаях набор данных должен быть разбит на несколько файлов для эффективного распределения чтения набора данных.

Рабочие по настройке

Главный процесс — это программа, выполняющая код Python, определяющий модель TensorFlow. Этот процесс не выполняет каких-либо тяжелых вычислений. Эффективные обучающие вычисления выполняются рабочими . Рабочие — это процессы, запускающие сервер параметров TensorFlow.

Начальник должен быть настроен с использованием IP-адреса работников. Это можно сделать с помощью переменной среды TF_CONFIG или создав ClusterResolver . Дополнительные сведения см. в разделе Обучение сервера параметров с помощью ParameterServerStrategy .

ПараметрServerStrategy TensorFlow определяет два типа рабочих: «работники» и «сервер параметров». TensorFlow требует создания экземпляра хотя бы одного работника каждого типа. Однако TF-DF использует только «рабочих». Таким образом, необходимо создать экземпляр одного «сервера параметров», но он не будет использоваться TF-DF. Например, конфигурация обучения TF-DF может выглядеть следующим образом:

  • 1 начальник
  • 50 рабочих
  • 1 сервер параметров

Рабочим требуется доступ к специальным программам обучения TensorFlow Decision Forests. Есть два варианта включения доступа:

  1. Используйте предварительно настроенный сервер параметров TF-DF C++ //third_party/tensorflow_decision_forests/tensorflow/distribute:tensorflow_std_server .
  2. Создайте сервер параметров, вызвав tf.distribute.Server() . В этом случае TF-DF следует импортировать import tensorflow_decision_forests .

Примеры

В этом разделе показаны полные примеры конфигураций распределенного обучения. Дополнительные примеры можно найти в модульных тестах TF-DF .

Пример. Распределенное обучение по пути к набору данных.

Разделите набор данных на набор фрагментированных файлов, используя один из совместимых форматов набора данных . Рекомендуется называть файлы следующим образом: /path/to/dataset/train-<5 digit index>-of-<total files> , например

/path/to/dataset/train-00000-of-00100
/path/to/dataset/train-00001-of-00005
/path/to/dataset/train-00002-of-00005
...

Для максимальной эффективности количество файлов должно быть как минимум в 10 раз больше количества рабочих. Например, если вы обучаете 100 рабочих, убедитесь, что набор данных разделен как минимум на 1000 файлов.

Затем на файлы можно ссылаться с помощью выражения сегментирования, например:

  • /путь/к/набору данных/поезд@1000
  • /путь/к/набору данных/поезд@*

Распределенное обучение осуществляется следующим образом. В этом примере набор данных сохраняется как TFRecord примеров TensorFlow (определяется ключом tfrecord+tfe ).

import tensorflow_decision_forests as tfdf
import tensorflow as tf

strategy = tf.distribute.experimental.ParameterServerStrategy(...)

with strategy.scope():
  model = tfdf.keras.DistributedGradientBoostedTreesModel()

model.fit_on_dataset_path(
    train_path="/path/to/dataset/train@1000",
    label_key="label_key",
    dataset_format="tfrecord+tfe")

print("Trained model")
model.summary()

Пример. Распределенное обучение на конечном распределенном наборе данных TensorFlow.

TF-DF ожидает распределенный набор данных TensorFlow с конечным сегментированием рабочих:

  • Распределенный : нераспределенный набор данных заключен в strategy.distribute_datasets_from_function .
  • Конечное : набор данных должен читать каждый пример ровно один раз. Набор данных не должен содержать repeat инструкций.
  • worker-sharded : каждый работник должен читать отдельную часть набора данных.

Вот пример:

import tensorflow_decision_forests as tfdf
import tensorflow as tf


def dataset_fn(context, paths):
  """Create a worker-sharded finite dataset from paths.

  Like for non-distributed training, each example should be visited exactly
  once (and by only one worker) during the training. In addition, for optimal
  training speed, the reading of the examples should be distributed among the
  workers (instead of being read by a single worker, or read and discarded
  multiple times).

  In other words, don't add a "repeat" statement and make sure to shard the
  dataset at the file level and not at the example level.
  """

  # List the dataset files
  ds_path = tf.data.Dataset.from_tensor_slices(paths)

  # Make sure the dataset is used with distributed training.
  assert context is not None


  # Split the among the workers.
  #
  # Note: The "shard" is applied on the file path. The shard should not be
  # applied on the examples directly.
  # Note: You cannot use 'context.num_input_pipelines' with ParameterServerV2.
  current_worker = tfdf.keras.get_worker_idx_and_num_workers(context)
  ds_path = ds_path.shard(
      num_shards=current_worker.num_workers,
      index=current_worker.worker_idx)

  def read_csv_file(path):
    """Reads a single csv file."""

    numerical = tf.constant([0.0], dtype=tf.float32)
    categorical_string = tf.constant(["NA"], dtype=tf.string)
    csv_columns = [
        numerical,  # feature 1
        categorical_string,  # feature 2
        numerical,  # feature 3
        # ... define the features here.
    ]
    return tf.data.experimental.CsvDataset(path, csv_columns, header=True)

  ds_columns = ds_path.interleave(read_csv_file)

  # We assume a binary classification label with the following possible values.
  label_values = ["<=50K", ">50K"]

  # Convert the text labels into integers:
  # "<=50K" => 0
  # ">50K" => 1
  init_label_table = tf.lookup.KeyValueTensorInitializer(
      keys=tf.constant(label_values),
      values=tf.constant(range(label_values), dtype=tf.int64))
  label_table = tf.lookup.StaticVocabularyTable(
      init_label_table, num_oov_buckets=1)

  def extract_label(*columns):
    return columns[0:-1], label_table.lookup(columns[-1])

  ds_dataset = ds_columns.map(extract_label)

  # The batch size has no impact on the quality of the model. However, a larger
  # batch size generally is faster.
  ds_dataset = ds_dataset.batch(500)
  return ds_dataset


strategy = tf.distribute.experimental.ParameterServerStrategy(...)
with strategy.scope():
  model = tfdf.keras.DistributedGradientBoostedTreesModel()

  train_dataset = strategy.distribute_datasets_from_function(
      lambda context: dataset_fn(context, [...list of csv files...])
  )

model.fit(train_dataset)

print("Trained model")
model.summary()

Пример. Распределенная настройка гиперпараметров на пути к набору данных.

Распределенная настройка гиперпараметров на пути к набору данных аналогична распределенному обучению. Единственное отличие состоит в том, что этот вариант совместим с нераспространяемыми моделями. Например, вы можете распространить настройку гиперпараметров (нераспределенной) модели деревьев с градиентным усилением.

with strategy.scope():
  tuner = tfdf.tuner.RandomSearch(num_trials=30, use_predefined_hps=True)
  model = tfdf.keras.GradientBoostedTreesModel(tuner=tuner)

training_history = model.fit_on_dataset_path(
  train_path=train_path,
  label_key=label,
  dataset_format="csv",
  valid_path=test_path)

logging.info("Trained model:")
model.summary()

Пример: модульное тестирование

Для модульного тестирования распределенного обучения вы можете создать макеты рабочих процессов. Дополнительную информацию см. в методе _create_in_process_tf_ps_cluster в модульных тестах TF-DF .