ภาพรวม
คู่มือนี้จะถือว่ามีความคุ้นเคยกับ TensorFlow Profiler และ tf.data โดยมีจุดมุ่งหมายเพื่อให้คำแนะนำทีละขั้นตอนพร้อมตัวอย่างเพื่อช่วยให้ผู้ใช้วินิจฉัยและแก้ไขปัญหาประสิทธิภาพของไปป์ไลน์อินพุต
ในการเริ่มต้น ให้รวบรวมโปรไฟล์ของงาน TensorFlow ของคุณ คำแนะนำเกี่ยวกับวิธีการดังกล่าวมีให้สำหรับ CPU/GPU และ Cloud TPU

ขั้นตอนการวิเคราะห์ที่มีรายละเอียดด้านล่างมุ่งเน้นไปที่เครื่องมือแสดงการติดตามในตัวสร้างโปรไฟล์ เครื่องมือนี้แสดงไทม์ไลน์ที่แสดงระยะเวลาของการดำเนินการที่ดำเนินการโดยโปรแกรม TensorFlow ของคุณ และช่วยให้คุณระบุได้ว่าการดำเนินการใดใช้เวลาดำเนินการนานที่สุด สำหรับข้อมูลเพิ่มเติมเกี่ยวกับโปรแกรมดูการติดตาม โปรดดู ส่วนนี้ ของคำแนะนำ TF Profiler โดยทั่วไป เหตุการณ์ tf.data จะปรากฏบนไทม์ไลน์ CPU ของโฮสต์
ขั้นตอนการวิเคราะห์
โปรดติดตามขั้นตอนการทำงานด้านล่างนี้ หากคุณมีข้อเสนอแนะเพื่อช่วยเราปรับปรุง โปรด สร้างปัญหา GitHub โดยมีป้ายกำกับ “comp:data”
1. ไปป์ไลน์ tf.data ของคุณสร้างข้อมูลเร็วเพียงพอหรือไม่
เริ่มต้นด้วยการตรวจสอบว่าไปป์ไลน์อินพุตเป็นจุดคอขวดสำหรับโปรแกรม TensorFlow ของคุณหรือไม่
โดยมองหาตัวเลือก IteratorGetNext::DoCompute ในโปรแกรมดูการติดตาม โดยทั่วไป คุณคาดว่าจะเห็นสิ่งเหล่านี้ตั้งแต่เริ่มต้นขั้นตอน ส่วนเหล่านี้แสดงถึงเวลาที่ไปป์ไลน์อินพุตของคุณเพื่อให้ได้ชุดองค์ประกอบเมื่อมีการร้องขอ หากคุณกำลังใช้ keras หรือวนซ้ำชุดข้อมูลของคุณใน tf.function สิ่งเหล่านี้ควรพบในเธรด tf_data_iterator_get_next
โปรดทราบว่าหากคุณใช้ กลยุทธ์การกระจาย คุณอาจเห็น IteratorGetNextAsOptional::DoCompute แทนที่จะเป็น IteratorGetNext::DoCompute (ณ TF 2.3)

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

หากการโทรกลับมาช้า tf.data จะไม่สามารถดำเนินการตามคำขอของผู้บริโภคได้ ดำเนินการต่อในส่วนถัดไป
2. คุณกำลังดึงข้อมูลล่วงหน้าหรือไม่?
แนวทางปฏิบัติที่ดีที่สุดสำหรับประสิทธิภาพของไปป์ไลน์อินพุตคือการแทรกการแปลง tf.data.Dataset.prefetch ที่ส่วนท้ายของไปป์ไลน์ tf.data การแปลงนี้จะซ้อนทับการคำนวณก่อนการประมวลผลของไปป์ไลน์อินพุตกับขั้นตอนถัดไปของการคำนวณโมเดล และจำเป็นสำหรับประสิทธิภาพไปป์ไลน์อินพุตที่เหมาะสมที่สุดเมื่อฝึกฝนโมเดลของคุณ หากคุณกำลังดึงข้อมูลล่วงหน้า คุณควรเห็น Iterator::Prefetch ชิ้นบนเธรดเดียวกันกับตัวเลือก IteratorGetNext::DoCompute

