Пользовательские петли обучения тензорфлоу

Пользовательские петли обучения тензорфлоу

1 августа 2025 г.

Обзор контента

  • Используйте tf.distribute.strategy с пользовательскими петлями обучения
  • Что сейчас поддерживается?
  • Примеры и учебные пособия
  • Другие темы
  • Настройка переменной среды TF_CONFIG
  • Что дальше?

Используйте tf.distribute.strategy с пользовательскими петлями обучения

Как показано выше, используяtf.distribute.Strategyс керамиModel.fitТребуется изменение только пару строк вашего кода. С немного большим усилием вы также можете использоватьtf.distribute.Strategyс индивидуальными петлями обучения.

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

Аtf.distribute.StrategyКлассы предоставляют основной набор методов для поддержки пользовательских петлей обучения. Использование их может потребовать незначительной реструктуризации кода изначально, но как только это будет сделано, вы сможете переключаться между графическими процессорами, TPU и несколькими машинами, просто изменяя экземпляр стратегии.

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

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

with mirrored_strategy.scope():
  model = tf.keras.Sequential([
      tf.keras.layers.Dense(1, input_shape=(1,),
                            kernel_regularizer=tf.keras.regularizers.L2(1e-4))])
  optimizer = tf.keras.optimizers.SGD()

Далее создайте набор данных ввода и вызовtf.distribute.Strategy.experimental_distribute_datasetРаспределить набор данных на основе стратегии.

dataset = tf.data.Dataset.from_tensors(([1.], [1.])).repeat(1000).batch(
    global_batch_size)
dist_dataset = mirrored_strategy.experimental_distribute_dataset(dataset)

Затем определите один шаг обучения. Использоватьtf.GradientTapeЧтобы вычислить градиенты и оптимизатор для применения этих градиентов для обновления переменных вашей модели. Чтобы распределить этот шаг обучения, поместите его в функциюtrain_stepи передать этоtf.distribute.Strategy.runвместе с входами набора данных, которые вы получили отdist_datasetСоздано ранее:

# Sets `reduction=NONE` to leave it to tf.nn.compute_average_loss() below.
loss_object = tf.keras.losses.BinaryCrossentropy(
  from_logits=True,
  reduction=tf.keras.losses.Reduction.NONE)

def train_step(inputs):
  features, labels = inputs

  with tf.GradientTape() as tape:
    predictions = model(features, training=True)
    per_example_loss = loss_object(labels, predictions)
    loss = tf.nn.compute_average_loss(per_example_loss)
    model_losses = model.losses
    if model_losses:
      loss += tf.nn.scale_regularization_loss(tf.add_n(model_losses))

  gradients = tape.gradient(loss, model.trainable_variables)
  optimizer.apply_gradients(zip(gradients, model.trainable_variables))
  return loss

@tf.function
def distributed_train_step(dist_inputs):
  per_replica_losses = mirrored_strategy.run(train_step, args=(dist_inputs,))
  return mirrored_strategy.reduce(tf.distribute.ReduceOp.SUM, per_replica_losses,
                         axis=None)

