ดูบน TensorFlow.org | ทำงานใน Google Colab | ดูแหล่งที่มาบน GitHub | ดาวน์โหลดโน๊ตบุ๊ค |
TensorFlow มีชุดตัวสร้างตัวเลขสุ่มหลอก (RNG) ในโมดูล tf.random
เอกสารนี้อธิบายวิธีที่คุณสามารถควบคุมตัวสร้างตัวเลขสุ่ม และวิธีที่ตัวสร้างเหล่านี้โต้ตอบกับระบบย่อยเทนเซอร์โฟลว์อื่นๆ
TensorFlow มีแนวทางสองวิธีในการควบคุมกระบวนการสร้างตัวเลขสุ่ม:
ผ่านการใช้วัตถุ
tf.random.Generator
อย่างชัดเจน แต่ละอ็อบเจ็กต์ดังกล่าวจะรักษาสถานะ (ในtf.Variable
) ที่จะเปลี่ยนแปลงหลังจากการสร้างตัวเลขแต่ละครั้งผ่านฟังก์ชันสุ่มไร้สัญชาติที่ใช้งานได้จริง เช่น
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' สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับอัลกอริทึมเหล่านี้