Случайность, как определено самым популярным языком машинного обучения

Случайность, как определено самым популярным языком машинного обучения

25 июля 2025 г.

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

  • Настраивать
  • TF.Random.Generator Class
  • Создание независимых случайных потоков
  • Взаимодействие с tf.function
  • Взаимодействие с стратегиями распространения
  • Сохранение генераторов
  • Без гражданства RNGS
  • Алгоритмы
  • Общий
  • XLA устройства

TensorFlow предоставляет набор генераторов псевдолупиточных чисел (RNG), вtf.randomмодуль. В этом документе описывается, как вы можете управлять генераторами случайных чисел, и как эти генераторы взаимодействуют с другими подсистемами Tensorflow.

Примечание:Случайные числа не гарантируются согласованными в рамках версий Tensorflow. Видеть:Совместимость версии

Tensorflow предоставляет два подхода для управления процессом генерации случайных чисел:

  1. Через явное использованиеtf.random.Generatorобъекты Каждый такой объект поддерживает состояние (вtf.Variable) это будет изменено после каждой генерации номеров.
  2. Через чисто функциональные случайные функции без состояния, такие какtf.random.stateless_uniformПолем Вызов эти функции с помощью одних и тех же аргументов (которые включают семена) и на одном и том же устройстве всегда даст одни и те же результаты.

Предупреждение:Старые RNG от TF 1.x, такие какtf.random.uniformиtf.random.normalеще не устарели, но сильно обескуражены.

Настраивать

import tensorflow as tf

# Creates some virtual devices (cpu:0, cpu:1, etc.) for using distribution strategy
physical_devices = tf.config.list_physical_devices("CPU")
tf.config.experimental.set_virtual_device_configuration(
    physical_devices[0], [
        tf.config.experimental.VirtualDeviceConfiguration(),
        tf.config.experimental.VirtualDeviceConfiguration(),
        tf.config.experimental.VirtualDeviceConfiguration()
    ])

2024-08-15 01:43:41.157432: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:485] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered
2024-08-15 01:43:41.178819: E external/local_xla/xla/stream_executor/cuda/cuda_dnn.cc:8454] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered
2024-08-15 01:43:41.185039: E external/local_xla/xla/stream_executor/cuda/cuda_blas.cc:1452] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1723686223.758551   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686223.762466   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686223.765643   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686223.769228   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686223.780976   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686223.784469   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686223.787369   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686223.790779   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686223.794250   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686223.797851   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686223.800627   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686223.804177   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355

Аtf.random.Generatorсорт

Аtf.random.GeneratorКласс используется в тех случаях, когда вы хотите, чтобы каждый вызов RNG давал разные результаты. Он поддерживает внутреннее состояние (управляетсяtf.Variableобъект), который будет обновляться каждый раз, когда генерируются случайные числа. Потому что государством управляетсяtf.Variable, он пользуется всеми объектами, предоставленнымиtf.Variableтакие как легкая контрольная точка, автоматическая зависимость управления и безопасность потоков.

Вы можете получитьtf.random.Generatorвручную создавая объект класса или вызовtf.random.get_global_generator()Чтобы получить глобальный генератор по умолчанию:

g1 = tf.random.Generator.from_seed(1)
print(g1.normal(shape=[2, 3]))
g2 = tf.random.get_global_generator()
print(g2.normal(shape=[2, 3]))

I0000 00:00:1723686225.040869   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.043016   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.045041   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.047115   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.049184   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.051188   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.053162   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.055160   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.057105   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.059113   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.061029   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
tf.Tensor(
[[ 0.43842277 -0.53439844 -0.07710262]
 [ 1.5658045  -0.1012345  -0.2744976 ]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[ 1.3061213   0.6299361   0.52625704]
 [ 1.3733886   0.29277426 -0.7945693 ]], shape=(2, 3), dtype=float32)
I0000 00:00:1723686225.063243   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.101904   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.103956   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.105919   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.107957   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.109922   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.111909   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.113815   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.115823   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.117783   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.120279   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.122678   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355
I0000 00:00:1723686225.125117   55391 cuda_executor.cc:1015] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero. See more at https://github.com/torvalds/linux/blob/v6.0/Documentation/ABI/testing/sysfs-bus-pci#L344-L355