Несколько других вещей, которые следует отметить в коде выше:

  1. Вы использовалиtf.nn.compute_average_lossЧтобы уменьшить потери прогнозирования в первом доме на скаляр.tf.nn.compute_average_lossсуммирует потерю за пример и делит сумму на глобальный размер партии. Это важно, потому что позже, после того как градиенты рассчитываются на каждой копии, они агрегируются по репликасуммированиеих.

    По умолчанию, глобальный размер партии принимается какtf.get_strategy().num_replicas_in_sync * tf.shape(per_example_loss)[0]Полем Это также может быть указано явно как аргумент ключевого словаglobal_batch_size=Полем Без коротких партий по умолчанию эквивалентноtf.nn.compute_average_loss(..., global_batch_size=global_batch_size)сglobal_batch_sizeопределено выше. (Для получения дополнительной информации о коротких партиях и о том, как избежать или обработать их, увидетьУчебное пособие по индивидуальному обучению.)

  2. Вы использовалиtf.nn.scale_regularization_lossчтобы масштабировать потери регуляризации, зарегистрированные вModelобъект, если таковой имеется, по1/num_replicas_in_syncтакже. Для тех потерь регуляризации, которые зависят от ввода, он падает на код моделирования, а не на пользовательскую петлю обучения, чтобы выполнить усреднение по размеру партии (!) (!); Таким образом, код моделирования может оставаться агностиком репликации, в то время как учебный цикл остается агностиком в том, как вычисляются потери регуляризации.

  3. Когда вы звонитеapply_gradientsВ рамках стратегии распределения его поведение изменяется. В частности, перед применением градиентов на каждом параллельном экземпляре во время синхронной тренировки он выполняет сумму-все-повторные градиенты.

  4. Вы также использовалиtf.distribute.Strategy.reduceAPI для объединения результатов, возвращенныхtf.distribute.Strategy.runдля отчетности.tf.distribute.Strategy.runВозвращает результаты из каждой локальной копии в стратегии, и есть несколько способов потребления этого результата. Ты можешьreduceих, чтобы получить агрегированную ценность. Вы также можете сделатьtf.distribute.Strategy.experimental_local_resultsЧтобы получить список значений, содержащихся в результате, по одному на локальную копию.

Наконец, как только вы определите шаг обучения, вы можете перевернутьdist_datasetи запустить обучение в цикле:

for dist_inputs in dist_dataset:
  print(distributed_train_step(dist_inputs))

