Tạo số ngẫu nhiên

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ ghi chép

TensorFlow cung cấp một tập hợp các trình tạo số giả ngẫu nhiên (RNG), trong mô-đun tf.random . Tài liệu này mô tả cách bạn có thể điều khiển các bộ tạo số ngẫu nhiên và cách các bộ tạo này tương tác với các hệ thống con tensorflow khác.

TensorFlow cung cấp hai cách tiếp cận để kiểm soát quá trình tạo số ngẫu nhiên:

  1. Thông qua việc sử dụng rõ ràng các đối tượng tf.random.Generator . Mỗi đối tượng như vậy duy trì một trạng thái (trong tf.Variable ) sẽ được thay đổi sau mỗi lần tạo số.

  2. Thông qua các chức năng ngẫu nhiên không trạng thái thuần túy như tf.random.stateless_uniform . Việc gọi các hàm này với các đối số giống nhau (bao gồm cả hạt giống) và trên cùng một vùng nhớ sẽ luôn tạo ra các kết quả giống nhau.

Thành lập

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()
    ])

Lớp tf.random.Generator

Lớp tf.random.Generator được sử dụng trong trường hợp bạn muốn mỗi lệnh gọi RNG tạo ra các kết quả khác nhau. Nó duy trì một trạng thái bên trong (được quản lý bởi một đối tượng tf.Variable ) sẽ được cập nhật mỗi khi các số ngẫu nhiên được tạo ra. Bởi vì trạng thái được quản lý bởi tf.Variable , nó được hưởng tất cả các tiện ích do tf.Variable cung cấp như kiểm tra dễ dàng, kiểm soát tự động phụ thuộc và an toàn luồng.

Bạn có thể lấy tf.random.Generator bằng cách tạo thủ công một đối tượng của lớp hoặc gọi tf.random.get_global_generator() để lấy trình tạo chung mặc định:

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]))
tf.Tensor(
[[ 0.43842277 -0.53439844 -0.07710262]
 [ 1.5658046  -0.1012345  -0.2744976 ]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[-0.5496427   0.24263908 -1.1436267 ]
 [ 1.861458   -0.6756685  -0.9900103 ]], shape=(2, 3), dtype=float32)

Có nhiều cách để tạo một đối tượng máy phát điện. Đơn giản nhất là Generator.from_seed , như được hiển thị ở trên, tạo một trình tạo từ một hạt giống. Hạt giống là bất kỳ số nguyên không âm nào. from_seed cũng nhận một đối số tùy chọn là thuật toán alg sẽ được sử dụng bởi trình tạo này:

g1 = tf.random.Generator.from_seed(1, alg='philox')
print(g1.normal(shape=[2, 3]))
tf.Tensor(
[[ 0.43842277 -0.53439844 -0.07710262]
 [ 1.5658046  -0.1012345  -0.2744976 ]], shape=(2, 3), dtype=float32)

Xem phần Thuật toán bên dưới để biết thêm thông tin về nó.

Một cách khác để tạo trình tạo là sử dụng Generator.from_non_deterministic_state . Một trình tạo được tạo theo cách này sẽ bắt đầu từ trạng thái không xác định, tùy thuộc vào thời gian và hệ điều hành.

g = tf.random.Generator.from_non_deterministic_state()
print(g.normal(shape=[2, 3]))
tf.Tensor(
[[-0.9078738   0.11009752  1.037219  ]
 [ 0.661036    0.4169741   1.4539026 ]], shape=(2, 3), dtype=float32)

Vẫn còn những cách khác để tạo trình tạo, chẳng hạn như từ các trạng thái rõ ràng, không được đề cập trong hướng dẫn này.

Khi sử dụng tf.random.get_global_generator để tải trình tạo toàn cục, bạn cần phải cẩn thận về vị trí thiết bị. Trình tạo toàn cục được tạo (từ trạng thái không xác định) tại lần đầu tiên tf.random.get_global_generator được gọi và được đặt trên thiết bị mặc định tại cuộc gọi đó. Vì vậy, ví dụ: nếu trang web đầu tiên bạn gọi tf.random.get_global_generator nằm trong phạm vi tf.device("gpu") , trình tạo toàn cục sẽ được đặt trên GPU và sử dụng trình tạo toàn cục sau này từ CPU sẽ phải chịu một bản sao từ GPU sang CPU.

Ngoài ra còn có một hàm tf.random.set_global_generator để thay thế trình tạo toàn cục bằng một đối tượng trình tạo khác. Tuy nhiên, bạn nên sử dụng chức năng này một cách thận trọng vì trình tạo toàn cục cũ có thể đã bị tf.function nắm bắt (như một tham chiếu yếu) và việc thay thế chức năng này sẽ khiến chức năng này bị thu thập rác, phá vỡ chức tf.function . Cách tốt hơn để đặt lại trình tạo chung là sử dụng một trong các hàm "đặt lại" chẳng hạn như Generator.reset_from_seed , hàm này sẽ không tạo các đối tượng trình tạo mới.

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)

Tạo luồng số ngẫu nhiên độc lập