หากคุณไม่มี prefetch ที่ส่วนท้ายของไปป์ไลน์ คุณควรเพิ่มเข้าไป สำหรับข้อมูลเพิ่มเติมเกี่ยวกับคำแนะนำด้านประสิทธิภาพ tf.data โปรดดู คู่มือประสิทธิภาพของ tf.data
หากคุณดึงข้อมูลล่วงหน้าแล้ว และไปป์ไลน์อินพุตยังคงเป็นคอขวดของคุณ ให้ไปที่ส่วนถัดไปเพื่อวิเคราะห์ประสิทธิภาพเพิ่มเติม
3. คุณใช้งาน CPU ในระดับสูงหรือไม่?
tf.data ได้รับปริมาณงานสูงโดยพยายามใช้ทรัพยากรที่มีอยู่ให้เกิดประโยชน์สูงสุด โดยทั่วไป แม้ว่าในขณะที่รันโมเดลของคุณบนตัวเร่งความเร็ว เช่น GPU หรือ TPU ไปป์ไลน์ tf.data ก็ยังทำงานบน CPU คุณสามารถตรวจสอบการใช้งานของคุณด้วยเครื่องมือเช่น sar และ htop หรือใน คอนโซลการตรวจสอบระบบคลาวด์ หากคุณใช้งานบน GCP
หากการใช้งานของคุณต่ำ แสดงว่าไปป์ไลน์อินพุตของคุณอาจไม่ได้รับประโยชน์เต็มที่จาก CPU ของโฮสต์ คุณควรศึกษา คู่มือประสิทธิภาพ tf.data เพื่อดูแนวทางปฏิบัติที่ดีที่สุด หากคุณใช้แนวทางปฏิบัติที่ดีที่สุดและการใช้งานและปริมาณงานยังต่ำ ให้ไปที่ การวิเคราะห์คอขวด ด้านล่าง
หากการใช้งานของคุณใกล้ถึงขีดจำกัดทรัพยากร แล้ว เพื่อปรับปรุงประสิทธิภาพให้ดียิ่งขึ้น คุณจะต้องปรับปรุงประสิทธิภาพของไปป์ไลน์อินพุตของคุณ (เช่น การหลีกเลี่ยงการคำนวณที่ไม่จำเป็น) หรือลดภาระการคำนวณ
คุณสามารถปรับปรุงประสิทธิภาพของไปป์ไลน์อินพุตของคุณได้โดยหลีกเลี่ยงการคำนวณที่ไม่จำเป็นใน tf.data วิธีหนึ่งในการทำเช่นนี้คือการแทรกการแปลง tf.data.Dataset.cache หลังจากทำงานหนักในการคำนวณ หากข้อมูลของคุณพอดีกับหน่วยความจำ ซึ่งจะช่วยลดการคำนวณโดยมีค่าใช้จ่ายจากการใช้หน่วยความจำที่เพิ่มขึ้น นอกจากนี้ การปิดใช้งานการทำงานแบบขนานภายใน tf.data มีศักยภาพในการเพิ่มประสิทธิภาพได้ > 10% และสามารถทำได้โดยการตั้งค่าตัวเลือกต่อไปนี้ในไปป์ไลน์อินพุตของคุณ:
dataset = ...
options = tf.data.Options()
options.experimental_threading.max_intra_op_parallelism = 1
dataset = dataset.with_options(options)
4. การวิเคราะห์คอขวด
ส่วนต่อไปนี้จะอธิบายวิธีการอ่านเหตุการณ์ tf.data ในโปรแกรมดูการติดตามเพื่อทำความเข้าใจว่าจุดคอขวดอยู่ที่ใดและกลยุทธ์การบรรเทาที่เป็นไปได้
ทำความเข้าใจเหตุการณ์ tf.data ใน Profiler
แต่ละเหตุการณ์ tf.data ใน Profiler มีชื่อ Iterator::<Dataset> โดยที่ <Dataset> เป็นชื่อของแหล่งที่มาของชุดข้อมูลหรือการแปลง แต่ละเหตุการณ์ยังมีชื่อยาว Iterator::<Dataset_1>::...::<Dataset_n> ซึ่งคุณสามารถดูได้โดยคลิกที่เหตุการณ์ tf.data ในชื่อแบบยาว <Dataset_n> จะจับคู่ <Dataset> จากชื่อ (แบบสั้น) และชุดข้อมูลอื่นๆ ในชื่อแบบยาวแสดงถึงการแปลงดาวน์สตรีม