tf.Tensor(0.9024367, shape=(), dtype=float32)
tf.Tensor(0.8953863, shape=(), dtype=float32)
tf.Tensor(0.8884038, shape=(), dtype=float32)
tf.Tensor(0.88148874, shape=(), dtype=float32)
tf.Tensor(0.87464076, shape=(), dtype=float32)
tf.Tensor(0.86785895, shape=(), dtype=float32)
tf.Tensor(0.86114323, shape=(), dtype=float32)
tf.Tensor(0.8544927, shape=(), dtype=float32)
tf.Tensor(0.84790725, shape=(), dtype=float32)
tf.Tensor(0.841386, shape=(), dtype=float32)
tf.Tensor(0.83492863, shape=(), dtype=float32)
tf.Tensor(0.8285344, shape=(), dtype=float32)
tf.Tensor(0.82220304, shape=(), dtype=float32)
tf.Tensor(0.8159339, shape=(), dtype=float32)
tf.Tensor(0.8097264, shape=(), dtype=float32)
tf.Tensor(0.8035801, shape=(), dtype=float32)
tf.Tensor(0.79749453, shape=(), dtype=float32)
tf.Tensor(0.79146886, shape=(), dtype=float32)
tf.Tensor(0.785503, shape=(), dtype=float32)
tf.Tensor(0.779596, shape=(), dtype=float32)
tf.Tensor(0.77374756, shape=(), dtype=float32)
tf.Tensor(0.7679571, shape=(), dtype=float32)
tf.Tensor(0.7622242, shape=(), dtype=float32)
tf.Tensor(0.7565481, shape=(), dtype=float32)
tf.Tensor(0.75092846, shape=(), dtype=float32)
tf.Tensor(0.7453647, shape=(), dtype=float32)
tf.Tensor(0.73985624, shape=(), dtype=float32)
tf.Tensor(0.7344028, shape=(), dtype=float32)
tf.Tensor(0.7290035, shape=(), dtype=float32)
tf.Tensor(0.723658, shape=(), dtype=float32)
tf.Tensor(0.7183659, shape=(), dtype=float32)
tf.Tensor(0.71312654, shape=(), dtype=float32)
tf.Tensor(0.7079393, shape=(), dtype=float32)
tf.Tensor(0.70280397, shape=(), dtype=float32)
tf.Tensor(0.6977197, shape=(), dtype=float32)
tf.Tensor(0.69268626, shape=(), dtype=float32)
tf.Tensor(0.687703, shape=(), dtype=float32)
tf.Tensor(0.68276954, shape=(), dtype=float32)
tf.Tensor(0.67788523, shape=(), dtype=float32)
tf.Tensor(0.6730496, shape=(), dtype=float32)
tf.Tensor(0.66826224, shape=(), dtype=float32)
tf.Tensor(0.66352266, shape=(), dtype=float32)
tf.Tensor(0.6588302, shape=(), dtype=float32)
tf.Tensor(0.6541846, shape=(), dtype=float32)
tf.Tensor(0.6495853, shape=(), dtype=float32)
tf.Tensor(0.64503175, shape=(), dtype=float32)
tf.Tensor(0.6405235, shape=(), dtype=float32)
tf.Tensor(0.6360602, shape=(), dtype=float32)
tf.Tensor(0.6316412, shape=(), dtype=float32)
tf.Tensor(0.62726617, shape=(), dtype=float32)
tf.Tensor(0.6229345, shape=(), dtype=float32)
tf.Tensor(0.61864597, shape=(), dtype=float32)
tf.Tensor(0.6143999, shape=(), dtype=float32)
tf.Tensor(0.6101959, shape=(), dtype=float32)
tf.Tensor(0.60603356, shape=(), dtype=float32)
tf.Tensor(0.60191244, shape=(), dtype=float32)
tf.Tensor(0.597832, shape=(), dtype=float32)
tf.Tensor(0.5937919, shape=(), dtype=float32)
tf.Tensor(0.5897917, shape=(), dtype=float32)
tf.Tensor(0.585831, shape=(), dtype=float32)
tf.Tensor(0.58190924, shape=(), dtype=float32)
tf.Tensor(0.5780261, shape=(), dtype=float32)
tf.Tensor(0.57418114, shape=(), dtype=float32)
tf.Tensor(0.57037395, shape=(), dtype=float32)
tf.Tensor(0.5666041, shape=(), dtype=float32)
tf.Tensor(0.56287116, shape=(), dtype=float32)
tf.Tensor(0.55917484, shape=(), dtype=float32)
tf.Tensor(0.5555145, shape=(), dtype=float32)
tf.Tensor(0.55189, shape=(), dtype=float32)
tf.Tensor(0.54830086, shape=(), dtype=float32)
tf.Tensor(0.54474664, shape=(), dtype=float32)
tf.Tensor(0.54122704, shape=(), dtype=float32)
tf.Tensor(0.5377416, shape=(), dtype=float32)
tf.Tensor(0.5342899, shape=(), dtype=float32)
tf.Tensor(0.5308717, shape=(), dtype=float32)
tf.Tensor(0.5274865, shape=(), dtype=float32)
tf.Tensor(0.52413404, shape=(), dtype=float32)
tf.Tensor(0.52081394, shape=(), dtype=float32)
tf.Tensor(0.51752573, shape=(), dtype=float32)
tf.Tensor(0.5142692, shape=(), dtype=float32)
tf.Tensor(0.51104385, shape=(), dtype=float32)
tf.Tensor(0.50784945, shape=(), dtype=float32)
tf.Tensor(0.50468564, shape=(), dtype=float32)
tf.Tensor(0.50155205, shape=(), dtype=float32)
tf.Tensor(0.49844825, shape=(), dtype=float32)
tf.Tensor(0.4953741, shape=(), dtype=float32)
tf.Tensor(0.49232918, shape=(), dtype=float32)
tf.Tensor(0.4893132, shape=(), dtype=float32)
tf.Tensor(0.48632562, shape=(), dtype=float32)
tf.Tensor(0.4833664, shape=(), dtype=float32)
tf.Tensor(0.4804351, shape=(), dtype=float32)
tf.Tensor(0.47753143, shape=(), dtype=float32)
tf.Tensor(0.47465506, shape=(), dtype=float32)
tf.Tensor(0.47180572, shape=(), dtype=float32)
tf.Tensor(0.46898302, shape=(), dtype=float32)
tf.Tensor(0.4661867, shape=(), dtype=float32)
tf.Tensor(0.46341658, shape=(), dtype=float32)
tf.Tensor(0.4606722, shape=(), dtype=float32)
tf.Tensor(0.4579534, shape=(), dtype=float32)
tf.Tensor(0.4552598, shape=(), dtype=float32)
tf.Tensor(0.45259115, shape=(), dtype=float32)
tf.Tensor(0.44994718, shape=(), dtype=float32)
tf.Tensor(0.44732755, shape=(), dtype=float32)
tf.Tensor(0.44473216, shape=(), dtype=float32)
tf.Tensor(0.44216052, shape=(), dtype=float32)
tf.Tensor(0.4396125, shape=(), dtype=float32)
tf.Tensor(0.43708783, shape=(), dtype=float32)
tf.Tensor(0.4345862, shape=(), dtype=float32)
tf.Tensor(0.4321074, shape=(), dtype=float32)
tf.Tensor(0.42965108, shape=(), dtype=float32)
tf.Tensor(0.4272171, shape=(), dtype=float32)
tf.Tensor(0.42480516, shape=(), dtype=float32)
tf.Tensor(0.42241505, shape=(), dtype=float32)
tf.Tensor(0.42004645, shape=(), dtype=float32)
tf.Tensor(0.41769922, shape=(), dtype=float32)
tf.Tensor(0.41537297, shape=(), dtype=float32)
tf.Tensor(0.41306767, shape=(), dtype=float32)
tf.Tensor(0.41078293, shape=(), dtype=float32)
tf.Tensor(0.4085186, shape=(), dtype=float32)
tf.Tensor(0.4062744, shape=(), dtype=float32)
tf.Tensor(0.4040502, shape=(), dtype=float32)
tf.Tensor(0.40184572, shape=(), dtype=float32)
tf.Tensor(0.39966068, shape=(), dtype=float32)
tf.Tensor(0.3974949, shape=(), dtype=float32)
tf.Tensor(0.39534825, shape=(), dtype=float32)
tf.Tensor(0.39322042, shape=(), dtype=float32)
tf.Tensor(0.39111122, shape=(), dtype=float32)
tf.Tensor(0.3890205, shape=(), dtype=float32)
tf.Tensor(0.38694802, shape=(), dtype=float32)
tf.Tensor(0.38489357, shape=(), dtype=float32)
tf.Tensor(0.38285697, shape=(), dtype=float32)
tf.Tensor(0.38083804, shape=(), dtype=float32)
tf.Tensor(0.3788365, shape=(), dtype=float32)
tf.Tensor(0.37685227, shape=(), dtype=float32)
tf.Tensor(0.3748851, shape=(), dtype=float32)
tf.Tensor(0.37293482, shape=(), dtype=float32)
tf.Tensor(0.37100127, shape=(), dtype=float32)
tf.Tensor(0.36908418, shape=(), dtype=float32)
tf.Tensor(0.36718345, shape=(), dtype=float32)
tf.Tensor(0.3652989, shape=(), dtype=float32)
tf.Tensor(0.36343032, shape=(), dtype=float32)
tf.Tensor(0.36157757, shape=(), dtype=float32)
tf.Tensor(0.35974047, shape=(), dtype=float32)
tf.Tensor(0.3579188, shape=(), dtype=float32)
tf.Tensor(0.35611248, shape=(), dtype=float32)
tf.Tensor(0.3543213, shape=(), dtype=float32)
tf.Tensor(0.35254508, shape=(), dtype=float32)
tf.Tensor(0.3507837, shape=(), dtype=float32)
tf.Tensor(0.34903696, shape=(), dtype=float32)
tf.Tensor(0.34730473, shape=(), dtype=float32)
tf.Tensor(0.3455869, shape=(), dtype=float32)
tf.Tensor(0.3438832, shape=(), dtype=float32)
tf.Tensor(0.34219357, shape=(), dtype=float32)
tf.Tensor(0.3405178, shape=(), dtype=float32)
tf.Tensor(0.3388558, shape=(), dtype=float32)
tf.Tensor(0.3372074, shape=(), dtype=float32)
tf.Tensor(0.33557245, shape=(), dtype=float32)
tf.Tensor(0.33395082, shape=(), dtype=float32)
tf.Tensor(0.33234236, shape=(), dtype=float32)
tf.Tensor(0.33074695, shape=(), dtype=float32)
tf.Tensor(0.32916442, shape=(), dtype=float32)
tf.Tensor(0.3275946, shape=(), dtype=float32)
tf.Tensor(0.3260375, shape=(), dtype=float32)
tf.Tensor(0.3244928, shape=(), dtype=float32)
tf.Tensor(0.3229605, shape=(), dtype=float32)
tf.Tensor(0.32144046, shape=(), dtype=float32)
tf.Tensor(0.31993246, shape=(), dtype=float32)
tf.Tensor(0.3184365, shape=(), dtype=float32)
tf.Tensor(0.31695238, shape=(), dtype=float32)
tf.Tensor(0.31548, shape=(), dtype=float32)
tf.Tensor(0.31401917, shape=(), dtype=float32)
tf.Tensor(0.3125699, shape=(), dtype=float32)
tf.Tensor(0.31113195, shape=(), dtype=float32)
tf.Tensor(0.30970532, shape=(), dtype=float32)
tf.Tensor(0.3082898, shape=(), dtype=float32)
tf.Tensor(0.30688527, shape=(), dtype=float32)
tf.Tensor(0.3054917, shape=(), dtype=float32)
tf.Tensor(0.30410892, shape=(), dtype=float32)
tf.Tensor(0.3027368, shape=(), dtype=float32)
tf.Tensor(0.30137527, shape=(), dtype=float32)
tf.Tensor(0.3000242, shape=(), dtype=float32)
tf.Tensor(0.29868355, shape=(), dtype=float32)
tf.Tensor(0.29735315, shape=(), dtype=float32)
tf.Tensor(0.29603288, shape=(), dtype=float32)
tf.Tensor(0.29472268, shape=(), dtype=float32)
tf.Tensor(0.2934224, shape=(), dtype=float32)
tf.Tensor(0.29213202, shape=(), dtype=float32)
tf.Tensor(0.29085135, shape=(), dtype=float32)
tf.Tensor(0.28958035, shape=(), dtype=float32)
tf.Tensor(0.2883189, shape=(), dtype=float32)
tf.Tensor(0.28706694, shape=(), dtype=float32)
tf.Tensor(0.28582436, shape=(), dtype=float32)
tf.Tensor(0.28459102, shape=(), dtype=float32)
tf.Tensor(0.28336692, shape=(), dtype=float32)
tf.Tensor(0.2821518, shape=(), dtype=float32)
tf.Tensor(0.28094578, shape=(), dtype=float32)
tf.Tensor(0.27974862, shape=(), dtype=float32)
tf.Tensor(0.2785603, shape=(), dtype=float32)
tf.Tensor(0.27738073, shape=(), dtype=float32)
tf.Tensor(0.2762098, shape=(), dtype=float32)