Есть несколько способов создания объекта генератора. Самый простой естьGenerator.from_seed, как показано выше, это создает генератор из семян. Семена-это нетрицательное целое число.from_seedТакже принимает дополнительный аргументalgкоторый является алгоритмом RNG, который будет использоваться этим генератором:

g1 = tf.random.Generator.from_seed(1, alg='philox')
print(g1.normal(shape=[2, 3]))

tf.Tensor(
[[ 0.43842277 -0.53439844 -0.07710262]
 [ 1.5658045  -0.1012345  -0.2744976 ]], shape=(2, 3), dtype=float32)

УвидетьАлгоритмыРаздел ниже для получения дополнительной информации об этом.

Другой способ создать генератор - сGenerator.from_non_deterministic_stateПолем Генератор, созданный таким образом, начнется с неэтерминированного состояния, в зависимости от, например, времени и ОС.

g = tf.random.Generator.from_non_deterministic_state()
print(g.normal(shape=[2, 3]))

tf.Tensor(
[[ 0.9104948  -0.23143363 -0.09841432]
 [-0.91448975  0.1579936   1.3923475 ]], shape=(2, 3), dtype=float32)

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

При использованииtf.random.get_global_generatorЧтобы получить глобальный генератор, вы должны быть осторожны с размещением устройств. Глобальный генератор создается (из нетериминационного состояния) в первый разtf.random.get_global_generatorвызывается и помещается на устройство по умолчанию при этом вызове. Так, например, если первый сайт, который вы звонитеtf.random.get_global_generatorнаходится в пределахtf.device("gpu")Scope, глобальный генератор будет размещен на графическом процессоре, а использование глобального генератора позже из процессора будет понести копию GPU-CPU.

Есть также функцияtf.random.set_global_generatorДля замены глобального генератора другим объектом генератора. Эта функция должна использоваться с осторожностью, потому что старый глобальный генератор мог быть захваченtf.function(как слабая ссылка), и замена его приведетtf.functionПолем Лучший способ сбросить глобальный генератор - использовать одну из функций «сброса», таких какGenerator.reset_from_seed, который не создаст новые объекты генератора.

g = tf.random.Generator.from_seed(1)
print(g.normal([]))
print(g.normal([]))
g.reset_from_seed(1)
print(g.normal([]))

tf.Tensor(0.43842277, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)
tf.Tensor(0.43842277, shape=(), dtype=float32)

Создание независимых случайных потоков

Во многих приложениях нужно несколько независимых потоков случайных номеров, независимых в том смысле, что они не совпадают и не будут иметь каких-либо статистически обнаруживаемых корреляций. Это достигается с помощью использованияGenerator.splitСоздать несколько генераторов, которые гарантируют независимо друг от друга (то есть генерирование независимых потоков).

g = tf.random.Generator.from_seed(1)
print(g.normal([]))
new_gs = g.split(3)
for new_g in new_gs:
  print(new_g.normal([]))
print(g.normal([]))

tf.Tensor(0.43842277, shape=(), dtype=float32)
tf.Tensor(2.536413, shape=(), dtype=float32)
tf.Tensor(0.33186463, shape=(), dtype=float32)
tf.Tensor(-0.07144657, shape=(), dtype=float32)
tf.Tensor(-0.79253083, shape=(), dtype=float32)

splitизменит состояние генератора, на котором он называется (gв приведенном выше примере), аналогично методу RNG, таким какnormalПолем В дополнение к тому, чтобы быть независимыми друг от друга, новые генераторы (new_gs) также гарантированно не зависит от старого (g)

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

with tf.device("cpu"):  # change "cpu" to the device you want
  g = tf.random.get_global_generator().split(1)[0]  
  print(g.normal([]))  # use of g won't cause cross-device copy, unlike the global generator

tf.Tensor(-0.66787744, shape=(), dtype=float32)