ตัวอย่างเช่น ภาพหน้าจอด้านบนถูกสร้างขึ้นจากรหัสต่อไปนี้:
dataset = tf.data.Dataset.range(10)
dataset = dataset.map(lambda x: x)
dataset = dataset.repeat(2)
dataset = dataset.batch(5)
ในที่นี้ Iterator::Map มีชื่อยาว Iterator::BatchV2::FiniteRepeat::Map โปรดทราบว่าชื่อชุดข้อมูลอาจแตกต่างกันเล็กน้อยจาก python API (เช่น FiniteRepeat แทนที่จะเป็น Repeat) แต่ควรใช้งานง่ายพอที่จะแยกวิเคราะห์ได้
การแปลงแบบซิงโครนัสและอะซิงโครนัส
สำหรับการแปลง tf.data แบบซิงโครนัส (เช่น Batch และ Map ) คุณจะเห็นเหตุการณ์จากการแปลงต้นทางบนเธรดเดียวกัน ในตัวอย่างข้างต้น เนื่องจากการแปลงทั้งหมดที่ใช้เป็นแบบซิงโครนัส เหตุการณ์ทั้งหมดจึงปรากฏบนเธรดเดียวกัน
สำหรับการแปลงแบบอะซิงโครนัส (เช่น Prefetch , ParallelMap , ParallelInterleave และ MapAndBatch ) เหตุการณ์จากการแปลงอัปสตรีมจะอยู่ในเธรดอื่น ในกรณีเช่นนี้ “ชื่อยาว” สามารถช่วยให้คุณระบุการเปลี่ยนแปลงในไปป์ไลน์ที่เหตุการณ์สอดคล้องกับได้