В приведенном выше примере вы итералировали надdist_datasetЧтобы внести свой вклад в ваше обучение. Вам также предоставленыtf.distribute.Strategy.make_experimental_numpy_datasetЧтобы поддержать входные данные Numpy. Вы можете использовать этот API для создания набора данных перед вызовомtf.distribute.Strategy.experimental_distribute_datasetПолем

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

iterator = iter(dist_dataset)
for _ in range(10):
  print(distributed_train_step(next(iterator)))

tf.Tensor(0.27504745, shape=(), dtype=float32)
tf.Tensor(0.2738936, shape=(), dtype=float32)
tf.Tensor(0.2727481, shape=(), dtype=float32)
tf.Tensor(0.27161098, shape=(), dtype=float32)
tf.Tensor(0.27048206, shape=(), dtype=float32)
tf.Tensor(0.26936132, shape=(), dtype=float32)
tf.Tensor(0.26824862, shape=(), dtype=float32)
tf.Tensor(0.26714393, shape=(), dtype=float32)
tf.Tensor(0.26604718, shape=(), dtype=float32)
tf.Tensor(0.26495826, shape=(), dtype=float32)

Это охватывает самый простой случай использованияtf.distribute.StrategyAPI для распространения пользовательских петлей обучения.

Что сейчас поддерживается?

