เคล็ดลับประสิทธิภาพ

เอกสารนี้ให้คำแนะนำด้านประสิทธิภาพเฉพาะสำหรับชุดข้อมูล TensorFlow (TFDS) โปรดทราบว่า TFDS จัดเตรียมชุดข้อมูลเป็นออบเจ็กต์ tf.data.Dataset ดังนั้นคำแนะนำจาก คู่มือ tf.data จึงยังคงมีผลบังคับใช้

ชุดข้อมูลเปรียบเทียบ

ใช้ tfds.benchmark(ds) เพื่อเปรียบเทียบออบเจ็กต์ tf.data.Dataset ใดๆ

ตรวจสอบให้แน่ใจว่าได้ระบุ batch_size= เพื่อทำให้ผลลัพธ์เป็นมาตรฐาน (เช่น 100 iter/วินาที -> 3200 ex/วินาที) สิ่งนี้ใช้ได้กับการวนซ้ำใด ๆ (เช่น tfds.benchmark(tfds.as_numpy(ds)) )

ds = tfds.load('mnist', split='train').batch(32).prefetch()
# Display some benchmark statistics
tfds.benchmark(ds, batch_size=32)
# Second iteration is much faster, due to auto-caching
tfds.benchmark(ds, batch_size=32)

ชุดข้อมูลขนาดเล็ก (น้อยกว่า 1 GB)

ชุดข้อมูล TFDS ทั้งหมดจัดเก็บข้อมูลบนดิสก์ในรูปแบบ TFRecord สำหรับชุดข้อมูลขนาดเล็ก (เช่น MNIST, CIFAR-10/-100) การอ่านจาก .tfrecord สามารถเพิ่มโอเวอร์เฮดได้อย่างมาก

เนื่องจากชุดข้อมูลเหล่านั้นมีขนาดพอดีกับหน่วยความจำ จึงเป็นไปได้ที่จะปรับปรุงประสิทธิภาพได้อย่างมากโดยการแคชหรือโหลดชุดข้อมูลล่วงหน้า โปรดทราบว่า TFDS จะแคชชุดข้อมูลขนาดเล็กโดยอัตโนมัติ (ส่วนต่อไปนี้มีรายละเอียด)

การแคชชุดข้อมูล

นี่คือตัวอย่างของไปป์ไลน์ข้อมูลซึ่งจะแคชชุดข้อมูลอย่างชัดเจนหลังจากทำให้รูปภาพเป็นมาตรฐาน

def normalize_img(image, label):
  """Normalizes images: `uint8` -> `float32`."""
  return tf.cast(image, tf.float32) / 255., label


ds, ds_info = tfds.load(
    'mnist',
    split='train',
    as_supervised=True,  # returns `(img, label)` instead of dict(image=, ...)
    with_info=True,
)
# Applying normalization before `ds.cache()` to re-use it.
# Note: Random transformations (e.g. images augmentations) should be applied
# after both `ds.cache()` (to avoid caching randomness) and `ds.batch()` (for
# vectorization [1]).
ds = ds.map(normalize_img, num_parallel_calls=tf.data.AUTOTUNE)
ds = ds.cache()
# For true randomness, we set the shuffle buffer to the full dataset size.
ds = ds.shuffle(ds_info.splits['train'].num_examples)
# Batch after shuffling to get unique batches at each epoch.
ds = ds.batch(128)
ds = ds.prefetch(tf.data.experimental.AUTOTUNE)

เมื่อวนซ้ำชุดข้อมูลนี้ การวนซ้ำครั้งที่สองจะเร็วกว่าชุดแรกมากเนื่องจากการแคช

แคชอัตโนมัติ

ตามค่าเริ่มต้น ชุดข้อมูล TFDS แคชอัตโนมัติ (พร้อม ds.cache() ) ซึ่งเป็นไปตามข้อจำกัดต่อไปนี้:

  • ขนาดชุดข้อมูลทั้งหมด (แยกทั้งหมด) ถูกกำหนดไว้และ < 250 MiB
  • shuffle_files ถูกปิดใช้งาน หรืออ่านเพียงส่วนเดียวเท่านั้น

