การสร้างตัวเลขสุ่ม

ดูบน TensorFlow.org ทำงานใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดโน๊ตบุ๊ค

TensorFlow มีชุดตัวสร้างตัวเลขสุ่มหลอก (RNG) ในโมดูล tf.random เอกสารนี้อธิบายวิธีที่คุณสามารถควบคุมตัวสร้างตัวเลขสุ่ม และวิธีที่ตัวสร้างเหล่านี้โต้ตอบกับระบบย่อยเทนเซอร์โฟลว์อื่นๆ

TensorFlow มีแนวทางสองวิธีในการควบคุมกระบวนการสร้างตัวเลขสุ่ม:

  1. ผ่านการใช้วัตถุ tf.random.Generator อย่างชัดเจน แต่ละอ็อบเจ็กต์ดังกล่าวจะรักษาสถานะ (ใน tf.Variable ) ที่จะเปลี่ยนแปลงหลังจากการสร้างตัวเลขแต่ละครั้ง

  2. ผ่านฟังก์ชันสุ่มไร้สัญชาติที่ใช้งานได้จริง เช่น tf.random.stateless_uniform การเรียกใช้ฟังก์ชันเหล่านี้ด้วยอาร์กิวเมนต์เดียวกัน (ซึ่งรวมถึง seed) และบนอุปกรณ์เดียวกันจะให้ผลลัพธ์เดียวกันเสมอ

ติดตั้ง

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

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

มีหลายวิธีในการสร้างวัตถุตัวสร้าง วิธีที่ง่ายที่สุดคือ Generator.from_seed ดังที่แสดงด้านบนซึ่งสร้างเครื่องกำเนิดจากเมล็ด 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.5658046  -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.9078738   0.11009752  1.037219  ]
 [ 0.661036    0.4169741   1.4539026 ]], shape=(2, 3), dtype=float32)

ยังมีวิธีอื่นๆ ในการสร้างตัวสร้าง เช่น จากสถานะที่ชัดเจน ซึ่งไม่ครอบคลุมในคู่มือนี้

เมื่อใช้ tf.random.get_global_generator เพื่อรับตัวสร้างส่วนกลาง คุณต้องระมัดระวังเกี่ยวกับการจัดวางอุปกรณ์ ตัวสร้างโกลบอลถูกสร้างขึ้น (จากสถานะที่ไม่ได้กำหนดไว้) ในครั้งแรกที่เรียก tf.random.get_global_generator และวางบนอุปกรณ์เริ่มต้นที่การโทรนั้น ตัวอย่างเช่น หากไซต์แรกที่คุณเรียกใช้ tf.random.get_global_generator อยู่ภายใน tf.device("gpu") ตัวสร้างส่วนกลางจะถูกวางไว้บน GPU และใช้ตัวสร้างโกลบอลในภายหลังจาก CPU จะ มีสำเนา GPU-to-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.4142675, shape=(), dtype=float32)

คุณสามารถทำการแยกแบบเรียกซ้ำโดยเรียก 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)
2

ปฏิสัมพันธ์กับกลยุทธ์การจัดจำหน่าย

มีสองวิธีที่ 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)
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)
ตัวยึดตำแหน่ง23

โปรดทราบว่าการใช้งานนี้อาจมีปัญหาด้านประสิทธิภาพเนื่องจากอุปกรณ์ของตัวสร้างแตกต่างจากแบบจำลอง

การสร้างเครื่องกำเนิดไฟฟ้าภายในกลยุทธ์การจัดจำหน่าย

หากตัวสร้างถูกสร้างขึ้นภายในขอบเขตของกลยุทธ์ แต่ละแบบจำลองจะได้รับกระแสตัวเลขสุ่มที่แตกต่างกันและเป็นอิสระ

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)
}

หากตัวกำเนิดถูก seed (เช่น สร้างโดย Generator.from_seed ) ตัวเลขสุ่มจะถูกกำหนดโดย 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))
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)
}

เราไม่แนะนำให้ส่ง tf.random.Generator เป็นอาร์กิวเมนต์ไปยัง Strategy.run อีกต่อไป เนื่องจาก Strategy.run โดยทั่วไปคาดว่าอาร์กิวเมนต์จะเป็นเมตริกซ์ ไม่ใช่ตัวสร้าง

ประหยัดเครื่องกำเนิดไฟฟ้า

โดยทั่วไปสำหรับการบันทึกหรือการทำให้เป็นอันดับ คุณสามารถจัดการ tf.random.Generator ได้ เช่นเดียวกับที่คุณจะจัดการกับ tf.Variable หรือ tf.Module (หรือคลาสย่อย) ใน TF มีสองกลไกสำหรับการทำให้เป็นอันดับ: Checkpoint และ SavedModel

ด่าน

เครื่องกำเนิดไฟฟ้าสามารถบันทึกและกู้คืนได้อย่างอิสระโดยใช้ 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([])))
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)
}