ตัวอย่างเช่น ภาพหน้าจอด้านบนถูกสร้างขึ้นจากรหัสต่อไปนี้:
dataset = tf.data.Dataset.range(10)
dataset = dataset.map(lambda x: x)
dataset = dataset.repeat(2)
dataset = dataset.batch(5)
dataset = dataset.prefetch(1)
ที่นี่ Iterator::Prefetch อยู่ในเธรด tf_data_iterator_get_next เนื่องจาก Prefetch เป็นแบบอะซิงโครนัส เหตุการณ์อินพุต ( BatchV2 ) จะอยู่ในเธรดอื่น และสามารถระบุตำแหน่งได้โดยการค้นหาชื่อยาว Iterator::Prefetch::BatchV2 ในกรณีนี้ จะอยู่ในเธรด tf_data_iterator_resource จากชื่อยาว คุณสามารถอนุมานได้ว่า BatchV2 อยู่ต้นทางของ Prefetch นอกจากนี้ parent_id ของเหตุการณ์ BatchV2 จะตรงกับ ID ของเหตุการณ์ Prefetch
การระบุจุดคอขวด
โดยทั่วไป หากต้องการระบุปัญหาคอขวดในไปป์ไลน์อินพุตของคุณ ให้เดินไปป์ไลน์อินพุตจากการเปลี่ยนแปลงด้านนอกสุดไปจนถึงแหล่งที่มา เริ่มต้นจากการเปลี่ยนแปลงขั้นสุดท้ายในไปป์ไลน์ของคุณ ทำซ้ำในการแปลงอัปสตรีมจนกว่าคุณจะพบการเปลี่ยนแปลงที่ช้าหรือเข้าถึงชุดข้อมูลต้นทาง เช่น TFRecord ในตัวอย่างด้านบน คุณจะเริ่มจาก Prefetch จากนั้นเดินต้นทางไปยัง BatchV2 , FiniteRepeat , Map และสุดท้ายคือ Range
โดยทั่วไป การแปลงที่ช้าจะสอดคล้องกับการเปลี่ยนแปลงที่มีเหตุการณ์ยาว แต่มีเหตุการณ์อินพุตสั้น ตัวอย่างบางส่วนดังต่อไปนี้
โปรดทราบว่าการเปลี่ยนแปลงขั้นสุดท้าย (ด้านนอกสุด) ในไปป์ไลน์อินพุตโฮสต์ส่วนใหญ่คือ Iterator::Model การแปลงโมเดลถูกนำมาใช้โดยอัตโนมัติโดยรันไทม์ tf.data และใช้สำหรับการกำหนดเครื่องมือและการปรับแต่งประสิทธิภาพของไปป์ไลน์อินพุตโดยอัตโนมัติ
หากงานของคุณใช้ กลยุทธ์การกระจาย โปรแกรม ดูการติดตามจะมีเหตุการณ์เพิ่มเติมที่สอดคล้องกับไปป์ไลน์อินพุตของอุปกรณ์ การเปลี่ยนแปลงด้านนอกสุดของไปป์ไลน์อุปกรณ์ (ซ้อนอยู่ใต้ IteratorGetNextOp::DoCompute หรือ IteratorGetNextAsOptionalOp::DoCompute ) จะเป็นเหตุการณ์ Iterator::Prefetch พร้อมด้วยเหตุการณ์ Upstream Iterator::Generator คุณสามารถค้นหาไปป์ไลน์ของโฮสต์ที่เกี่ยวข้องได้โดยค้นหาเหตุการณ์ Iterator::Model
ตัวอย่างที่ 1

ภาพหน้าจอด้านบนถูกสร้างขึ้นจากไปป์ไลน์อินพุตต่อไปนี้:
dataset = tf.data.TFRecordDataset(filename)
dataset = dataset.map(parse_record)
dataset = dataset.batch(32)
dataset = dataset.repeat()
ในภาพหน้าจอ ให้สังเกตว่า (1) เหตุการณ์ Iterator::Map ยาว แต่ (2) เหตุการณ์อินพุต ( Iterator::FlatMap ) กลับมาอย่างรวดเร็ว สิ่งนี้ชี้ให้เห็นว่าการเปลี่ยนแปลงแผนที่ตามลำดับคือจุดคอขวด
โปรดทราบว่าในภาพหน้าจอ เหตุการณ์ InstantiatedCapturedFunction::Run สอดคล้องกับเวลาที่ใช้ในการเรียกใช้ฟังก์ชันแผนที่
ตัวอย่างที่ 2