คุณสามารถเลือกไม่ใช้การแคชอัตโนมัติได้โดยส่ง try_autocaching=False ไปที่ tfds.ReadConfig ใน tfds.load ดูเอกสารประกอบแคตตาล็อกชุดข้อมูลเพื่อดูว่าชุดข้อมูลใดจะใช้แคชอัตโนมัติหรือไม่

กำลังโหลดข้อมูลทั้งหมดเป็นเทนเซอร์ตัวเดียว

หากชุดข้อมูลของคุณพอดีกับหน่วยความจำ คุณยังสามารถโหลดชุดข้อมูลทั้งหมดเป็นอาร์เรย์ Tensor หรือ NumPy เดียวได้ สามารถทำได้โดยการตั้ง batch_size=-1 เพื่อแบทช์ตัวอย่างทั้งหมดใน tf.Tensor เดียว จากนั้นใช้ tfds.as_numpy สำหรับการแปลงจาก tf.Tensor เป็น np.array

(img_train, label_train), (img_test, label_test) = tfds.as_numpy(tfds.load(
    'mnist',
    split=['train', 'test'],
    batch_size=-1,
    as_supervised=True,
))

ชุดข้อมูลขนาดใหญ่

ชุดข้อมูลขนาดใหญ่จะถูกแบ่งย่อย (แบ่งออกเป็นหลายไฟล์) และโดยทั่วไปจะไม่พอดีกับหน่วยความจำ ดังนั้นจึงไม่ควรแคช

สุ่มและการฝึกอบรม

ในระหว่างการฝึก สิ่งสำคัญคือต้องสับเปลี่ยนข้อมูลให้ดี การสับเปลี่ยนข้อมูลที่ไม่ดีอาจส่งผลให้ความแม่นยำในการฝึกลดลง

นอกเหนือจากการใช้ ds.shuffle เพื่อสับเปลี่ยนบันทึกแล้ว คุณควรตั้ง shuffle_files=True เพื่อให้มีลักษณะการสับเปลี่ยนที่ดีสำหรับชุดข้อมูลขนาดใหญ่ที่แบ่งเป็นหลายไฟล์ มิฉะนั้น ยุคจะอ่านชิ้นส่วนในลำดับเดียวกัน ดังนั้นข้อมูลจะไม่ถูกสุ่มอย่างแท้จริง

ds = tfds.load('imagenet2012', split='train', shuffle_files=True)

นอกจากนี้ เมื่อ shuffle_files=True TFDS จะปิดใช้งาน options.deterministic ซึ่งอาจช่วยเพิ่มประสิทธิภาพเล็กน้อย หากต้องการรับการสับเปลี่ยนตามที่กำหนด คุณสามารถเลือกไม่ใช้คุณลักษณะนี้ได้ด้วย tfds.ReadConfig : โดยการตั้ง read_config.shuffle_seed หรือเขียนทับ read_config.options.deterministic

แบ่งข้อมูลของคุณโดยอัตโนมัติระหว่างพนักงาน (TF)

เมื่อฝึกอบรมพนักงานหลายคน คุณสามารถใช้อาร์กิวเมนต์ input_context ของ tfds.ReadConfig ได้ ดังนั้นพนักงานแต่ละคนจะอ่านชุดย่อยของข้อมูล

input_context = tf.distribute.InputContext(
    input_pipeline_id=1,  # Worker id
    num_input_pipelines=4,  # Total number of workers
)
read_config = tfds.ReadConfig(
    input_context=input_context,
)
ds = tfds.load('dataset', split='train', read_config=read_config)

นี่เป็นส่วนเสริมของ API แยกย่อย ขั้นแรก จะใช้ subplit API: train[:50%] จะถูกแปลงเป็นรายการไฟล์ที่จะอ่าน จากนั้น ds.shard() op จะถูกนำมาใช้กับไฟล์เหล่านั้น ตัวอย่างเช่น เมื่อใช้ train[:50%] กับ num_input_pipelines=2 พนักงาน 2 คนแต่ละคนจะอ่านข้อมูล 1/4