Обучение API

MirroredStrategy

TPUStrategy

MultiWorkerMirroredStrategy

ParameterServerStrategy

CentralStorageStrategy

Пользовательская учебная петля

Поддерживается

Поддерживается

Поддерживается

Экспериментальная поддержка

Экспериментальная поддержка

Примеры и учебные пособия

Вот несколько примеров использования стратегий распространения с пользовательскими петлями обучения:

  1. Учебник: Обучение с помощью индивидуальной тренировочной петли иMirroredStrategyПолем
  2. Учебник: Обучение с помощью индивидуальной тренировочной петли иMultiWorkerMirroredStrategyПолем
  3. Гид: Содержит пример индивидуальной тренировочной петли сTPUStrategyПолем
  4. Учебник: Обучение сервера параметров с помощью пользовательской учебной петли иParameterServerStrategyПолем
  5. Tensorflow Model Gardenрепозиторийсодержащий коллекции современных моделей, реализованных с использованием различных стратегий.

Другие темы

Этот раздел охватывает некоторые темы, которые имеют отношение к множеству вариантов использования.

Настройка переменной среды TF_CONFIG

Для обучения с несколькими работниками, как упоминалось ранее, вам нужно настроить'TF_CONFIG'Переменная среда для каждого бинарного запуска в вашем кластере. А'TF_CONFIG'Переменная среда - это строка JSON, которая указывает, какие задачи представляют собой кластер, их адреса и роль каждой задачи в кластере. Аtensorflow/ecosystemRepo предоставляет шаблон Kubernetes, который устанавливает'TF_CONFIG'для ваших тренировочных задач.