ภาพหน้าจอด้านบนถูกสร้างขึ้นจากไปป์ไลน์อินพุตต่อไปนี้:
dataset = tf.data.TFRecordDataset(filename)
dataset = dataset.map(parse_record, num_parallel_calls=2)
dataset = dataset.batch(32)
dataset = dataset.repeat()
ตัวอย่างนี้คล้ายกับตัวอย่างด้านบน แต่ใช้ ParallelMap แทน Map เราสังเกตตรงนี้ว่า (1) เหตุการณ์ Iterator::ParallelMap ยาว แต่ (2) เหตุการณ์อินพุต Iterator::FlatMap (ซึ่งอยู่บนเธรดอื่น เนื่องจาก ParallelMap เป็นแบบอะซิงโครนัส) สั้น สิ่งนี้ชี้ให้เห็นว่าการแปลง ParallelMap นั้นเป็นคอขวด
จัดการกับปัญหาคอขวด
ชุดข้อมูลต้นทาง
หากคุณระบุแหล่งที่มาของชุดข้อมูลเป็นจุดคอขวด เช่น การอ่านจากไฟล์ TFRecord คุณสามารถปรับปรุงประสิทธิภาพได้โดยการแยกข้อมูลแบบขนาน ในการดำเนินการดังกล่าว ตรวจสอบให้แน่ใจว่าข้อมูลของคุณถูกแบ่งกลุ่มเป็นหลายไฟล์ และใช้ tf.data.Dataset.interleave โดยตั้งค่าพารามิเตอร์ num_parallel_calls เป็น tf.data.AUTOTUNE หากการกำหนดระดับไม่สำคัญสำหรับโปรแกรมของคุณ คุณสามารถปรับปรุงประสิทธิภาพเพิ่มเติมได้โดยการตั้งค่าสถานะ deterministic=False บน tf.data.Dataset.interleave ตั้งแต่ TF 2.2 ตัวอย่างเช่น หากคุณกำลังอ่านจาก TFRecords คุณสามารถทำสิ่งต่อไปนี้:
dataset = tf.data.Dataset.from_tensor_slices(filenames)
dataset = dataset.interleave(tf.data.TFRecordDataset,
num_parallel_calls=tf.data.AUTOTUNE,
deterministic=False)
โปรดทราบว่าไฟล์ที่แบ่งส่วนควรมีขนาดใหญ่พอสมควรเพื่อตัดค่าใช้จ่ายในการเปิดไฟล์ สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการดึงข้อมูลแบบขนาน โปรดดู ส่วนนี้ ของคู่มือประสิทธิภาพ tf.data
ชุดข้อมูลการแปลง
หากคุณระบุการเปลี่ยนแปลงระดับกลางของ tf.data ว่าเป็นคอขวด คุณสามารถแก้ไขได้โดยการทำให้การแปลงขนานกันหรือ แคชการคำนวณ หากข้อมูลของคุณพอดีกับหน่วยความจำและเหมาะสม การแปลงบางอย่างเช่น Map มีคู่ขนานกัน คู่มือประสิทธิภาพ tf.data สาธิต วิธีทำให้สิ่งเหล่านี้ขนานกัน การแปลงอื่นๆ เช่น Filter , Unbatch และ Batch จะเป็นไปตามลำดับโดยธรรมชาติ คุณสามารถทำให้มันขนานกันได้โดยการแนะนำ "ความเท่าเทียมภายนอก" ตัวอย่างเช่น สมมติว่าไปป์ไลน์อินพุตของคุณในตอนแรกมีลักษณะดังนี้ โดยที่ Batch เป็นจุดคอขวด:
filenames = tf.data.Dataset.list_files(file_path, shuffle=is_training)
dataset = filenames_to_dataset(filenames)
dataset = dataset.batch(batch_size)
คุณสามารถแนะนำ "ความขนานภายนอก" ได้โดยการรันสำเนาไปป์ไลน์อินพุตหลายชุดบนอินพุตแบบแบ่งส่วนและรวมผลลัพธ์:
filenames = tf.data.Dataset.list_files(file_path, shuffle=is_training)
def make_dataset(shard_index):
filenames = filenames.shard(NUM_SHARDS, shard_index)
dataset = filenames_to_dataset(filenames)
Return dataset.batch(batch_size)
indices = tf.data.Dataset.range(NUM_SHARDS)
dataset = indices.interleave(make_dataset,
num_parallel_calls=tf.data.AUTOTUNE)
dataset = dataset.prefetch(tf.data.AUTOTUNE)
แหล่งข้อมูลเพิ่มเติม
- คู่มือประสิทธิภาพ tf.data เกี่ยวกับวิธีเขียนไปป์ไลน์อินพุต
tf.dataประสิทธิภาพ - วิดีโอ Inside TensorFlow: แนวทางปฏิบัติที่ดีที่สุด
tf.data - คู่มือโปรไฟล์
- บทช่วยสอนสร้างโปรไฟล์ด้วย colab