Trong nhiều ứng dụng, người ta cần nhiều luồng số ngẫu nhiên độc lập, độc lập theo nghĩa là chúng sẽ không trùng lặp và sẽ không có bất kỳ mối tương quan nào có thể phát hiện được về mặt thống kê. Điều này đạt được bằng cách sử dụng Generator.split để tạo nhiều bộ tạo được đảm bảo độc lập với nhau (tức là tạo ra các luồng độc lập).

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 sẽ thay đổi trạng thái của bộ tạo mà nó được gọi ( g trong ví dụ trên), tương tự như một phương pháp RNG chẳng hạn như normal . Ngoài việc độc lập với nhau, các máy phát mới ( new_gs ) cũng được đảm bảo độc lập với máy cũ ( g ).

Tạo trình tạo mới cũng hữu ích khi bạn muốn đảm bảo rằng trình tạo bạn sử dụng nằm trên cùng một thiết bị với các máy tính khác, để tránh chi phí sao chép trên nhiều thiết bị. Ví dụ:

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.4142675, shape=(), dtype=float32)

Bạn có thể thực hiện chia tách một cách đệ quy, gọi split trên các máy phát điện được chia nhỏ. Không có giới hạn (chặn tràn số nguyên) về độ sâu của đệ quy.

Tương tác với tf.function

tf.random.Generator tuân theo các quy tắc tương tự như tf.Variable khi được sử dụng với tf.function . function. Điều này bao gồm ba khía cạnh.

Tạo máy phát điện ngoài tf.function

tf.function có thể sử dụng một bộ tạo được tạo bên ngoài nó.

g = tf.random.Generator.from_seed(1)
@tf.function
def foo():
  return g.normal([])
print(foo())
tf.Tensor(0.43842277, shape=(), dtype=float32)

Người dùng cần đảm bảo rằng đối tượng máy phát điện vẫn còn sống (không bị thu gom rác) khi hàm được gọi.

Tạo máy phát điện bên trong tf.function

Việc tạo máy phát điện bên trong chức năng tf.function chỉ có thể xảy ra trong lần chạy chức năng đầu tiên.

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)

Truyền các trình tạo làm đối số cho tf.function

Khi được sử dụng làm đối số cho một tf.function ., các đối tượng bộ tạo khác nhau sẽ gây ra việc kiểm tra lại 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

Lưu ý rằng hành vi gửi lại này phù hợp với 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)
2

Tương tác với các chiến lược phân phối

Có hai cách mà Generator tương tác với các chiến lược phân phối.

Tạo máy phát điện bên ngoài chiến lược phân phối

Nếu một trình tạo được tạo bên ngoài phạm vi chiến lược, tất cả quyền truy cập của các bản sao vào trình tạo sẽ được tuần tự hóa và do đó các bản sao sẽ nhận được các số ngẫu nhiên khác nhau.

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)
WARNING:tensorflow:There are non-GPU devices in `tf.distribute.Strategy`, not using nccl allreduce.
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)

Lưu ý rằng cách sử dụng này có thể có vấn đề về hiệu suất vì thiết bị của máy phát khác với các bản sao.

Tạo máy phát điện bên trong chiến lược phân phối

Nếu một trình tạo được tạo bên trong phạm vi chiến lược, mỗi bản sao sẽ nhận được một dòng số ngẫu nhiên khác nhau và độc lập.

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([])))
WARNING:tensorflow:There are non-GPU devices in `tf.distribute.Strategy`, not using nccl allreduce.
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)
}

Nếu trình tạo được tạo hạt giống (ví dụ: được tạo bởi Generator.from_seed ), các số ngẫu nhiên được xác định bởi hạt giống, mặc dù các bản sao khác nhau nhận được các số khác nhau và không tương quan. Người ta có thể coi một số ngẫu nhiên được tạo ra trên một bản sao là một mã băm của ID bản sao và một số ngẫu nhiên "chính" chung cho tất cả các bản sao. Do đó, toàn bộ hệ thống vẫn mang tính xác định.

tf.random.Generator cũng có thể được tạo bên trong 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))
WARNING:tensorflow:There are non-GPU devices in `tf.distribute.Strategy`, not using nccl allreduce.
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)
}

Chúng tôi không còn khuyên bạn nên chuyển tf.random.Generator làm đối số cho Strategy.run , vì Strategy.run thường mong đợi các đối số là tensor, không phải trình tạo.

Tiết kiệm máy phát điện

Nói chung để lưu hoặc tuần tự hóa, bạn có thể xử lý tf.random.Generator giống như cách bạn xử lý tf.Variable hoặc tf.Module (hoặc các lớp con của nó). Trong TF có hai cơ chế để tuần tự hóa: CheckpointSavedModel .

Trạm kiểm soát

Trình tạo có thể được lưu và khôi phục miễn phí bằng tf.train.Checkpoint . Luồng số ngẫu nhiên từ điểm khôi phục sẽ giống như luồng từ điểm lưu.

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)

Bạn cũng có thể lưu và khôi phục trong chiến lược phân phối:

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([])))
WARNING:tensorflow:There are non-GPU devices in `tf.distribute.Strategy`, not using nccl allreduce.
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)
}