Примечание:Теоретически, вы можете использовать конструкторы, такие какfrom_seedвместоsplitЗдесь, чтобы получить новый генератор, но при этом вы теряете гарантию, что новый генератор не зависит от глобального генератора. Вы также будете использовать риск того, что вы можете случайно создать два генератора с тем же семенем или с семенами, которые приводят к перекрывающимся потокам случайных номеров.

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

Взаимодействие сtf.function

tf.random.Generatorподчиняется тем же правилам, что иtf.VariableПри использовании сtf.functionПолем Это включает в себя три аспекта.

Создание генераторов снаружиtf.function

tf.functionможет использовать генератор, созданный вне его.

g = tf.random.Generator.from_seed(1)
@tf.function
def foo():
  return g.normal([])
print(foo())

tf.Tensor(0.43842277, shape=(), dtype=float32)

Пользователь должен убедиться, что объект генератора все еще жив (а не составлен из мусора) при вызванной функции.

Создание генераторов внутриtf.function

Создание генераторов внутриtf.functionможет произойти только во время первого запуска функции.

g = None
@tf.function
def foo():
  global g
  if g is None:
    g = tf.random.Generator.from_seed(1)
  return g.normal([])
print(foo())
print(foo())

tf.Tensor(0.43842277, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)

Прохождение генераторов в качестве аргументовtf.function

При использовании в качестве аргументаtf.functionразные объекты генератора вызовут обращениеtf.functionПолем

num_traces = 0
@tf.function
def foo(g):
  global num_traces
  num_traces += 1
  return g.normal([])
foo(tf.random.Generator.from_seed(1))
foo(tf.random.Generator.from_seed(2))
print(num_traces)

2

Обратите внимание, что это повторное поведение соответствуетtf.Variable:

num_traces = 0
@tf.function
def foo(v):
  global num_traces
  num_traces += 1
  return v.read_value()
foo(tf.Variable(1))
foo(tf.Variable(2))
print(num_traces)

1

Взаимодействие с стратегиями распространения

Есть два способа, которымиGeneratorвзаимодействует со стратегиями распространения.

Создание генераторов вне стратегий распространения

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

g = tf.random.Generator.from_seed(1)
strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  def f():
    print(g.normal([]))
  results = strat.run(f)

INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1')
WARNING:tensorflow:Using MirroredStrategy eagerly has significant overhead currently. We will be working on improving this in the future, but for now please wrap `call_for_each_replica` or `experimental_run` or `run` inside a tf.function to get the best performance.
tf.Tensor(0.43842274, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)

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

Создание генераторов внутри стратегий распространения

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

strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  g = tf.random.Generator.from_seed(1)
  print(strat.run(lambda: g.normal([])))
  print(strat.run(lambda: g.normal([])))

INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1')
WARNING:tensorflow:Using MirroredStrategy eagerly has significant overhead currently. We will be working on improving this in the future, but for now please wrap `call_for_each_replica` or `experimental_run` or `run` inside a tf.function to get the best performance.
PerReplica:{
  0: tf.Tensor(-0.87930447, shape=(), dtype=float32),
  1: tf.Tensor(0.020661574, shape=(), dtype=float32)
}
WARNING:tensorflow:Using MirroredStrategy eagerly has significant overhead currently. We will be working on improving this in the future, but for now please wrap `call_for_each_replica` or `experimental_run` or `run` inside a tf.function to get the best performance.
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32)
}

Примечание:В настоящее времяtf.random.GeneratorНе предоставляет возможность позволить разным репликам получить идентичные (вместо разных) потоков (что технически не сложно). Если у вас есть вариант использования для этой функции, сообщите о разработчиках TensorFlow.

Если генератор посеян (например, созданGenerator.from_seed), случайные числа определяются семенами, хотя разные реплики получают разные и некоррелированные числа. Можно придумать случайное число, сгенерированное в реплике как хэш идентификатора реплики и «первичное» случайное число, которое является общим для всех реплик. Следовательно, вся система все еще детерминированная.

tf.random.Generatorтакже может быть создан внутриStrategy.run:

strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  def f():
    g = tf.random.Generator.from_seed(1)
    a = g.normal([])
    b = g.normal([])
    return tf.stack([a, b])
  print(strat.run(f))
  print(strat.run(f))

INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1')
WARNING:tensorflow:Using MirroredStrategy eagerly has significant overhead currently. We will be working on improving this in the future, but for now please wrap `call_for_each_replica` or `experimental_run` or `run` inside a tf.function to get the best performance.
PerReplica:{
  0: tf.Tensor([-0.87930447 -1.5822568 ], shape=(2,), dtype=float32),
  1: tf.Tensor([0.02066157 0.77539235], shape=(2,), dtype=float32)
}
WARNING:tensorflow:Using MirroredStrategy eagerly has significant overhead currently. We will be working on improving this in the future, but for now please wrap `call_for_each_replica` or `experimental_run` or `run` inside a tf.function to get the best performance.
PerReplica:{
  0: tf.Tensor([-0.87930447 -1.5822568 ], shape=(2,), dtype=float32),
  1: tf.Tensor([0.02066157 0.77539235], shape=(2,), dtype=float32)
}

Мы больше не рекомендуем пройтиtf.random.Generatorкак аргументыStrategy.run, потому чтоStrategy.runКак правило, ожидает, что аргументы будут тензорами, а не генераторами.

Сохранение генераторов

Обычно для сохранения или сериализации вы можете справитьсяtf.random.Generatorтак же, как вы бы справились сtf.Variableилиtf.Module(или его подклассы). В TF есть два механизма для сериализации:Контрольная точкаиСохраняйте модельПолем

Контрольная точка

Генераторы могут быть свободно сохранены и восстановлены с помощьюtf.train.CheckpointПолем Случайный поток из точки восстановления будет таким же, как и из точки сохранения.

filename = "./checkpoint"
g = tf.random.Generator.from_seed(1)
cp = tf.train.Checkpoint(generator=g)
print(g.normal([]))

tf.Tensor(0.43842277, shape=(), dtype=float32)

cp.write(filename)
print("RNG stream from saving point:")
print(g.normal([]))
print(g.normal([]))

RNG stream from saving point:
tf.Tensor(1.6272374, shape=(), dtype=float32)
tf.Tensor(1.6307176, shape=(), dtype=float32)

cp.restore(filename)
print("RNG stream from restoring point:")
print(g.normal([]))
print(g.normal([]))

RNG stream from restoring point:
tf.Tensor(1.6272374, shape=(), dtype=float32)
tf.Tensor(1.6307176, shape=(), dtype=float32)

Вы также можете сохранить и восстановить в рамках стратегии распространения:

filename = "./checkpoint"
strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  g = tf.random.Generator.from_seed(1)
  cp = tf.train.Checkpoint(my_generator=g)
  print(strat.run(lambda: g.normal([])))

INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1')
INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1')
PerReplica:{
  0: tf.Tensor(-0.87930447, shape=(), dtype=float32),
  1: tf.Tensor(0.020661574, shape=(), dtype=float32)
}

with strat.scope():
  cp.write(filename)
  print("RNG stream from saving point:")
  print(strat.run(lambda: g.normal([])))
  print(strat.run(lambda: g.normal([])))

RNG stream from saving point:
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32)
}
PerReplica:{
  0: tf.Tensor(-0.5039703, shape=(), dtype=float32),
  1: tf.Tensor(0.1251838, shape=(), dtype=float32)
}

with strat.scope():
  cp.restore(filename)
  print("RNG stream from restoring point:")
  print(strat.run(lambda: g.normal([])))
  print(strat.run(lambda: g.normal([])))

RNG stream from restoring point:
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32)
}
PerReplica:{
  0: tf.Tensor(-0.5039703, shape=(), dtype=float32),
  1: tf.Tensor(0.1251838, shape=(), dtype=float32)
}

Вы должны убедиться, что реплики не расходится в их истории вызовов RNG (например, одна копия делает один вызов RNG, в то время как другой делает два вызова RNG) перед сохранением. В противном случае их внутренние государства RNG будут расходятся иtf.train.Checkpoint(Что сохраняет только состояние первой реплики) не будет должным образом восстановить все копии.

Вы также можете восстановить сохраненную контрольную точку в другую стратегию распределения с другим количеством реплик. Потому что аtf.random.GeneratorОбъект, созданный в стратегии, может использоваться только в одной и той же стратегии, чтобы восстановить другую стратегию, вы должны создать новыйtf.random.Generatorв целевой стратегии и новойtf.train.CheckpointДля этого, как показано в этом примере:

filename = "./checkpoint"
strat1 = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat1.scope():
  g1 = tf.random.Generator.from_seed(1)
  cp1 = tf.train.Checkpoint(my_generator=g1)
  print(strat1.run(lambda: g1.normal([])))

INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1')
INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1')
PerReplica:{
  0: tf.Tensor(-0.87930447, shape=(), dtype=float32),
  1: tf.Tensor(0.020661574, shape=(), dtype=float32)
}

with strat1.scope():
  cp1.write(filename)
  print("RNG stream from saving point:")
  print(strat1.run(lambda: g1.normal([])))
  print(strat1.run(lambda: g1.normal([])))

RNG stream from saving point:
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32)
}
PerReplica:{
  0: tf.Tensor(-0.5039703, shape=(), dtype=float32),
  1: tf.Tensor(0.1251838, shape=(), dtype=float32)
}

strat2 = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1", "cpu:2"])
with strat2.scope():
  g2 = tf.random.Generator.from_seed(1)
  cp2 = tf.train.Checkpoint(my_generator=g2)
  cp2.restore(filename)
  print("RNG stream from restoring point:")
  print(strat2.run(lambda: g2.normal([])))
  print(strat2.run(lambda: g2.normal([])))

INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1', '/job:localhost/replica:0/task:0/device:CPU:2')
INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1', '/job:localhost/replica:0/task:0/device:CPU:2')
RNG stream from restoring point:
PerReplica:{
  0: tf.Tensor(-1.5822568, shape=(), dtype=float32),
  1: tf.Tensor(0.77539235, shape=(), dtype=float32),
  2: tf.Tensor(0.6851049, shape=(), dtype=float32)
}
PerReplica:{
  0: tf.Tensor(-0.5039703, shape=(), dtype=float32),
  1: tf.Tensor(0.1251838, shape=(), dtype=float32),
  2: tf.Tensor(-0.58519536, shape=(), dtype=float32)
}

Хотяg1иcp1разные объекты изg2иcp2, они связаны через общий файл контрольной точкиfilenameи имя объектаmy_generatorПолем Перекрывающиеся реплики между стратегиями (например,cpu:0иcpu:1Выше) будут иметь свои потоки RNG должным образом восстановленные, как в предыдущих примерах. Эта гарантия не охватывает случай, когда генератор сохраняется в сфере стратегии и восстанавливается за пределами какой -либо стратегии или наоборот, потому что устройство вне стратегии, находящиеся вне стратегии, рассматривается как отличная от любой копии в стратегии.

Сохраняйте модель

tf.random.Generatorможно сохранить в спасении. Генератор может быть создан в рамках стратегии. Сохранение также может произойти в рамках стратегии.

filename = "./saved_model"

class MyModule(tf.Module):

  def __init__(self):
    super(MyModule, self).__init__()
    self.g = tf.random.Generator.from_seed(0)

  @tf.function
  def __call__(self):
    return self.g.normal([])

  @tf.function
  def state(self):
    return self.g.state

strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
with strat.scope():
  m = MyModule()
  print(strat.run(m))
  print("state:", m.state())

INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1')
INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:CPU:0', '/job:localhost/replica:0/task:0/device:CPU:1')
PerReplica:{
  0: tf.Tensor(-1.4154755, shape=(), dtype=float32),
  1: tf.Tensor(-0.11388441, shape=(), dtype=float32)
}
state: tf.Tensor([256   0   0], shape=(3,), dtype=int64)