เมื่อ shuffle_files=True ไฟล์จะถูกสับภายในผู้ปฏิบัติงานคนเดียว แต่ไม่ข้ามผู้ปฏิบัติงาน ผู้ปฏิบัติงานแต่ละคนจะอ่านชุดย่อยของไฟล์เดียวกันระหว่างยุคต่างๆ

แบ่งข้อมูลของคุณโดยอัตโนมัติระหว่างพนักงาน (Jax)

Jax ช่วยให้คุณสามารถใช้ tfds.split_for_jax_process หรือ tfds.even_splits API เพื่อกระจายข้อมูลของคุณไปยังผู้ปฏิบัติงานได้ ดู คู่มือการแยก API

split = tfds.split_for_jax_process('train', drop_remainder=True)
ds = tfds.load('my_dataset', split=split)

tfds.split_for_jax_process เป็นนามแฝงง่าย ๆ สำหรับ:

# The current `process_index` loads only `1 / process_count` of the data.
splits = tfds.even_splits('train', n=jax.process_count(), drop_remainder=True)
split = splits[jax.process_index()]

การถอดรหัสภาพที่เร็วขึ้น

ตามค่าเริ่มต้น TFDS จะถอดรหัสรูปภาพโดยอัตโนมัติ อย่างไรก็ตาม มีหลายกรณีที่การข้ามการถอดรหัสรูปภาพด้วย tfds.decode.SkipDecoding และใช้ tf.io.decode_image op ด้วยตนเองจะมีประสิทธิภาพมากกว่า:

  • เมื่อกรองตัวอย่าง (ด้วย tf.data.Dataset.filter ) เพื่อถอดรหัสรูปภาพหลังจากกรองตัวอย่างแล้ว
  • เมื่อครอบตัดรูปภาพ ให้ใช้ fused tf.image.decode_and_crop_jpeg op

รหัสสำหรับทั้งสองตัวอย่างมีอยู่ใน คู่มือการถอดรหัส

ข้ามคุณสมบัติที่ไม่ได้ใช้

หากคุณใช้คุณสมบัติเพียงบางส่วนเท่านั้น คุณสามารถข้ามคุณสมบัติบางอย่างไปโดยสิ้นเชิงได้ หากชุดข้อมูลของคุณมีคุณสมบัติที่ไม่ได้ใช้มากมาย การไม่ถอดรหัสสามารถปรับปรุงประสิทธิภาพได้อย่างมาก ดู https://www.tensorflow.org/datasets/decode#only_decode_a_sub-set_of_the_features

tf.data ใช้ RAM ทั้งหมดของฉัน!

หากคุณมี RAM ที่จำกัด หรือหากคุณโหลดชุดข้อมูลจำนวนมากพร้อมกันในขณะที่ใช้ tf.data ต่อไปนี้คือตัวเลือกบางส่วนที่สามารถช่วยได้:

แทนที่ขนาดบัฟเฟอร์

builder.as_dataset(
  read_config=tfds.ReadConfig(
    ...
    override_buffer_size=1024,  # Save quite a bit of RAM.
  ),
  ...
)

สิ่งนี้จะแทนที่ buffer_size ที่ส่งไปยัง TFRecordDataset (หรือเทียบเท่า): https://www.tensorflow.org/api_docs/python/tf/data/TFRecordDataset#args

ใช้ tf.data.Dataset.with_options เพื่อหยุดพฤติกรรมเวทมนตร์

https://www.tensorflow.org/api_docs/python/tf/data/Dataset#with_options

options = tf.data.Options()

# Stop magic stuff that eats up RAM:
options.autotune.enabled = False
options.experimental_distribute.auto_shard_policy = (
  tf.data.experimental.AutoShardPolicy.OFF)
options.experimental_optimization.inject_prefetch = False

data = data.with_options(options)