Bạn nên đảm bảo rằng các bản sao không khác nhau trong lịch sử cuộc gọi RNG của chúng (ví dụ: một bản sao thực hiện một cuộc gọi RNG trong khi một bản sao khác thực hiện hai cuộc gọi RNG) trước khi lưu. Nếu không, các trạng thái RNG bên trong của chúng sẽ khác nhau và tf.train.Checkpoint (chỉ lưu trạng thái của bản sao đầu tiên) sẽ không khôi phục đúng tất cả các bản sao.

Bạn cũng có thể khôi phục một điểm kiểm tra đã lưu thành một chiến lược phân phối khác với số lượng bản sao khác nhau. Vì đối tượng tf.random.Generator được tạo trong một chiến lược chỉ có thể được sử dụng trong cùng một chiến lược, nên để khôi phục về một chiến lược khác, bạn phải tạo một tf.random.Generator mới trong chiến lược mục tiêu và một tf.train.Checkpoint cho nó, như được hiển thị trong ví dụ này:

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([])))
WARNING:tensorflow:There are non-GPU devices in `tf.distribute.Strategy`, not using nccl allreduce.
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([])))
WARNING:tensorflow:There are non-GPU devices in `tf.distribute.Strategy`, not using nccl allreduce.
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)
}

Mặc dù g1cp1 là các đối tượng khác với g2cp2 , chúng được liên kết thông qua filename tệp điểm kiểm tra chung và tên đối tượng my_generator . Các bản sao chồng chéo giữa các chiến lược (ví dụ: cpu:0cpu:1 ở trên) sẽ có các luồng RNG của chúng được khôi phục đúng cách như trong các ví dụ trước. Đảm bảo này không bao gồm trường hợp trình tạo được lưu trong phạm vi chiến lược và được khôi phục bên ngoài bất kỳ phạm vi chiến lược nào hoặc ngược lại, bởi vì thiết bị bên ngoài chiến lược được coi là khác với bất kỳ bản sao nào trong chiến lược.

SavedModel

tf.random.Generator có thể được lưu vào SavedModel. Trình tạo có thể được tạo trong một phạm vi chiến lược. Việc tiết kiệm cũng có thể xảy ra trong một phạm vi chiến lược.

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())
WARNING:tensorflow:There are non-GPU devices in `tf.distribute.Strategy`, not using nccl allreduce.
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.113884404, 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
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)
2021-09-22 20:45:46.222281: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
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)

Việc tải SavedModel chứa tf.random.Generator vào chiến lược phân phối không được khuyến nghị vì tất cả các bản sao sẽ tạo ra cùng một luồng số ngẫu nhiên (đó là do ID bản sao bị đóng băng trong biểu đồ của SavedModel).

Việc tải một tf.random.Generator phân phối (một trình tạo được tạo trong một chiến lược phân phối) vào một môi trường không phải chiến lược, như ví dụ trên, cũng có một cảnh báo. Trạng thái RNG sẽ được khôi phục đúng cách, nhưng các số ngẫu nhiên được tạo sẽ khác với trình tạo ban đầu trong chiến lược của nó (một lần nữa vì thiết bị bên ngoài chiến lược được coi là khác với bất kỳ bản sao nào trong chiến lược).

RNG không trạng thái

Việc sử dụng RNG không trạng thái rất đơn giản. Vì chúng chỉ là chức năng thuần túy, không có trạng thái hoặc tác dụng phụ nào liên quan.

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)

Mọi RNG không trạng thái yêu cầu đối số seed , đối số này phải là một số nguyên Tensor của hình dạng [2] . Kết quả của op hoàn toàn được xác định bởi hạt giống này.

Thuật toán RNG được sử dụng bởi RNG không trạng thái phụ thuộc vào thiết bị, có nghĩa là cùng một op chạy trên một thiết bị khác nhau có thể tạo ra các đầu ra khác nhau.

Các thuật toán

Tổng quan

Cả lớp tf.random.Generator và các hàm stateless đều hỗ trợ thuật toán Philox (được viết là "philox" hoặc tf.random.Algorithm.PHILOX ) trên tất cả các thiết bị.

Các thiết bị khác nhau sẽ tạo ra các số nguyên giống nhau, nếu sử dụng cùng một thuật toán và bắt đầu từ cùng một trạng thái. Chúng cũng sẽ tạo ra các số dấu phẩy động "gần như giống nhau", mặc dù có thể có sự khác biệt nhỏ về số do các cách khác nhau mà thiết bị thực hiện tính toán dấu phẩy động (ví dụ: thứ tự giảm).

Thiết bị XLA

Trên các thiết bị chạy bằng XLA (chẳng hạn như TPU và cả CPU / GPU khi XLA được bật), thuật toán ThreeFry (được viết là "threefry" hoặc tf.random.Algorithm.THREEFRY ) cũng được hỗ trợ. Thuật toán này nhanh trên TPU nhưng chậm trên CPU / GPU so với Philox.

Xem bài báo 'Các số ngẫu nhiên song song: Dễ như 1, 2, 3' để biết thêm chi tiết về các thuật toán này.