with strat.scope():
  tf.saved_model.save(m, filename)
  print("RNG stream from saving point:")
  print(strat.run(m))
  print("state:", m.state())
  print(strat.run(m))
  print("state:", m.state())

INFO:tensorflow:Assets written to: ./saved_model/assets
INFO:tensorflow:Assets written to: ./saved_model/assets
RNG stream from saving point:
PerReplica:{
  0: tf.Tensor(-0.68758255, shape=(), dtype=float32),
  1: tf.Tensor(0.8084062, shape=(), dtype=float32)
}
state: tf.Tensor([512   0   0], shape=(3,), dtype=int64)
PerReplica:{
  0: tf.Tensor(-0.27342677, shape=(), dtype=float32),
  1: tf.Tensor(-0.53093255, shape=(), dtype=float32)
}
state: tf.Tensor([768   0   0], shape=(3,), dtype=int64)

imported = tf.saved_model.load(filename)
print("RNG stream from loading point:")
print("state:", imported.state())
print(imported())
print("state:", imported.state())
print(imported())
print("state:", imported.state())

RNG stream from loading point:
state: tf.Tensor([256   0   0], shape=(3,), dtype=int64)
tf.Tensor(-1.0359411, shape=(), dtype=float32)
state: tf.Tensor([512   0   0], shape=(3,), dtype=int64)
tf.Tensor(-0.06425078, shape=(), dtype=float32)
state: tf.Tensor([768   0   0], shape=(3,), dtype=int64)

Загрузка сохраненной модели, содержащейtf.random.GeneratorВ стратегию распределения не рекомендуется, потому что все реплики будут генерировать один и тот же случайный поток (это потому, что идентификатор реплики заморожен на графике SaveedModel).

Загрузка распределенногоtf.random.Generator(Генератор, созданный в рамках стратегии распределения) в не стратегическую среду, как приведенный выше пример, также имеет предостережение. Состояние RNG будет должным образом восстановлено, но генерируемые случайные числа будут отличаться от исходного генератора в своей стратегии (опять же, потому что устройство вне стратегий, находящихся в стороне, рассматривается как отличная от любой реплики в стратегии).

Без гражданства RNGS

Использование RNG без состояния просто. Поскольку они просто чистые функции, не существует состояния или побочного эффекта.

print(tf.random.stateless_normal(shape=[2, 3], seed=[1, 2]))
print(tf.random.stateless_normal(shape=[2, 3], seed=[1, 2]))

tf.Tensor(
[[ 0.5441101   0.20738031  0.07356433]
 [ 0.04643455 -1.30159    -0.95385665]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[ 0.5441101   0.20738031  0.07356433]
 [ 0.04643455 -1.30159    -0.95385665]], shape=(2, 3), dtype=float32)

Каждое RNG без гражданства требуетseedаргумент, который должен быть целочисленным тензором формы[2]Полем Результаты ОП полностью определены этим семенем.

Алгоритм RNG, используемый RNG без состояния, зависит от устройства, что означает, что один и тот же OP, работающий на другом устройстве, может создавать разные выходы.

Алгоритмы

Общий

Обаtf.random.Generatorкласс иstatelessфункции поддерживают алгоритм Philox (написано как"philox"илиtf.random.Algorithm.PHILOX) на всех устройствах.

Различные устройства будут генерировать одинаковые целочисленные числа, при использовании одного и того же алгоритма и начнутся с того же состояния. Они также будут генерировать «практически одинаковые» числа точек, хотя могут возникнуть небольшие числовые расхождения, вызванные различными способами, которыми устройства выполняют вычисление с плавающей точкой (например, порядок сокращения).

XLA устройства

На устройствах, управляемых xLA (например, TPU, а также процессором/графическим процессором при включении XLA) Алгоритм ThreeFry (написано как"threefry"илиtf.random.Algorithm.THREEFRY) также поддерживается. Этот алгоритм быстрый на TPU, но медленно на процессоре/графическом процессоре по сравнению с Philox.

Смотрите бумагу'Параллельные случайные числа: просто 1, 2, 3'Для получения более подробной информации об этих алгоритмах.

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


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