Есть два компонента'TF_CONFIG': кластер и задача.

  • Кластер предоставляет информацию о учебном кластере, который является диктом, состоящим из различных типов рабочих мест, таких как работники. При обучении с несколькими работниками обычно есть один работник, который берет на себя немного большую ответственность, например, сохранение контрольно-пропускного пункта и написание сводного файла для Tensorboard в дополнение к тому, что делает обычный работник. Такой работник называется «главным» работником, и обычно работник с индексом0назначается главным работником (на самом деле так и какtf.distribute.Strategyреализован).
  • С другой стороны, задача предоставляет информацию о текущей задаче. Первый компонентный кластер одинакова для всех работников, а вторая задача компонента отличается для каждого работника и указывает тип и индекс этого работника.

Один пример'TF_CONFIG'является:

os.environ["TF_CONFIG"] = json.dumps({
    "cluster": {
        "worker": ["host1:port", "host2:port", "host3:port"],
        "ps": ["host4:port", "host5:port"]
    },
   "task": {"type": "worker", "index": 1}
})

Этот'TF_CONFIG'указывает, что есть три работника и два"ps"Задачи в"cluster"вместе с их хозяевами и портами. А"task"часть указывает роль текущей задачи в"cluster"- Работник1(второй работник). Допустимые роли в кластере"chief"В"worker"В"ps", и"evaluator"Полем Там не должно быть"ps"работа, кроме как при использованииtf.distribute.experimental.ParameterServerStrategyПолем

Что дальше?

tf.distribute.Strategyактивно находится в стадии разработки. Попробуйте это и предоставьте ваш отзыв, используяGitHub выпускиПолем

Первоначально опубликовано наTensorflowВеб -сайт, эта статья появляется здесь под новым заголовком и имеет лицензию в CC на 4.0. Образцы кода, разделенные по лицензии Apache 2.0.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE