tf.distribute.Strategy
Политика распределения состояния и вычислений по списку устройств.
tf.distribute.Strategy(
extended
)
Обзор и примеры см. в руководстве. Глоссарий понятий, упомянутых на этой странице, таких как «на реплику», реплика и сведение, см. в разделах tf.distribute.StrategyExtended и tf.distribute.
Кратко:
- Чтобы использовать это с Keras
compile/fit, ознакомьтесь с инструкцией. - В противном случае используйте
tf.distribute.Strategy.scope, чтобы указать, что при построении и выполнении модели следует использовать стратегию. (В результате вы окажетесь в «контексте между репликами» этой стратегии, то есть стратегия будет управлять такими аспектами, как размещение переменных.) -
Если вы пишете собственный цикл обучения, потребуется вызвать еще несколько методов; см. руководство:
- Сначала создайте обычный объект
tf.data.Dataset. - Используйте
tf.distribute.Strategy.experimental_distribute_dataset, чтобы преобразоватьtf.data.Datasetв объект, создающий значения «на реплику». Если вы хотите вручную указать, как набор данных должен распределяться между репликами, вместо этого используйтеtf.distribute.Strategy.distribute_datasets_from_function. - Используйте
tf.distribute.Strategy.run, чтобы выполнить функцию один раз для каждой реплики. Функция принимает значения, которые могут быть значениями «на реплику» (например, из объектаtf.distribute.DistributedDataset), и возвращает значения «на реплику». Эта функция выполняется в «контексте реплики», то есть каждая операция выполняется отдельно на каждой реплике. - Наконец, используйте метод (например,
tf.distribute.Strategy.reduce), чтобы преобразовать полученные значения «на реплику» в обычныеTensor.
- Сначала создайте обычный объект
Собственный цикл обучения может быть очень простым:
with my_strategy.scope():
@tf.function
def distribute_train_epoch(dataset):
def replica_fn(input):
# process input and return result
return result
total_result = 0
for x in dataset:
per_replica_result = my_strategy.run(replica_fn, args=(x,))
total_result += my_strategy.reduce(tf.distribute.ReduceOp.SUM,
per_replica_result, axis=None)
return total_result
dist_dataset = my_strategy.experimental_distribute_dataset(dataset)
for _ in range(EPOCHS):
train_result = distribute_train_epoch(dist_dataset)
Этот код принимает обычные dataset и replica_fn и выполняет их распределенно с помощью указанной tf.distribute.Strategy, названной выше my_strategy. Все переменные, созданные в replica_fn, создаются с использованием политики my_strategy, а библиотечные функции, вызываемые replica_fn, могут использовать API get_replica_context() для реализации специфичного для распределенных вычислений поведения.
Вы можете использовать API reduce для агрегирования результатов по репликам и возвращать их как результат одной итерации по tf.distribute.DistributedDataset. Также можно использовать tf.keras.metrics (например, для потерь, точности и т. д.), чтобы накапливать метрики на протяжении шагов заданной эпохи.
Более подробный пример см. в руководстве по собственным циклам обучения.
Примечание: В настоящее время tf.distribute.Strategy не поддерживает секционированные переменные TensorFlow (когда одна переменная разделена между несколькими устройствами).
| Атрибуты | |
|---|---|
cluster_resolver | Возвращает сопоставитель кластера, связанный с этой стратегией. Как правило, при использовании стратегии с несколькими рабочими узлами Стратегии, для которых предусмотрен связанный объект Стратегии с одним рабочим узлом обычно не имеют объекта Объект
os.environ['TF_CONFIG'] = json.dumps({
'cluster': {
'worker': ["localhost:12345", "localhost:23456"],
'ps': ["localhost:34567"]
},
'task': {'type': 'worker', 'index': 0}
})
# This implicitly uses TF_CONFIG for the cluster and current task info.
strategy = tf.distribute.experimental.MultiWorkerMirroredStrategy()
...
if strategy.cluster_resolver.task_type == 'worker':
# Perform something that's only applicable on workers. Since we set this
# as a worker above, this block will run on this particular instance.
elif strategy.cluster_resolver.task_type == 'ps':
# Perform something that's only applicable on parameter servers. Since we
# set this as a worker above, this block will not run on this particular
# instance.
Дополнительные сведения см. в документации API для |
extended | tf.distribute.StrategyExtended с дополнительными методами. |
num_replicas_in_sync | Возвращает количество реплик, по которым агрегируются градиенты. |
Методы
distribute_datasets_from_function
distribute_datasets_from_function(
dataset_fn, options=None
)
Распределяет экземпляры tf.data.Dataset, созданные вызовами dataset_fn.
Аргумент dataset_fn, передаваемый пользователем, — это входная функция, которая принимает аргумент tf.distribute.InputContext и возвращает экземпляр tf.data.Dataset. Предполагается, что набор данных, возвращаемый dataset_fn, уже разбит на пакеты размером на одну реплику (то есть глобальный размер пакета, разделенный на количество синхронизированных реплик) и разделен на шарды. tf.distribute.Strategy.distribute_datasets_from_function не разбивает на пакеты и шарды экземпляр tf.data.Dataset, возвращаемый входной функцией. dataset_fn вызывается на устройстве CPU каждого рабочего узла, и каждый вызов создает набор данных, из которого каждая реплика этого рабочего узла извлекает один пакет входных данных (то есть если у рабочего узла две реплики, на каждом шаге из Dataset извлекаются два пакета).
Этот метод можно использовать для нескольких целей. Во-первых, он позволяет задать собственную логику разбиения на пакеты и шарды. (В отличие от этого, tf.distribute.experimental_distribute_dataset выполняет разбиение на пакеты и шарды автоматически.) Например, если experimental_distribute_dataset не может разделить входные файлы на шарды, этот метод можно использовать для ручного разделения набора данных на шарды (что позволяет избежать медленного резервного поведения в experimental_distribute_dataset). Если набор данных бесконечен, такое разделение на шарды можно выполнить, создав копии набора данных, отличающиеся только случайным начальным значением.
Функция dataset_fn должна принимать экземпляр tf.distribute.InputContext, содержащий сведения о разбиении на пакеты и репликации входных данных.
С помощью свойства element_spec объекта tf.distribute.DistributedDataset, возвращаемого этим API, можно запросить tf.TypeSpec элементов, возвращаемых итератором. Это можно использовать для настройки свойства input_signature объекта tf.function. Пример см. в разделе tf.distribute.DistributedDataset.element_spec.
Примечание: При использовании TPUStrategy порядок обработки данных рабочими узлами при использованииtf.distribute.Strategy.experimental_distribute_datasetилиtf.distribute.Strategy.distribute_datasets_from_functionне гарантируется. Это обычно важно, если вы используетеtf.distributeдля масштабирования предсказаний. Однако можно добавить индекс для каждого элемента пакета и упорядочить результаты соответствующим образом. Пример упорядочивания результатов см. в этом фрагменте кода.
Примечание: В настоящее время преобразования наборов данных с состоянием не поддерживаются дляtf.distribute.experimental_distribute_datasetилиtf.distribute.distribute_datasets_from_function. Все операции с состоянием, которые могут присутствовать в наборе данных, игнорируются. Например, если в наборе данных естьmap_fn, использующийtf.random.uniformдля поворота изображения, то граф набора данных зависит от состояния (то есть от случайного начального значения) на локальном компьютере, где выполняется процесс Python.
Руководство по использованию и свойствам этого метода см. в руководстве по распределенной обработке входных данных). Если вас интересует обработка последнего неполного пакета, прочитайте этот раздел.
| Аргументы | |
|---|---|
dataset_fn | Функция, принимающая экземпляр tf.distribute.InputContext и возвращающая tf.data.Dataset. |
options | tf.distribute.InputOptions, используемый для управления параметрами распределения этого набора данных. |
| Возвращает | |
|---|---|
Объект tf.distribute.DistributedDataset. |
experimental_distribute_dataset
experimental_distribute_dataset(
dataset, options=None
)
Создает tf.distribute.DistributedDataset из tf.data.Dataset.
Возвращаемый объект tf.distribute.DistributedDataset можно перебирать так же, как обычные наборы данных. ПРИМЕЧАНИЕ. Пользователь не может добавлять преобразования к объекту tf.distribute.DistributedDataset. Можно только создать итератор или проверить tf.TypeSpec создаваемых им данных. Дополнительные сведения см. в документации API для tf.distribute.DistributedDataset.
Пример:
global_batch_size = 2
# Passing the devices is optional.
strategy = tf.distribute.MirroredStrategy(devices=["GPU:0", "GPU:1"])
# Create a dataset
dataset = tf.data.Dataset.range(4).batch(global_batch_size)
# Distribute that dataset
dist_dataset = strategy.experimental_distribute_dataset(dataset)
@tf.function
def replica_fn(input):
return input*2
result = []
# Iterate over the `tf.distribute.DistributedDataset`
for x in dist_dataset:
# process dataset elements
result.append(strategy.run(replica_fn, args=(x,)))
print(result)
[PerReplica:{
0: <tf.Tensor: shape=(1,), dtype=int64, numpy=array([0])>,
1: <tf.Tensor: shape=(1,), dtype=int64, numpy=array([2])>
}, PerReplica:{
0: <tf.Tensor: shape=(1,), dtype=int64, numpy=array([4])>,
1: <tf.Tensor: shape=(1,), dtype=int64, numpy=array([6])>
}]Этот метод выполняет три ключевых действия: разбиение на пакеты, разделение на шарды и предварительную выборку.
В приведенном выше фрагменте кода dataset разбивается на пакеты с помощью global_batch_size, а вызов experimental_distribute_dataset повторно разбивает dataset, задавая новый размер пакета, равный глобальному размеру пакета, деленному на количество синхронизированных реплик. Перебор выполняется с помощью цикла for в стиле Python. x — это tf.distribute.DistributedValues, содержащий данные для всех реплик, и каждая реплика получает данные нового размера пакета. tf.distribute.Strategy.run передает правильные данные на реплику из x соответствующему replica_fn, выполняемому на каждой реплике.
Разделение на шарды включает автоматическое распределение между несколькими рабочими узлами и внутри каждого рабочего узла. Во-первых, при распределенном обучении на нескольких рабочих узлах (то есть при использовании tf.distribute.experimental.MultiWorkerMirroredStrategy или tf.distribute.TPUStrategy) автоматическое разделение набора данных на шарды по рабочим узлам означает, что каждому рабочему узлу назначается подмножество всего набора данных (если задано подходящее значение tf.data.experimental.AutoShardPolicy). Это гарантирует, что на каждом шаге каждый рабочий узел обработает глобальный пакет элементов набора данных без пересечений. Для автоматического разделения на шарды предусмотрено несколько вариантов, которые можно указать с помощью tf.data.experimental.DistributeOptions. Затем данные разделяются между устройствами каждого рабочего узла (если их больше одного). Это происходит независимо от автоматического разделения на шарды между рабочими узлами.
Примечание: Для автоматического разделения на шарды между несколькими рабочими узлами по умолчанию используется режимtf.data.experimental.AutoShardPolicy.AUTO. В этом режиме предпринимается попытка разделить входной набор данных на шарды по файлам, если он создается из наборов данных для чтения (например,tf.data.TFRecordDataset,tf.data.TextLineDatasetи т. д.); в противном случае набор данных разделяется по данным, и каждый рабочий узел считывает весь набор данных, обрабатывая только назначенный ему шард. Однако если на каждый рабочий узел приходится меньше одного входного файла, рекомендуется отключить автоматическое разделение наборов данных на шарды между рабочими узлами, задав дляtf.data.experimental.DistributeOptions.auto_shard_policyзначениеtf.data.experimental.AutoShardPolicy.OFF.
По умолчанию этот метод добавляет преобразование предварительной выборки в конец предоставленного пользователем экземпляра tf.data.Dataset. Аргумент преобразования предварительной выборки, buffer_size, равен количеству синхронизированных реплик.
Если описанная выше логика разделения пакетов и наборов данных на шарды вам не подходит, используйте вместо этого tf.distribute.Strategy.distribute_datasets_from_function, который не выполняет автоматическое разбиение на пакеты и шарды.
Примечание: При использовании TPUStrategy порядок обработки данных рабочими узлами при использованииtf.distribute.Strategy.experimental_distribute_datasetилиtf.distribute.Strategy.distribute_datasets_from_functionне гарантируется. Это обычно важно, если вы используетеtf.distributeдля масштабирования предсказаний. Однако можно добавить индекс для каждого элемента пакета и упорядочить результаты соответствующим образом. Пример упорядочивания результатов см. в этом фрагменте кода.
Примечание: В настоящее время преобразования наборов данных с состоянием не поддерживаются дляtf.distribute.experimental_distribute_datasetилиtf.distribute.distribute_datasets_from_function. Все операции с состоянием, которые могут присутствовать в наборе данных, игнорируются. Например, если в наборе данных естьmap_fn, использующийtf.random.uniformдля поворота изображения, то граф набора данных зависит от состояния (то есть от случайного начального значения) на локальном компьютере, где выполняется процесс Python.
Руководство по использованию и свойствам этого метода см. в руководстве по распределенной обработке входных данных. Если вас интересует обработка последнего неполного пакета, прочитайте этот раздел.
| Аргументы | |
|---|---|
dataset | tf.data.Dataset, который будет разделен между всеми репликами на шарды согласно приведенным выше правилам. |
options | tf.distribute.InputOptions, используемый для управления параметрами распределения этого набора данных. |
| Возвращает | |
|---|---|
Объект tf.distribute.DistributedDataset. |
experimental_distribute_values_from_function
experimental_distribute_values_from_function(
value_fn
)
Создает tf.distribute.DistributedValues из value_fn.
Эта функция создает tf.distribute.DistributedValues для передачи в run, reduce или другие методы, принимающие распределенные значения, если наборы данных не используются.
| Аргументы | |
|---|---|
value_fn | Функция для создания значений. Она вызывается для каждой реплики, и единственным аргументом ей передается tf.distribute.ValueContext. Она должна возвращать тензор или тип, который можно преобразовать в тензор. |
| Возвращает | |
|---|---|
Объект tf.distribute.DistributedValues, содержащий значение для каждой реплики. |
Примеры использования:
-
Возврат постоянного значения для каждой реплики:
strategy = tf.distribute.MirroredStrategy(["GPU:0", "GPU:1"]) def value_fn(ctx): return tf.constant(1.) distributed_values = ( strategy.experimental_distribute_values_from_function( value_fn)) local_result = strategy.experimental_local_results( distributed_values) local_result (<tf.Tensor: shape=(), dtype=float32, numpy=1.0>, <tf.Tensor: shape=(), dtype=float32, numpy=1.0>) -
Распределение значений в массиве на основе replica_id:
strategy = tf.distribute.MirroredStrategy(["GPU:0", "GPU:1"]) array_value = np.array([3., 2., 1.]) def value_fn(ctx): return array_value[ctx.replica_id_in_sync_group] distributed_values = ( strategy.experimental_distribute_values_from_function( value_fn)) local_result = strategy.experimental_local_results( distributed_values) local_result (3.0, 2.0) -
Задание значений с помощью num_replicas_in_sync:
strategy = tf.distribute.MirroredStrategy(["GPU:0", "GPU:1"]) def value_fn(ctx): return ctx.num_replicas_in_sync distributed_values = ( strategy.experimental_distribute_values_from_function( value_fn)) local_result = strategy.experimental_local_results( distributed_values) local_result (2, 2) -
Размещение значений на устройствах и их распределение:
strategy = tf.distribute.TPUStrategy() worker_devices = strategy.extended.worker_devices multiple_values = [] for i in range(strategy.num_replicas_in_sync): with tf.device(worker_devices[i]): multiple_values.append(tf.constant(1.0)) def value_fn(ctx): return multiple_values[ctx.replica_id_in_sync_group] distributed_values = strategy. experimental_distribute_values_from_function( value_fn)
experimental_local_results
experimental_local_results(
value
)
Возвращает список всех локальных значений на репликах, содержащихся в value.
Примечание: Возвращаются только значения на рабочем узле, инициированном этим клиентом. При использованииtf.distribute.Strategy, напримерtf.distribute.experimental.MultiWorkerMirroredStrategy, каждый рабочий узел является отдельным клиентом, и эта функция возвращает только значения, вычисленные на этом рабочем узле.
| Аргументы | |
|---|---|
value | Значение, возвращаемое experimental_run(), run(), or a variable created inscope`. |
| Возвращает | |
|---|---|
Кортеж значений, содержащихся в value, где i-й элемент соответствует i-й реплике. Если value представляет одно значение, возвращается (value,). |
gather
gather(
value, axis
)
Собирает value по репликам вдоль axis на текущем устройстве.
Для объекта tf.distribute.DistributedValues или объекта типа tf.Tensor value этот API собирает и объединяет в конкатенацию value по репликам вдоль измерения axis-го порядка. Результат копируется на «текущее» устройство, которым обычно является CPU рабочего узла, на котором выполняется программа. Для tf.distribute.TPUStrategy это первый хост TPU. Для многоклиентской стратегии tf.distribute.MultiWorkerMirroredStrategy это CPU каждого рабочего узла.
Этот API можно вызывать только в контексте между репликами. Соответствующий метод для контекста реплики см. в разделе tf.distribute.ReplicaContext.all_gather.
Примечание: Для всех стратегий, кромеtf.distribute.TPUStrategy, входные данныеvalueна разных репликах должны иметь одинаковый ранг, а их формы должны совпадать по всем измерениям, кроме измеренияaxis-го порядка. Иными словами, формы не могут различаться в измеренииd, гдеdне равно аргументуaxis. Например, если задан объектtf.distribute.DistributedValuesс компонентными тензорами форм(1, 2, 3)и(1, 3, 3)на двух репликах, для него можно вызватьgather(..., axis=1, ...), но нельзяgather(..., axis=0, ...)илиgather(..., axis=2, ...). Однако дляtf.distribute.TPUStrategy.gatherвсе тензоры должны иметь одинаковый ранг и одинаковую форму.
Примечание: Для объектаtf.distribute.DistributedValuesvalueего компонентные тензоры должны иметь ненулевой ранг. В противном случае перед их сбором рассмотрите возможность использованияtf.expand_dims.
strategy = tf.distribute.MirroredStrategy(["GPU:0", "GPU:1"])
# A DistributedValues with component tensor of shape (2, 1) on each replica
distributed_values = strategy.experimental_distribute_values_from_function(lambda _: tf.identity(tf.constant([[1], [2]])))
@tf.function
def run():
return strategy.gather(distributed_values, axis=0)
run()
<tf.Tensor: shape=(4, 1), dtype=int32, numpy=
array([[1],
[2],
[1],
[2]], dtype=int32)>Другие варианты сочетаний показаны в следующем примере:
strategy = tf.distribute.MirroredStrategy(["GPU:0", "GPU:1", "GPU:2", "GPU:3"])
single_tensor = tf.reshape(tf.range(6), shape=(1,2,3))
distributed_values = strategy.experimental_distribute_values_from_function(lambda _: tf.identity(single_tensor))
@tf.function
def run(axis):
return strategy.gather(distributed_values, axis=axis)
axis=0
run(axis)
<tf.Tensor: shape=(4, 2, 3), dtype=int32, numpy=
array([[[0, 1, 2],
[3, 4, 5]],
[[0, 1, 2],
[3, 4, 5]],
[[0, 1, 2],
[3, 4, 5]],
[[0, 1, 2],
[3, 4, 5]]], dtype=int32)>
axis=1
run(axis)
<tf.Tensor: shape=(1, 8, 3), dtype=int32, numpy=
array([[[0, 1, 2],
[3, 4, 5],
[0, 1, 2],
[3, 4, 5],
[0, 1, 2],
[3, 4, 5],
[0, 1, 2],
[3, 4, 5]]], dtype=int32)>
axis=2
run(axis)
<tf.Tensor: shape=(1, 2, 12), dtype=int32, numpy=
array([[[0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2],
[3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5]]], dtype=int32)>| Аргументы | |
|---|---|
value | Экземпляр tf.distribute.DistributedValues, например возвращаемый Strategy.run, который нужно объединить в один тензор. При использовании с tf.distribute.OneDeviceStrategy или стратегией по умолчанию это также может быть обычный тензор. Тензоры, составляющие DistributedValues, могут быть только плотными тензорами с ненулевым рангом, а не объектами tf.IndexedSlices. |
axis | Тензор int32 ранга 0. Измерение, вдоль которого выполняется сбор. Должно находиться в диапазоне [0, rank(value)). |
| Возвращает | |
|---|---|
Объект Tensor, представляющий собой конкатенацию value по репликам вдоль измерения axis. |
reduce
reduce(
reduce_op, value, axis
)
Сводит value по репликам и возвращает результат на текущем устройстве.
strategy = tf.distribute.MirroredStrategy(["GPU:0", "GPU:1"])
def step_fn():
i = tf.distribute.get_replica_context().replica_id_in_sync_group
return tf.identity(i)
per_replica_result = strategy.run(step_fn)
total = strategy.reduce("SUM", per_replica_result, axis=None)
total
<tf.Tensor: shape=(), dtype=int32, numpy=1>Чтобы понять, как это будет выглядеть для нескольких реплик, рассмотрим тот же пример с MirroredStrategy и двумя GPU:
strategy = tf.distribute.MirroredStrategy(devices=["GPU:0", "GPU:1"])
def step_fn():
i = tf.distribute.get_replica_context().replica_id_in_sync_group
return tf.identity(i)
per_replica_result = strategy.run(step_fn)
# Check devices on which per replica result is:
strategy.experimental_local_results(per_replica_result)[0].device
# /job:localhost/replica:0/task:0/device:GPU:0
strategy.experimental_local_results(per_replica_result)[1].device
# /job:localhost/replica:0/task:0/device:GPU:1
total = strategy.reduce("SUM", per_replica_result, axis=None)
# Check device on which reduced result is:
total.device
# /job:localhost/replica:0/task:0/device:CPU:0
Этот API обычно используется для агрегирования результатов, возвращаемых разными репликами, например для вывода отчетов. Например, потери, вычисленные на разных репликах, можно усреднить с помощью этого API перед выводом.
Примечание: Результат копируется на «текущее» устройство, которым обычно является CPU рабочего узла, на котором выполняется программа. ДляTPUStrategyэто первый хост TPU. Для многоклиентскойMultiWorkerMirroredStrategyэто CPU каждого рабочего узла.
В tf.distribute есть несколько API для сведения значений по репликам:
-
tf.distribute.ReplicaContext.all_reduce: этот метод отличается отStrategy.reduceтем, что предназначен для контекста реплики и не копирует результаты на хост-устройство. Обычноall_reduceследует использовать для сведения данных внутри шага обучения, например градиентов. -
tf.distribute.StrategyExtended.reduce_toиtf.distribute.StrategyExtended.batch_reduce_to: эти API являются более продвинутыми версиямиStrategy.reduce, поскольку позволяют настраивать место назначения результата. Они также вызываются в контексте между репликами.
Каким должно быть значение axis?
Полученное от run значение на одну реплику, например потери на один пример, означает, что пакет будет разделен между всеми репликами. Эта функция позволяет агрегировать данные по репликам и, при необходимости, также по элементам пакета, соответствующим образом задав параметр axis.
Например, если глобальный размер пакета равен 8, а реплик две, значения для примеров [0, 1, 2, 3] будут на реплике 0, а [4, 5, 6, 7] — на реплике 1. При axis=None операция reduce будет выполнять сведение только по репликам и возвращать [0+4, 1+5, 2+6, 3+7]. Это полезно, если каждая реплика вычисляет скаляр или какое-либо другое значение без измерения «пакета» (например, градиент или потери).
strategy.reduce("sum", per_replica_result, axis=None)
Иногда требуется выполнить сведение и по глобальному пакету, и по всем репликам. Для этого задайте измерение пакета в качестве axis, обычно axis=0. В этом случае будет возвращен скаляр 0+1+2+3+4+5+6+7.
strategy.reduce("sum", per_replica_result, axis=0)
Если последний пакет неполный, необходимо указать axis, чтобы результирующая форма была одинаковой для всех реплик. Например, если последний пакет содержит 6 элементов и разделен на [0, 1, 2, 3] и [4, 5], формы не совпадут, если не указать axis=0. Если указать tf.distribute.ReduceOp.MEAN, при использовании axis=0 будет применен правильный знаменатель, равный 6. Сравните это с вычислением reduce_mean для получения скалярного значения на каждой реплике и использованием этой функции для усреднения этих средних значений: в таком случае некоторые значения будут иметь вес 1/8, а другие — 1/4.
| Аргументы | |
|---|---|
reduce_op | Значение tf.distribute.ReduceOp, задающее способ объединения значений. Можно использовать строковое представление перечисления, например "SUM" или "MEAN". |
value | Экземпляр tf.distribute.DistributedValues, например возвращаемый Strategy.run, который нужно объединить в один тензор. При использовании с OneDeviceStrategy или стратегией по умолчанию это также может быть обычный тензор. |
axis | Задает измерение, вдоль которого выполняется сведение внутри тензора каждой реплики. Обычно следует указывать измерение пакета или None, чтобы выполнять сведение только по репликам (например, если у тензора нет измерения пакета). |
| Возвращает | |
|---|---|
Объект Tensor. |
run
run(
fn, args=(), kwargs=None, options=None
)
Вызывает fn на каждой реплике с заданными аргументами.
Этот метод является основным способом распределения вычислений с помощью объекта tf.distribute. Он вызывает fn на каждой реплике. Если args или kwargs содержат tf.distribute.DistributedValues, например созданные с помощью tf.distribute.DistributedDataset из tf.distribute.Strategy.experimental_distribute_dataset или tf.distribute.Strategy.distribute_datasets_from_function, то при выполнении fn на определённой реплике будут использоваться компоненты tf.distribute.DistributedValues, соответствующие этой реплике.
fn вызывается в контексте реплики. fn может вызвать tf.distribute.get_replica_context(), чтобы получить доступ к таким элементам, как all_reduce. О понятии контекста реплики см. строку документации модуля tf.distribute.
Все аргументы в args или kwargs могут представлять собой вложенную структуру тензоров, например список тензоров. В этом случае args и kwargs будут переданы в fn, вызываемый на каждой реплике. Либо args или kwargs могут быть объектами tf.distribute.DistributedValues, содержащими тензоры или составные тензоры, то есть tf.compat.v1.TensorInfo.CompositeTensor. В этом случае при каждом вызове fn будет получен компонент объекта tf.distribute.DistributedValues, соответствующий данной реплике. Обратите внимание, что произвольные значения Python, не относящиеся к перечисленным выше типам, не поддерживаются.
Пример использования:
-
Входной тензор-константа.
strategy = tf.distribute.MirroredStrategy(["GPU:0", "GPU:1"]) tensor_input = tf.constant(3.0) @tf.function def replica_fn(input): return input*2.0 result = strategy.run(replica_fn, args=(tensor_input,)) result PerReplica:{ 0: <tf.Tensor: shape=(), dtype=float32, numpy=6.0>, 1: <tf.Tensor: shape=(), dtype=float32, numpy=6.0> } -
Входной объект DistributedValues.
strategy = tf.distribute.MirroredStrategy(["GPU:0", "GPU:1"]) @tf.function def run(): def value_fn(value_context): return value_context.num_replicas_in_sync distributed_values = ( strategy.experimental_distribute_values_from_function( value_fn)) def replica_fn2(input): return input*2 return strategy.run(replica_fn2, args=(distributed_values,)) result = run() result <tf.Tensor: shape=(), dtype=int32, numpy=4> -
Используйте
tf.distribute.ReplicaContextдля выполнения allreduce над значениями.strategy = tf.distribute.MirroredStrategy(["gpu:0", "gpu:1"]) @tf.function def run(): def value_fn(value_context): return tf.constant(value_context.replica_id_in_sync_group) distributed_values = ( strategy.experimental_distribute_values_from_function( value_fn)) def replica_fn(input): return tf.distribute.get_replica_context().all_reduce( "sum", input) return strategy.run(replica_fn, args=(distributed_values,)) result = run() result PerReplica:{ 0: <tf.Tensor: shape=(), dtype=int32, numpy=1>, 1: <tf.Tensor: shape=(), dtype=int32, numpy=1> }
| Аргументы | |
|---|---|
fn | Функция, выполняемая на каждой реплике. |
args | Необязательные позиционные аргументы для fn. Элементом может быть тензор, вложенная структура тензоров или объект tf.distribute.DistributedValues. |
kwargs | Необязательные именованные аргументы для fn. Элементом может быть тензор, вложенная структура тензоров или объект tf.distribute.DistributedValues. |
options | Необязательный экземпляр tf.distribute.RunOptions, задающий параметры выполнения fn. |
| Возвращает | |
|---|---|
Объединённое возвращаемое значение fn для всех реплик. Структура возвращаемого значения совпадает со структурой значения, возвращаемого fn. Каждый элемент структуры может быть объектом tf.distribute.DistributedValues, объектом Tensor или Tensor (например, при выполнении на одной реплике). |
scope
scope()
Менеджер контекста, который делает стратегию текущей и распределяет переменные.
Этот метод возвращает менеджер контекста и используется следующим образом:
strategy = tf.distribute.MirroredStrategy(["GPU:0", "GPU:1"])
# Variable created inside scope:
with strategy.scope():
mirrored_variable = tf.Variable(1.)
mirrored_variable
MirroredVariable:{
0: <tf.Variable 'Variable:0' shape=() dtype=float32, numpy=1.0>,
1: <tf.Variable 'Variable/replica_1:0' shape=() dtype=float32, numpy=1.0>
}
# Variable created outside scope:
regular_variable = tf.Variable(1.)
regular_variable
<tf.Variable 'Variable:0' shape=() dtype=float32, numpy=1.0>Что происходит при входе в Strategy.scope?
-
strategyустанавливается в глобальном контексте как «текущая» стратегия. В этой областиtf.distribute.get_strategy()будет возвращать эту стратегию. За пределами этой области метод возвращает стратегию по умолчанию, которая ничего не делает. - При входе в эту область также выполняется переход в «контекст между репликами». Объяснение контекстов между репликами и реплик см. в разделе
tf.distribute.StrategyExtended. - Создание переменных внутри
scopeперехватывается стратегией. Каждая стратегия определяет, как она влияет на создание переменных. Синхронные стратегии, такие какMirroredStrategy,TPUStrategyиMultiWorkerMiroredStrategy, создают переменные, реплицируемые на каждой реплике, тогда какParameterServerStrategyсоздаёт переменные на серверах параметров. Для этого используется пользовательскийtf.variable_creator_scope. - В некоторых стратегиях также может выполняться переход в область действия устройства по умолчанию: в
MultiWorkerMiroredStrategyдля каждого рабочего узла устанавливается область действия устройства по умолчанию «/CPU:0».
Примечание: Вход в область действия не приводит к автоматическому распределению вычислений, за исключением высокоуровневых обучающих фреймворков, таких как kerasmodel.fit. Если вы не используетеmodel.fit, необходимо явно распределить вычисления с помощью APIstrategy.run. Пример приведён в руководстве по созданию пользовательского цикла обучения.
Что должно находиться внутри области действия, а что — за её пределами?
Существуют требования к тому, какие действия должны выполняться внутри области действия. Однако в тех случаях, когда нам известно, какая стратегия используется, мы часто сами входим в эту область, поэтому пользователю не нужно делать это явно (то есть вызывать эти методы можно как внутри области действия, так и за её пределами).
- Любые функции, создающие переменные, которые должны быть распределёнными, необходимо вызывать внутри
strategy.scope. Этого можно добиться, вызвав функцию создания переменной непосредственно в контексте области действия или воспользовавшись другим API, напримерstrategy.runилиkeras.Model.fit, который автоматически выполнит вход в эту область. Переменные, созданные за пределами области действия, распределяться не будут, что может повлиять на производительность. К распространённым объектам TensorFlow, создающим переменные, относятся модели, оптимизаторы и метрики. Такие объекты всегда следует инициализировать в области действия, а любые функции, которые могут создавать переменные отложенно (например,Model.call(), трассировкаtf.functionи т. д.), также следует вызывать внутри области действия. Ещё одним источником создания переменных может быть восстановление контрольной точки, если переменные создаются отложенно. Обратите внимание, что любая переменная, созданная внутри стратегии, сохраняет информацию о стратегии. Поэтому чтение и запись таких переменных за пределамиstrategy.scopeтакже могут выполняться без проблем, и пользователю не нужно входить в область действия. - Некоторые API стратегий (например,
strategy.runиstrategy.reduce), для которых требуется находиться в области действия стратегии, автоматически входят в неё. Поэтому при использовании этих API вам не нужно входить в область действия самостоятельно. - Когда
tf.keras.Modelсоздаётся внутриstrategy.scope, объект Model сохраняет информацию об области действия. При вызове высокоуровневых методов обучающего фреймворка, таких какmodel.compile,model.fitи т. д., сохранённая область действия будет активирована автоматически, а связанная с ней стратегия будет использоваться для распределения обучения и других операций. Подробный пример приведён в руководстве по распределённому обучению с помощью Keras. ПРЕДУПРЕЖДЕНИЕ: простой вызовmodel(..)не приводит к автоматическому входу в сохранённую область действия — такое поведение поддерживается только высокоуровневыми API обучающего фреймворка:model.compile,model.fit,model.evaluate,model.predictиmodel.saveможно вызывать как внутри области действия, так и за её пределами. - Следующие действия можно выполнять как внутри области действия, так и за её пределами:
- Создание входных наборов данных
- Определение
tf.function, представляющих шаг обучения - API сохранения, например
tf.saved_model.save. При загрузке создаются переменные, поэтому загрузку следует выполнять внутри области действия, если вы хотите обучать модель распределённым способом. - Сохранение контрольных точек. Как указано выше,
checkpoint.restoreиногда необходимо вызывать внутри области действия, если при этом создаются переменные.
| Возвращает | |
|---|---|
| Менеджер контекста. |
© 2022 The TensorFlow Authors. All rights reserved.
Licensed under the Creative Commons Attribution License 4.0.
Code samples licensed under the Apache 2.0 License.
https://www.tensorflow.org/api_docs/python/tf/distribute/Strategy