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

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

ชุดข้อมูลเกณฑ์มาตรฐาน

ใช้ tfds.benchmark(ds) เพื่อเปรียบเทียบวัตถุ tf.data.Dataset

ตรวจสอบให้แน่ใจว่าได้ระบุ batch_size= เพื่อทำให้ผลลัพธ์เป็นมาตรฐาน (เช่น 100 iter/sec -> 3200 ex/sec) ใช้ได้กับ iterable ใดๆ (เช่น 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 เดียว

หากชุดข้อมูลของคุณพอดีกับหน่วยความจำ คุณยังสามารถโหลดชุดข้อมูลทั้งหมดเป็นอาร์เรย์ 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)

นี่เป็นส่วนเสริมของ subsplit 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 ) ให้ถอดรหัสรูปภาพหลังจากกรองตัวอย่างแล้ว
  • เมื่อครอบตัดรูปภาพ ให้ใช้ tf.image.decode_and_crop_jpeg ที่หลอมรวมเข้าด้วยกัน

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

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

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