คุณควรตรวจสอบให้แน่ใจว่าแบบจำลองไม่แตกต่างกันในประวัติการโทร RNG (เช่น แบบจำลองหนึ่งทำการโทร RNG หนึ่งครั้ง ในขณะที่อีกเครื่องหนึ่งทำการเรียก RNG สองครั้ง) ก่อนบันทึก มิฉะนั้น สถานะ RNG ภายในจะแตกต่างกัน และ tf.train.Checkpoint (ซึ่งจะบันทึกเฉพาะสถานะของแบบจำลองแรกเท่านั้น) จะไม่สามารถกู้คืนแบบจำลองทั้งหมดได้อย่างถูกต้อง

คุณยังสามารถกู้คืนจุดตรวจสอบที่บันทึกไว้เป็นกลยุทธ์การกระจายที่แตกต่างกันด้วยจำนวนแบบจำลองที่แตกต่างกัน เนื่องจากวัตถุ tf.random.Generator ที่สร้างขึ้นในกลยุทธ์สามารถใช้ได้ในกลยุทธ์เดียวกันเท่านั้น หากต้องการกู้คืนเป็นกลยุทธ์อื่น คุณต้องสร้าง tf.random.Generator ใหม่ในกลยุทธ์เป้าหมายและ tf.train.Checkpoint ใหม่ 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([])))
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)
}

แม้ว่า g1 และ cp1 เป็นอ็อบเจ็กต์ที่แตกต่างจาก g2 และ cp2 แต่ก็มีการเชื่อมโยงผ่าน filename จุดตรวจทั่วไปและชื่ออ็อบเจ็กต์ my_generator การจำลองที่ทับซ้อนกันระหว่างกลยุทธ์ต่างๆ (เช่น cpu:0 และ cpu:1 ด้านบน) จะทำให้สตรีม RNG ถูกกู้คืนอย่างเหมาะสมเหมือนในตัวอย่างก่อนหน้านี้ การรับประกันนี้ไม่ครอบคลุมกรณีที่เครื่องกำเนิดไฟฟ้าถูกบันทึกในขอบเขตกลยุทธ์และกู้คืนนอกขอบเขตกลยุทธ์ใดๆ หรือในทางกลับกัน เนื่องจากอุปกรณ์ภายนอกกลยุทธ์จะถือว่าแตกต่างจากแบบจำลองใดๆ ในกลยุทธ์

รูปแบบที่บันทึกไว้

tf.random.Generator สามารถบันทึกลงใน SavedModel ตัวสร้างสามารถสร้างได้ภายในขอบเขตของกลยุทธ์ การออมยังสามารถเกิดขึ้นได้ภายในขอบเขตของกลยุทธ์

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)

ไม่แนะนำให้โหลด SavedModel ที่มี tf.random.Generator ลงในกลยุทธ์การแจกจ่าย เนื่องจากแบบจำลองทั้งหมดจะสร้างสตรีมตัวเลขสุ่มเดียวกัน (ซึ่งเป็นเพราะว่า ID แบบจำลองถูกตรึงไว้ในกราฟของ SavedModel)

การโหลด 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)
ตัวยึดตำแหน่ง53

RNG ไร้สัญชาติทุกอันต้องมีอาร์กิวเมนต์ seed ซึ่งต้องเป็นจำนวนเต็มเทนเซอร์ของรูปร่าง [2] ผลของ op ถูกกำหนดโดยเมล็ดพันธุ์นี้อย่างเต็มที่

อัลกอริธึม RNG ที่ใช้โดย RNG ไร้สัญชาตินั้นขึ้นอยู่กับอุปกรณ์ ซึ่งหมายความว่า op เดียวกันที่ทำงานบนอุปกรณ์อื่นอาจสร้างเอาต์พุตที่แตกต่างกัน

อัลกอริทึม

ทั่วไป

ทั้งคลาส tf.random.Generator และฟังก์ชัน stateless รองรับอัลกอริธึม Philox (เขียนว่า "philox" หรือ tf.random.Algorithm.PHILOX ) บนอุปกรณ์ทั้งหมด

อุปกรณ์ต่างๆ จะสร้างตัวเลขจำนวนเต็มเดียวกัน หากใช้อัลกอริธึมเดียวกันและเริ่มจากสถานะเดียวกัน พวกเขายังจะสร้างตัวเลขทศนิยมที่ "เกือบจะเหมือนกัน" แม้ว่าอาจมีความคลาดเคลื่อนทางตัวเลขเล็กน้อยที่เกิดจากวิธีต่างๆ ที่อุปกรณ์ดำเนินการคำนวณจุดทศนิยม (เช่น ลำดับการลดลง)

อุปกรณ์ XLA

บนอุปกรณ์ที่ใช้ XLA (เช่น TPU และ CPU/GPU เมื่อเปิดใช้งาน XLA) อัลกอริทึม ThreeFry (เขียนว่า "threefry" หรือ tf.random.Algorithm.THREEFRY ) ยังได้รับการสนับสนุน อัลกอริทึมนี้เร็วบน TPU แต่ช้าบน CPU/GPU เมื่อเทียบกับ Philox

ดูกระดาษ 'Parallel Random Numbers: As Easy as 1, 2, 3' สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับอัลกอริทึมเหล่านี้