หน้านี้ได้รับการแปลโดย Cloud Translation API
Switch to English

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

ดูใน 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 2 virtual devices cpu:0 and cpu:1 for using distribution strategy
physical_devices = tf.config.experimental.list_physical_devices("CPU")
tf.config.experimental.set_virtual_device_configuration(
    physical_devices[0], [
        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.43842274 -0.53439844 -0.07710262]
 [ 1.5658046  -0.1012345  -0.2744976 ]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[-0.9323887   0.3864468   1.5209497 ]
 [ 0.54473144 -0.6031506  -0.47044003]], shape=(2, 3), dtype=float32)

มีหลายวิธีในการสร้างวัตถุเครื่องกำเนิดไฟฟ้า วิธีที่ง่ายที่สุดคือ 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.43842274 -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(
[[-1.3158257   2.4625542   1.3490729 ]
 [ 0.77426016 -2.261468   -0.4887435 ]], 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.43842274, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)
tf.Tensor(0.43842274, 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.43842274, 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.10396493, shape=(), dtype=float32)

คุณสามารถแยกแบบวนซ้ำเรียก 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.43842274, 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.43842274, shape=(), dtype=float32)
tf.Tensor(1.6272374, shape=(), dtype=float32)

การส่งผ่านเครื่องกำเนิดไฟฟ้าเป็นอาร์กิวเมนต์ไปยัง tf.function

เมื่อใช้เป็นอาร์กิวเมนต์ของฟังก์ชัน tf.function . วัตถุตัวสร้างที่แตกต่างกันที่มีขนาดสถานะเดียวกัน (ขนาดสถานะถูกกำหนดโดยอัลกอริทึม RNG) จะไม่ทำให้เกิดการย้อนกลับของ 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)
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)
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 `experimental_run_v2` 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():
  try:
    tf.random.Generator.from_seed(1)
  except ValueError as e:
    print("ValueError:", e)
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')
ValueError: Creating a generator within a strategy scope is disallowed, because there is ambiguity on how to replicate a generator (e.g. should it be copied so that each replica gets the same random numbers, or 'split' so that each replica gets different random numbers).

โปรดทราบว่า Strategy.run จะเรียกใช้ฟังก์ชันอาร์กิวเมนต์ในขอบเขตกลยุทธ์โดยปริยาย:

strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
def f():
  tf.random.Generator.from_seed(1)
try:
  strat.run(f)
except ValueError as e:
  print("ValueError:", e)
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 `experimental_run_v2` inside a tf.function to get the best performance.
INFO:tensorflow:Error reported to Coordinator: Creating a generator within a strategy scope is disallowed, because there is ambiguity on how to replicate a generator (e.g. should it be copied so that each replica gets the same random numbers, or 'split' so that each replica gets different random numbers).
Traceback (most recent call last):
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/training/coordinator.py", line 297, in stop_on_exception
    yield
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/distribute/mirrored_run.py", line 323, in run
    self.main_result = self.main_fn(*self.main_args, **self.main_kwargs)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/autograph/impl/api.py", line 275, in wrapper
    return func(*args, **kwargs)
  File "<ipython-input-14-2cd7806456bd>", line 3, in f
    tf.random.Generator.from_seed(1)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py", line 441, in from_seed
    return cls(state=state, alg=alg)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py", line 363, in __init__
    trainable=False)
  File "/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow/python/ops/stateful_random_ops.py", line 378, in _create_variable
    "Creating a generator within a strategy scope is disallowed, because "
ValueError: Creating a generator within a strategy scope is disallowed, because there is ambiguity on how to replicate a generator (e.g. should it be copied so that each replica gets the same random numbers, or 'split' so that each replica gets different random numbers).
ValueError: Creating a generator within a strategy scope is disallowed, because there is ambiguity on how to replicate a generator (e.g. should it be copied so that each replica gets the same random numbers, or 'split' so that each replica gets different random numbers).

การส่งเครื่องกำเนิดไฟฟ้าเป็นอาร์กิวเมนต์ไปยัง Strategy.run

หากคุณต้องการให้แต่ละแบบจำลองใช้เครื่องกำเนิดไฟฟ้าของตัวเองคุณต้องสร้างเครื่องกำเนิดไฟฟ้า n (โดยการคัดลอกหรือแยก) โดยที่ n คือจำนวนของแบบจำลองจากนั้นส่งต่อเป็นอาร์กิวเมนต์ไปยัง Strategy.run

strat = tf.distribute.MirroredStrategy(devices=["cpu:0", "cpu:1"])
gs = tf.random.get_global_generator().split(2)
# to_args is a workaround for the absence of APIs to create arguments for 
# run. It will be replaced when such APIs are available.
def to_args(gs):  
  with strat.scope():
    def f():
      return [gs[tf.distribute.get_replica_context().replica_id_in_sync_group]]
    return strat.run(f)
args = to_args(gs)
def f(g):
  print(g.normal([]))
results = strat.run(f, args=args)
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 `experimental_run_v2` inside a tf.function to get the best performance.
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 `experimental_run_v2` inside a tf.function to get the best performance.
tf.Tensor(-0.42417043, shape=(), dtype=float32)
tf.Tensor(0.08118553, shape=(), dtype=float32)

RNG ไร้สัญชาติ

การใช้ 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.3015898  -0.95385665]], shape=(2, 3), dtype=float32)
tf.Tensor(
[[ 0.5441101   0.20738031  0.07356433]
 [ 0.04643455 -1.3015898  -0.95385665]], shape=(2, 3), dtype=float32)

RNG ไร้สัญชาติทุกตัวต้องมีอาร์กิวเมนต์ seed ซึ่งต้องเป็น Tensor จำนวนเต็มของรูปร่าง [2] ผลลัพธ์ของ 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: Easy as 1, 2, 3' สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับอัลกอริทึมเหล่านี้