เหตุการณ์ภัยพิบัติที่เกี่ยวข้องกับ NaN ในบางครั้งอาจเกิดขึ้นระหว่างโปรแกรม TensorFlow ซึ่งทำให้กระบวนการฝึกอบรมแบบจำลองหมดอำนาจ สาเหตุของเหตุการณ์ดังกล่าวมักไม่ชัดเจน โดยเฉพาะอย่างยิ่งสำหรับแบบจำลองที่มีขนาดและความซับซ้อนที่ไม่ซับซ้อน เพื่อให้ง่ายต่อการแก้ไขจุดบกพร่องของโมเดลประเภทนี้ TensorBoard 2.3+ (ร่วมกับ TensorFlow 2.3+) จึงมีแดชบอร์ดพิเศษที่เรียกว่า Debugger V2 ที่นี่ เราสาธิตวิธีใช้เครื่องมือนี้โดยทำงานผ่านจุดบกพร่องจริงที่เกี่ยวข้องกับ NaN ในโครงข่ายประสาทเทียมที่เขียนด้วย TensorFlow
เทคนิคที่แสดงในบทช่วยสอนนี้ใช้ได้กับกิจกรรมการดีบักประเภทอื่น เช่น การตรวจสอบรูปร่างเทนเซอร์รันไทม์ในโปรแกรมที่ซับซ้อน บทช่วยสอนนี้เน้นที่ NaN เนื่องจากความถี่ในการเกิดขึ้นค่อนข้างสูง
การสังเกตจุดบกพร่อง
ซอร์สโค้ดของโปรแกรม TF2 ที่เราจะทำการดีบั๊กมี อยู่ใน GitHub โปรแกรมตัวอย่างยังถูกรวมไว้ในแพ็คเกจ tensorflow pip (เวอร์ชัน 2.3+) และสามารถเรียกใช้ได้โดย:
python -m tensorflow.python.debug.examples.v2.debug_mnist_v2
โปรแกรม TF2 นี้สร้างการรับรู้หลายชั้น (MLP) และฝึกให้จดจำภาพ MNIST ตัวอย่างนี้จงใจใช้ API ระดับต่ำของ TF2 เพื่อกำหนดโครงสร้างเลเยอร์ที่กำหนดเอง ฟังก์ชันการสูญเสีย และลูปการฝึก เนื่องจากโอกาสที่ข้อบกพร่องของ NaN จะสูงขึ้นเมื่อเราใช้ API ที่ยืดหยุ่นกว่าแต่มีโอกาสเกิดข้อผิดพลาดมากกว่าเมื่อเราใช้ API ที่ง่ายกว่านี้ -to-use แต่ API ระดับสูงที่มีความยืดหยุ่นน้อยกว่าเล็กน้อย เช่น tf.keras
โปรแกรมพิมพ์ความถูกต้องของการทดสอบหลังจากแต่ละขั้นตอนการฝึกอบรม เราจะเห็นในคอนโซลว่าความแม่นยำในการทดสอบติดอยู่ที่ระดับโอกาสใกล้ (~0.1) หลังจากขั้นตอนแรก นี่ไม่ใช่อย่างที่คาดหวังไว้สำหรับการฝึกโมเดล: เราคาดว่าความแม่นยำจะค่อยๆ เข้าใกล้ 1.0 (100%) เมื่อขั้นตอนเพิ่มขึ้น
Accuracy at step 0: 0.216
Accuracy at step 1: 0.098
Accuracy at step 2: 0.098
Accuracy at step 3: 0.098
...
การเดาอย่างมีการศึกษาคือปัญหานี้เกิดจากความไม่เสถียรของตัวเลข เช่น NaN หรืออนันต์ อย่างไรก็ตาม เราจะยืนยันได้อย่างไรว่าเป็นกรณีนี้จริงๆ และเราจะค้นหาการดำเนินการ TensorFlow (op) ที่รับผิดชอบในการสร้างความไม่เสถียรของตัวเลขได้อย่างไร เพื่อตอบคำถามเหล่านี้ ให้ใช้เครื่องมือโปรแกรมบั๊กกี้ด้วย Debugger V2
การวัดโค้ด TensorFlow ด้วย Debugger V2
tf.debugging.experimental.enable_dump_debug_info() เป็นจุดเริ่มต้น API ของ Debugger V2 เป็นเครื่องมือในโปรแกรม TF2 ด้วยรหัสบรรทัดเดียว ตัวอย่างเช่น การเพิ่มบรรทัดต่อไปนี้ใกล้กับจุดเริ่มต้นของโปรแกรมจะทำให้ข้อมูลการดีบักถูกเขียนไปยังไดเร็กทอรีบันทึก (logdir) ที่ /tmp/tfdbg2_logdir ข้อมูลการดีบักครอบคลุมแง่มุมต่างๆ ของรันไทม์ TensorFlow ใน TF2 จะรวมประวัติทั้งหมดของการดำเนินการอย่างกระตือรือร้น การสร้างกราฟที่ดำเนินการโดย @tf.function การดำเนินการของกราฟ ค่าเทนเซอร์ที่สร้างโดยเหตุการณ์การดำเนินการ ตลอดจนตำแหน่งโค้ด (การติดตามสแต็ก Python) ของเหตุการณ์เหล่านั้น . ความสมบูรณ์ของข้อมูลการดีบักทำให้ผู้ใช้สามารถจำกัดจุดบกพร่องที่ไม่ชัดเจนได้
tf.debugging.experimental.enable_dump_debug_info(
"/tmp/tfdbg2_logdir",
tensor_debug_mode="FULL_HEALTH",
circular_buffer_size=-1)
อาร์กิวเมนต์ tensor_debug_mode ควบคุมข้อมูลที่ Debugger V2 แยกจากเมตริกซ์กระตือรือร้นหรือในกราฟ “FULL_HEALTH” เป็นโหมดที่รวบรวมข้อมูลต่อไปนี้เกี่ยวกับเทนเซอร์ประเภททศนิยมแต่ละตัว (เช่น float32 ที่มองเห็นได้ทั่วไปและ bfloat16 dtype ทั่วไปน้อยกว่า):
- DType
- อันดับ
- จำนวนองค์ประกอบทั้งหมด
- การแจกแจงองค์ประกอบประเภทลอยตัวเป็นหมวดหมู่ต่อไปนี้: ค่าจำกัดเชิงลบ (
-), ศูนย์ (0), ค่าจำกัดบวก (+) ค่าอนันต์เชิงลบ (-∞) ค่าอินฟินิตี้บวก (+∞) และNaN
โหมด “FULL_HEALTH” เหมาะสำหรับการดีบักจุดบกพร่องที่เกี่ยวข้องกับ NaN และอินฟินิตี้ ดูด้านล่างสำหรับ tensor_debug_mode อื่น ๆ ที่รองรับ
อาร์กิวเมนต์ circular_buffer_size ควบคุมจำนวนเหตุการณ์เทนเซอร์ที่บันทึกลงใน logdir ค่าเริ่มต้นคือ 1000 ซึ่งทำให้เฉพาะเทนเซอร์ 1000 ตัวสุดท้ายก่อนสิ้นสุดโปรแกรม TF2 ที่ใช้เครื่องมือวัดเพื่อบันทึกลงในดิสก์ ลักษณะการทำงานเริ่มต้นนี้ช่วยลดโอเวอร์เฮดของดีบักเกอร์โดยเสียสละความสมบูรณ์ของข้อมูลการดีบัก หากต้องการความสมบูรณ์มากกว่า ในกรณีนี้ เราสามารถปิดใช้งานบัฟเฟอร์แบบวงกลมได้โดยการตั้งค่าอาร์กิวเมนต์เป็นค่าลบ (เช่น -1 ที่นี่)
ตัวอย่าง debug_mnist_v2 เรียกใช้ enable_dump_debug_info() โดยส่งแฟล็กบรรทัดคำสั่งไป ในการรันโปรแกรม TF2 ที่มีปัญหาอีกครั้งโดยเปิดใช้งานเครื่องมือการดีบักนี้ ให้ทำดังนี้
python -m tensorflow.python.debug.examples.v2.debug_mnist_v2 \
--dump_dir /tmp/tfdbg2_logdir --dump_tensor_debug_mode FULL_HEALTH
การเริ่ม Debugger V2 GUI ใน TensorBoard
การรันโปรแกรมด้วยเครื่องมือดีบักเกอร์จะสร้าง logdir ที่ /tmp/tfdbg2_logdir เราสามารถเริ่ม TensorBoard และชี้ไปที่ logdir ด้วย:
tensorboard --logdir /tmp/tfdbg2_logdir
ในเว็บเบราว์เซอร์ ให้ไปที่หน้าของ TensorBoard ที่ http://localhost:6006 ปลั๊กอิน "ดีบักเกอร์ V2" จะไม่ทำงานโดยค่าเริ่มต้น ดังนั้นให้เลือกจากเมนู "ปลั๊กอินที่ไม่ใช้งาน" ที่ด้านบนขวา เมื่อเลือกแล้วควรมีลักษณะดังนี้:

ใช้ Debugger V2 GUI เพื่อค้นหาสาเหตุของ NaNs
Debugger V2 GUI ใน TensorBoard แบ่งออกเป็นหกส่วน:
- การ แจ้งเตือน : ส่วนซ้ายบนนี้ประกอบด้วยรายการของเหตุการณ์ "การแจ้งเตือน" ที่ตรวจพบโดยโปรแกรมแก้ไขข้อบกพร่องในข้อมูลการดีบักจากโปรแกรม TensorFlow ที่ติดตั้งอุปกรณ์ การแจ้งเตือนแต่ละครั้งบ่งบอกถึงความผิดปกติบางอย่างที่ควรให้ความสนใจ ในกรณีของเรา ส่วนนี้เน้นเหตุการณ์ 499 NaN/∞ ที่มีสีชมพู-แดงเด่นชัด สิ่งนี้ยืนยันความสงสัยของเราว่าแบบจำลองล้มเหลวในการเรียนรู้เนื่องจากการมีอยู่ของ NaN และ/หรืออนันต์ในค่าเทนเซอร์ภายใน เราจะเจาะลึกการแจ้งเตือนเหล่านี้ในไม่ช้า
- Python Execution Timeline : นี่คือครึ่งบนของส่วนบนตรงกลาง นำเสนอประวัติเต็มรูปแบบของการดำเนินการอย่างกระตือรือร้นของ ops และกราฟ แต่ละช่องของไทม์ไลน์จะถูกทำเครื่องหมายด้วยตัวอักษรเริ่มต้นของชื่อ op หรือกราฟ (เช่น "T" สำหรับ "TensorSliceDataset" op, "m" สำหรับ "model"
tf.function) เราสามารถนำทางไทม์ไลน์นี้ได้โดยใช้ปุ่มนำทางและแถบเลื่อนด้านบนไทม์ไลน์ - การ ดำเนินการกราฟ : ตั้งอยู่ที่มุมบนขวาของ GUI ส่วนนี้จะเป็นศูนย์กลางของงานการดีบักของเรา มันมีประวัติของเมตริกซ์แบบลอยตัวทั้งหมดที่คำนวณภายในกราฟ (เช่น รวบรวมโดย
@tf-functions) - โครงสร้างกราฟ (ครึ่งล่างของส่วนตรงกลางบน) ซอร์สโค้ด (ส่วนซ้ายล่าง) และ Stack Trace (ส่วนขวาล่าง) จะว่างเปล่าในตอนแรก เนื้อหาของพวกเขาจะถูกเติมเมื่อเราโต้ตอบกับ GUI ทั้งสามส่วนนี้จะมีบทบาทสำคัญในงานการดีบักของเรา
เมื่อเรามุ่งไปที่องค์กรของ UI แล้ว ให้ทำตามขั้นตอนต่อไปนี้เพื่อทำความเข้าใจว่าทำไม NaN จึงปรากฏขึ้น ขั้นแรก ให้คลิกการแจ้งเตือน NaN/∞ ในส่วนการแจ้งเตือน สิ่งนี้จะเลื่อนรายการเทนเซอร์กราฟ 600 กราฟโดยอัตโนมัติในส่วนการดำเนินการกราฟและเน้นที่ #88 ซึ่งเป็นเมตริกซ์ชื่อ Log:0 ที่สร้างโดย Log (ลอการิทึมธรรมชาติ) สีชมพู-แดงที่เด่นชัดจะเน้นองค์ประกอบ -∞ ท่ามกลาง 1000 องค์ประกอบของเทนเซอร์ 2D float32 นี่เป็นเมตริกซ์แรกในประวัติรันไทม์ของโปรแกรม TF2 ที่มี NaN หรืออินฟินิตี้: เมตริกซ์ที่คำนวณก่อนจะไม่มี NaN หรือ ∞ (ในความเป็นจริง ส่วนใหญ่) เทนเซอร์ที่คำนวณหลังจากนั้นมี NaNs เราสามารถยืนยันได้โดยเลื่อนขึ้นและลงที่รายการ Graph Execution การสังเกตนี้ให้คำใบ้ที่ชัดเจนว่า Log op เป็นที่มาของความไม่แน่นอนของตัวเลขในโปรแกรม TF2 นี้

เหตุใด Log op นี้จึงคาย -∞ การตอบคำถามนั้นต้องตรวจสอบอินพุตของ op การคลิกที่ชื่อเทนเซอร์ ( Log:0 ) จะแสดงภาพบริเวณใกล้เคียงของ Log op ที่เรียบง่ายแต่ให้ข้อมูลในกราฟ TensorFlow ในส่วนโครงสร้างกราฟ สังเกตทิศทางจากบนลงล่างของการไหลของข้อมูล ตัว op จะแสดงเป็นตัวหนาตรงกลาง เหนือมันทันที เราจะเห็น Placeholder op ให้อินพุตเดียวเท่านั้นใน Log op เทนเซอร์ที่สร้างโดย probs Placeholder ในรายการ Graph Execution อยู่ที่ไหน โดยใช้สีพื้นหลังสีเหลืองเป็นภาพช่วย เราจะเห็นได้ว่า probs:0 เทนเซอร์อยู่เหนือเมตริกซ์ Log:0 สามแถว นั่นคือในแถว 85

การดูรายละเอียดเชิงตัวเลขของเมตริกซ์ probs:0 ในแถวที่ 85 อย่างละเอียดถี่ถ้วนเผยให้เห็นว่าเหตุใด Log:0 ของผู้ใช้จึงสร้าง -∞: ในบรรดาองค์ประกอบ 1,000 องค์ประกอบของ probs:0 องค์ประกอบหนึ่งมีค่าเป็น 0 -∞ คือ ผลลัพธ์ของการคำนวณลอการิทึมธรรมชาติของ 0! หากเราสามารถทำให้แน่ใจว่า Log op สัมผัสกับอินพุตที่เป็นบวกเท่านั้น เราจะสามารถป้องกันไม่ให้ NaN/∞ เกิดขึ้นได้ สิ่งนี้สามารถทำได้โดยการใช้การตัด (เช่น โดยใช้ tf.clip_by_value() ) บนตัวแทนที่ probs เทนเซอร์
เรากำลังเข้าใกล้การแก้ไขข้อผิดพลาด แต่ยังไม่เสร็จสิ้น เพื่อนำการแก้ไขไปใช้ เราจำเป็นต้องทราบที่มาของซอร์สโค้ด Python ที่ Log op และอินพุตตัวยึดตำแหน่ง Debugger V2 ให้การสนับสนุนชั้นหนึ่งสำหรับการติดตาม ops ของกราฟและเหตุการณ์การดำเนินการไปยังแหล่งที่มา เมื่อเราคลิกเมตริกซ์ Log:0 ใน Graph Executions ส่วน Stack Trace จะถูกเติมด้วยสแต็กเทรซดั้งเดิมของการสร้าง Log op การติดตามสแต็กค่อนข้างใหญ่เนื่องจากมีเฟรมจำนวนมากจากโค้ดภายในของ TensorFlow (เช่น gen_math_ops.py และ dumping_callback.py) ซึ่งเราสามารถละเว้นงานการดีบักส่วนใหญ่ได้อย่างปลอดภัย เฟรมเวิร์กที่น่าสนใจคือบรรทัดที่ 216 ของ debug_mnist_v2.py (เช่น ไฟล์ Python ที่เรากำลังพยายามแก้ไข) การคลิก "บรรทัดที่ 216" จะแสดงมุมมองของบรรทัดโค้ดที่เกี่ยวข้องในส่วนซอร์สโค้ด

ในที่สุดสิ่งนี้ก็นำเราไปสู่ซอร์สโค้ดที่สร้าง Log op ที่มีปัญหาจาก probs ที่มีปัญหา นี่คือฟังก์ชันการสูญเสียเอนโทรปีแบบแบ่งหมวดหมู่ที่เรากำหนดเองซึ่งตกแต่งด้วย @tf.function และด้วยเหตุนี้จึงแปลงเป็นกราฟ TensorFlow Placeholder op probs สอดคล้องกับอาร์กิวเมนต์อินพุตแรกในฟังก์ชันการสูญเสีย Log op ถูกสร้างขึ้นด้วยการเรียก API tf.math.log()
การแก้ไขค่าสำหรับจุดบกพร่องนี้จะมีลักษณะดังนี้:
diff = -(labels *
tf.math.log(tf.clip_by_value(probs), 1e-6, 1.))
จะแก้ไขความไม่แน่นอนของตัวเลขในโปรแกรม TF2 นี้ และทำให้ MLP ฝึกได้สำเร็จ อีกวิธีหนึ่งในการแก้ไขความไม่เสถียรของตัวเลขคือการใช้ tf.keras.losses.CategoricalCrossentropy
นี่เป็นการสรุปการเดินทางของเราจากการสังเกตจุดบกพร่องของโมเดล TF2 ไปจนถึงการเปลี่ยนแปลงโค้ดที่แก้ไขจุดบกพร่อง โดยได้รับความช่วยเหลือจากเครื่องมือ Debugger V2 ซึ่งให้ทัศนวิสัยอย่างครบถ้วนเกี่ยวกับประวัติความกระตือรือร้นและการดำเนินการกราฟของโปรแกรม TF2 ที่ติดตั้งเครื่องมือวัด รวมถึงข้อมูลสรุปที่เป็นตัวเลข ของค่าเทนเซอร์และความสัมพันธ์ระหว่าง ops, tensor และซอร์สโค้ดดั้งเดิม
ความเข้ากันได้ของฮาร์ดแวร์ของดีบักเกอร์ V2
Debugger V2 รองรับฮาร์ดแวร์การฝึกอบรมหลักรวมถึง CPU และ GPU รองรับการฝึกอบรม Multi-GPU ด้วย tf.distributed.MirroredStrategy การรองรับ TPU ยังอยู่ในช่วงเริ่มต้นและต้องมีการโทร
tf.config.set_soft_device_placement(True)
ก่อนที่จะเรียก enable_dump_debug_info() อาจมีข้อจำกัดอื่นๆ เกี่ยวกับ TPU เช่นกัน หากคุณประสบปัญหาในการใช้ Debugger V2 โปรดรายงานจุดบกพร่องใน หน้าปัญหา GitHub ของเรา
ความเข้ากันได้ของ API ของดีบักเกอร์ V2
Debugger V2 ถูกใช้งานในระดับที่ค่อนข้างต่ำของซอฟต์แวร์ stack ของ TensorFlow และด้วยเหตุนี้จึงเข้ากันได้กับ tf.keras , tf.data และ API อื่นๆ ที่สร้างขึ้นจากระดับที่ต่ำกว่าของ TensorFlow Debugger V2 ยังเข้ากันได้กับ TF1 แม้ว่า Eager Execution Timeline จะว่างเปล่าสำหรับบันทึกการดีบักที่สร้างโดยโปรแกรม TF1
เคล็ดลับการใช้ API
คำถามที่พบบ่อยเกี่ยวกับการดีบัก API นี้คือตำแหน่งใดในโค้ด TensorFlow ควรแทรกการเรียก enable_dump_debug_info() โดยทั่วไปแล้ว ควรเรียก API ให้เร็วที่สุดในโปรแกรม TF2 ของคุณ โดยเฉพาะหลังจากการนำเข้า Python และก่อนเริ่มสร้างกราฟและดำเนินการ วิธีนี้จะช่วยรับประกันความครอบคลุมของ ops และกราฟทั้งหมดที่ขับเคลื่อนโมเดลของคุณและการฝึก
tensor_debug_modes ที่รองรับในปัจจุบัน ได้แก่ NO_TENSOR , CURT_HEALTH , CONCISE_HEALTH , FULL_HEALTH และ SHAPE โดยจะแตกต่างกันไปตามปริมาณข้อมูลที่ดึงมาจากเมตริกซ์แต่ละตัวและค่าใช้จ่ายด้านประสิทธิภาพไปยังโปรแกรมที่แก้ไขข้อบกพร่อง โปรดดู ส่วน args ของเอกสารของ enable_dump_debug_info()
ค่าโสหุ้ยด้านประสิทธิภาพ
API การดีบักจะแนะนำโอเวอร์เฮดด้านประสิทธิภาพให้กับโปรแกรม TensorFlow ที่ติดตั้งเครื่องมือวัด ค่าใช้จ่ายจะแตกต่างกันไปตาม tensor_debug_mode ประเภทของฮาร์ดแวร์ และลักษณะของโปรแกรม TensorFlow ที่ใช้เครื่องมือ ตามจุดอ้างอิง บน GPU โหมด NO_TENSOR เพิ่มโอเวอร์เฮด 15% ระหว่างการฝึก โมเดล Transformer ภายใต้ขนาดแบตช์ 64 เปอร์เซ็นต์โอเวอร์เฮดสำหรับ tensor_debug_modes อื่นๆ จะสูงกว่า: ประมาณ 50% สำหรับ CURT_HEALTH , CONCISE_HEALTH , FULL_HEALTH และ SHAPE โหมด สำหรับซีพียู โอเวอร์เฮดจะต่ำกว่าเล็กน้อย สำหรับ TPU ค่าใช้จ่ายในปัจจุบันสูงขึ้น
ความสัมพันธ์กับ API การดีบัก TensorFlow อื่นๆ
โปรดทราบว่า TensorFlow มีเครื่องมือและ API อื่นๆ สำหรับการดีบัก คุณสามารถเรียกดู API ดังกล่าวได้ภายใต้ เนมสเปซ tf.debugging.* ที่หน้าเอกสาร API ในบรรดา API เหล่านี้ tf.print() ที่ใช้บ่อยที่สุดคือ เมื่อใดควรใช้ Debugger V2 และเมื่อใดควร tf.print() แทน tf.print() จะสะดวกในกรณีที่
- เรารู้ดีว่าควรพิมพ์เทนเซอร์ตัวไหน
- เราทราบตำแหน่งที่แน่นอนในซอร์สโค้ดที่จะแทรกคำสั่ง
tf.print()เหล่านั้น - จำนวนเทนเซอร์ดังกล่าวไม่มากเกินไป
สำหรับกรณีอื่นๆ (เช่น การตรวจสอบค่าเทนเซอร์หลายๆ ค่า การตรวจสอบค่าเทนเซอร์ที่สร้างโดยโค้ดภายในของ TensorFlow และการค้นหาที่มาของความไม่เสถียรของตัวเลขดังที่เราได้แสดงไว้ข้างต้น) Debugger V2 ให้วิธีการดีบักที่เร็วขึ้น นอกจากนี้ Debugger V2 ยังให้แนวทางที่เป็นหนึ่งเดียวในการตรวจสอบการกระตือรือร้นและกราฟเทนเซอร์ นอกจากนี้ยังให้ข้อมูลเกี่ยวกับโครงสร้างกราฟและตำแหน่งของโค้ด ซึ่งเกินความสามารถของ tf.print()
API อื่นที่สามารถใช้ในการดีบักปัญหาที่เกี่ยวข้องกับ ∞ และ NaN ได้คือ tf.debugging.enable_check_numerics() ต่างจาก enable_dump_debug_info() enable_check_numerics() จะไม่บันทึกข้อมูลการดีบักบนดิสก์ แต่จะตรวจสอบ ∞ และ NaN ระหว่างรันไทม์ TensorFlow และเกิดข้อผิดพลาดกับตำแหน่งโค้ดต้นทางทันทีที่ op สร้างค่าตัวเลขที่ไม่ดีดังกล่าว มันมีค่าใช้จ่ายด้านประสิทธิภาพที่ต่ำกว่าเมื่อเปรียบเทียบกับ enable_dump_debug_info() แต่ไม่สามารถติดตามประวัติการดำเนินการของโปรแกรมได้อย่างสมบูรณ์ และไม่มีส่วนต่อประสานกับผู้ใช้แบบกราฟิก เช่น Debugger V2
เหตุการณ์ภัยพิบัติที่เกี่ยวข้องกับ NaN ในบางครั้งอาจเกิดขึ้นระหว่างโปรแกรม TensorFlow ซึ่งทำให้กระบวนการฝึกอบรมแบบจำลองหมดอำนาจ สาเหตุของเหตุการณ์ดังกล่าวมักไม่ชัดเจน โดยเฉพาะอย่างยิ่งสำหรับแบบจำลองที่มีขนาดและความซับซ้อนที่ไม่ซับซ้อน เพื่อให้ง่ายต่อการแก้ไขจุดบกพร่องของโมเดลประเภทนี้ TensorBoard 2.3+ (ร่วมกับ TensorFlow 2.3+) จึงมีแดชบอร์ดพิเศษที่เรียกว่า Debugger V2 ที่นี่ เราสาธิตวิธีใช้เครื่องมือนี้โดยทำงานผ่านจุดบกพร่องจริงที่เกี่ยวข้องกับ NaN ในโครงข่ายประสาทเทียมที่เขียนด้วย TensorFlow
เทคนิคที่แสดงในบทช่วยสอนนี้ใช้ได้กับกิจกรรมการดีบักประเภทอื่น เช่น การตรวจสอบรูปร่างเทนเซอร์รันไทม์ในโปรแกรมที่ซับซ้อน บทช่วยสอนนี้เน้นที่ NaN เนื่องจากความถี่ในการเกิดขึ้นค่อนข้างสูง
การสังเกตจุดบกพร่อง
ซอร์สโค้ดของโปรแกรม TF2 ที่เราจะทำการดีบั๊กมี อยู่ใน GitHub โปรแกรมตัวอย่างยังถูกรวมไว้ในแพ็คเกจ tensorflow pip (เวอร์ชัน 2.3+) และสามารถเรียกใช้ได้โดย:
python -m tensorflow.python.debug.examples.v2.debug_mnist_v2
โปรแกรม TF2 นี้สร้างการรับรู้หลายชั้น (MLP) และฝึกให้จดจำภาพ MNIST ตัวอย่างนี้จงใจใช้ API ระดับต่ำของ TF2 เพื่อกำหนดโครงสร้างเลเยอร์ที่กำหนดเอง ฟังก์ชันการสูญเสีย และลูปการฝึก เนื่องจากโอกาสที่ข้อบกพร่องของ NaN จะสูงขึ้นเมื่อเราใช้ API ที่ยืดหยุ่นกว่าแต่มีโอกาสเกิดข้อผิดพลาดมากกว่าเมื่อเราใช้ API ที่ง่ายกว่านี้ -to-use แต่ API ระดับสูงที่มีความยืดหยุ่นน้อยกว่าเล็กน้อย เช่น tf.keras
โปรแกรมพิมพ์ความถูกต้องของการทดสอบหลังจากแต่ละขั้นตอนการฝึกอบรม เราจะเห็นในคอนโซลว่าความแม่นยำในการทดสอบติดอยู่ที่ระดับโอกาสใกล้ (~0.1) หลังจากขั้นตอนแรก นี่ไม่ใช่อย่างที่คาดหวังไว้สำหรับการฝึกโมเดล: เราคาดว่าความแม่นยำจะค่อยๆ เข้าใกล้ 1.0 (100%) เมื่อขั้นตอนเพิ่มขึ้น
Accuracy at step 0: 0.216
Accuracy at step 1: 0.098
Accuracy at step 2: 0.098
Accuracy at step 3: 0.098
...
การเดาอย่างมีการศึกษาคือปัญหานี้เกิดจากความไม่เสถียรของตัวเลข เช่น NaN หรืออนันต์ อย่างไรก็ตาม เราจะยืนยันได้อย่างไรว่าเป็นกรณีนี้จริงๆ และเราจะค้นหาการดำเนินการ TensorFlow (op) ที่รับผิดชอบในการสร้างความไม่เสถียรของตัวเลขได้อย่างไร เพื่อตอบคำถามเหล่านี้ ให้ใช้เครื่องมือโปรแกรมบั๊กกี้ด้วย Debugger V2
การวัดโค้ด TensorFlow ด้วย Debugger V2
tf.debugging.experimental.enable_dump_debug_info() เป็นจุดเริ่มต้น API ของ Debugger V2 เป็นเครื่องมือในโปรแกรม TF2 ด้วยรหัสบรรทัดเดียว ตัวอย่างเช่น การเพิ่มบรรทัดต่อไปนี้ใกล้กับจุดเริ่มต้นของโปรแกรมจะทำให้ข้อมูลการดีบักถูกเขียนไปยังไดเร็กทอรีบันทึก (logdir) ที่ /tmp/tfdbg2_logdir ข้อมูลการดีบักครอบคลุมแง่มุมต่างๆ ของรันไทม์ TensorFlow ใน TF2 จะรวมประวัติทั้งหมดของการดำเนินการอย่างกระตือรือร้น การสร้างกราฟที่ดำเนินการโดย @tf.function การดำเนินการของกราฟ ค่าเทนเซอร์ที่สร้างโดยเหตุการณ์การดำเนินการ ตลอดจนตำแหน่งโค้ด (การติดตามสแต็ก Python) ของเหตุการณ์เหล่านั้น . ความสมบูรณ์ของข้อมูลการดีบักทำให้ผู้ใช้สามารถจำกัดจุดบกพร่องที่ไม่ชัดเจนได้
tf.debugging.experimental.enable_dump_debug_info(
"/tmp/tfdbg2_logdir",
tensor_debug_mode="FULL_HEALTH",
circular_buffer_size=-1)
อาร์กิวเมนต์ tensor_debug_mode ควบคุมข้อมูลที่ Debugger V2 แยกจากเมตริกซ์กระตือรือร้นหรือในกราฟ “FULL_HEALTH” เป็นโหมดที่รวบรวมข้อมูลต่อไปนี้เกี่ยวกับเทนเซอร์ประเภททศนิยมแต่ละตัว (เช่น float32 ที่มองเห็นได้ทั่วไปและ bfloat16 dtype ทั่วไปน้อยกว่า):
- DType
- อันดับ
- จำนวนองค์ประกอบทั้งหมด
- การแจกแจงองค์ประกอบประเภทลอยตัวเป็นหมวดหมู่ต่อไปนี้: ค่าจำกัดเชิงลบ (
-), ศูนย์ (0), ค่าจำกัดบวก (+) ค่าอนันต์เชิงลบ (-∞) ค่าอินฟินิตี้บวก (+∞) และNaN
โหมด “FULL_HEALTH” เหมาะสำหรับการดีบักจุดบกพร่องที่เกี่ยวข้องกับ NaN และอินฟินิตี้ ดูด้านล่างสำหรับ tensor_debug_mode อื่น ๆ ที่รองรับ
อาร์กิวเมนต์ circular_buffer_size ควบคุมจำนวนเหตุการณ์เทนเซอร์ที่บันทึกลงใน logdir ค่าเริ่มต้นคือ 1000 ซึ่งทำให้เฉพาะเทนเซอร์ 1000 ตัวสุดท้ายก่อนสิ้นสุดโปรแกรม TF2 ที่ใช้เครื่องมือวัดเพื่อบันทึกลงในดิสก์ ลักษณะการทำงานเริ่มต้นนี้ช่วยลดโอเวอร์เฮดของดีบักเกอร์โดยเสียสละความสมบูรณ์ของข้อมูลการดีบัก หากต้องการความสมบูรณ์มากกว่า ในกรณีนี้ เราสามารถปิดใช้งานบัฟเฟอร์แบบวงกลมได้โดยการตั้งค่าอาร์กิวเมนต์เป็นค่าลบ (เช่น -1 ที่นี่)
ตัวอย่าง debug_mnist_v2 เรียกใช้ enable_dump_debug_info() โดยส่งแฟล็กบรรทัดคำสั่งไป ในการรันโปรแกรม TF2 ที่มีปัญหาอีกครั้งโดยเปิดใช้งานเครื่องมือการดีบักนี้ ให้ทำดังนี้
python -m tensorflow.python.debug.examples.v2.debug_mnist_v2 \
--dump_dir /tmp/tfdbg2_logdir --dump_tensor_debug_mode FULL_HEALTH
การเริ่ม Debugger V2 GUI ใน TensorBoard
การรันโปรแกรมด้วยเครื่องมือดีบักเกอร์จะสร้าง logdir ที่ /tmp/tfdbg2_logdir เราสามารถเริ่ม TensorBoard และชี้ไปที่ logdir ด้วย:
tensorboard --logdir /tmp/tfdbg2_logdir
ในเว็บเบราว์เซอร์ ให้ไปที่หน้าของ TensorBoard ที่ http://localhost:6006 ปลั๊กอิน "ดีบักเกอร์ V2" จะไม่ทำงานโดยค่าเริ่มต้น ดังนั้นให้เลือกจากเมนู "ปลั๊กอินที่ไม่ใช้งาน" ที่ด้านบนขวา เมื่อเลือกแล้วควรมีลักษณะดังนี้:

ใช้ Debugger V2 GUI เพื่อค้นหาสาเหตุของ NaNs
Debugger V2 GUI ใน TensorBoard แบ่งออกเป็นหกส่วน:
- การ แจ้งเตือน : ส่วนซ้ายบนนี้ประกอบด้วยรายการของเหตุการณ์ "การแจ้งเตือน" ที่ตรวจพบโดยโปรแกรมแก้ไขข้อบกพร่องในข้อมูลการดีบักจากโปรแกรม TensorFlow ที่ติดตั้งอุปกรณ์ การแจ้งเตือนแต่ละครั้งบ่งบอกถึงความผิดปกติบางอย่างที่ควรให้ความสนใจ ในกรณีของเรา ส่วนนี้เน้นเหตุการณ์ 499 NaN/∞ ที่มีสีชมพู-แดงเด่นชัด สิ่งนี้ยืนยันความสงสัยของเราว่าแบบจำลองล้มเหลวในการเรียนรู้เนื่องจากการมีอยู่ของ NaN และ/หรืออนันต์ในค่าเทนเซอร์ภายใน เราจะเจาะลึกการแจ้งเตือนเหล่านี้ในไม่ช้า
- Python Execution Timeline : นี่คือครึ่งบนของส่วนบนตรงกลาง นำเสนอประวัติเต็มรูปแบบของการดำเนินการอย่างกระตือรือร้นของ ops และกราฟ แต่ละช่องของไทม์ไลน์จะถูกทำเครื่องหมายด้วยตัวอักษรเริ่มต้นของชื่อ op หรือกราฟ (เช่น "T" สำหรับ "TensorSliceDataset" op, "m" สำหรับ "model"
tf.function) เราสามารถนำทางไทม์ไลน์นี้ได้โดยใช้ปุ่มนำทางและแถบเลื่อนด้านบนไทม์ไลน์ - การ ดำเนินการกราฟ : ตั้งอยู่ที่มุมบนขวาของ GUI ส่วนนี้จะเป็นศูนย์กลางของงานการดีบักของเรา มันมีประวัติของเมตริกซ์แบบลอยตัวทั้งหมดที่คำนวณภายในกราฟ (เช่น รวบรวมโดย
@tf-functions) - โครงสร้างกราฟ (ครึ่งล่างของส่วนตรงกลางบน) ซอร์สโค้ด (ส่วนซ้ายล่าง) และ Stack Trace (ส่วนขวาล่าง) จะว่างเปล่าในตอนแรก เนื้อหาของพวกเขาจะถูกเติมเมื่อเราโต้ตอบกับ GUI ทั้งสามส่วนนี้จะมีบทบาทสำคัญในงานการดีบักของเรา
เมื่อเรามุ่งไปที่องค์กรของ UI แล้ว ให้ทำตามขั้นตอนต่อไปนี้เพื่อทำความเข้าใจว่าทำไม NaN จึงปรากฏขึ้น ขั้นแรก ให้คลิกการแจ้งเตือน NaN/∞ ในส่วนการแจ้งเตือน สิ่งนี้จะเลื่อนรายการเทนเซอร์กราฟ 600 กราฟโดยอัตโนมัติในส่วนการดำเนินการกราฟและเน้นที่ #88 ซึ่งเป็นเมตริกซ์ชื่อ Log:0 ที่สร้างโดย Log (ลอการิทึมธรรมชาติ) สีชมพู-แดงที่เด่นชัดจะเน้นองค์ประกอบ -∞ ท่ามกลาง 1000 องค์ประกอบของเทนเซอร์ 2D float32 นี่เป็นเมตริกซ์แรกในประวัติรันไทม์ของโปรแกรม TF2 ที่มี NaN หรืออินฟินิตี้: เมตริกซ์ที่คำนวณก่อนจะไม่มี NaN หรือ ∞ (ในความเป็นจริง ส่วนใหญ่) เทนเซอร์ที่คำนวณหลังจากนั้นมี NaNs เราสามารถยืนยันได้โดยเลื่อนขึ้นและลงที่รายการ Graph Execution การสังเกตนี้ให้คำใบ้ที่ชัดเจนว่า Log op เป็นที่มาของความไม่แน่นอนของตัวเลขในโปรแกรม TF2 นี้

เหตุใด Log op นี้จึงคาย -∞ การตอบคำถามนั้นต้องตรวจสอบอินพุตของ op การคลิกที่ชื่อเทนเซอร์ ( Log:0 ) จะแสดงภาพบริเวณใกล้เคียงของ Log op ที่เรียบง่ายแต่ให้ข้อมูลในกราฟ TensorFlow ในส่วนโครงสร้างกราฟ สังเกตทิศทางจากบนลงล่างของการไหลของข้อมูล ตัว op จะแสดงเป็นตัวหนาตรงกลาง เหนือมันทันที เราจะเห็น Placeholder op ให้อินพุตเดียวเท่านั้นใน Log op เทนเซอร์ที่สร้างโดย probs Placeholder ในรายการ Graph Execution อยู่ที่ไหน โดยใช้สีพื้นหลังสีเหลืองเป็นภาพช่วย เราจะเห็นได้ว่า probs:0 เทนเซอร์อยู่เหนือเมตริกซ์ Log:0 สามแถว นั่นคือในแถว 85

การดูรายละเอียดเชิงตัวเลขของเมตริกซ์ probs:0 ในแถวที่ 85 อย่างละเอียดถี่ถ้วนเผยให้เห็นว่าเหตุใด Log:0 ของผู้ใช้จึงสร้าง -∞: ในบรรดาองค์ประกอบ 1,000 องค์ประกอบของ probs:0 องค์ประกอบหนึ่งมีค่าเป็น 0 -∞ คือ ผลลัพธ์ของการคำนวณลอการิทึมธรรมชาติของ 0! หากเราสามารถทำให้แน่ใจว่า Log op สัมผัสกับอินพุตที่เป็นบวกเท่านั้น เราจะสามารถป้องกันไม่ให้ NaN/∞ เกิดขึ้นได้ สิ่งนี้สามารถทำได้โดยการใช้การตัด (เช่น โดยใช้ tf.clip_by_value() ) บนตัวแทนที่ probs เทนเซอร์
เรากำลังเข้าใกล้การแก้ไขข้อผิดพลาด แต่ยังไม่เสร็จสิ้น เพื่อนำการแก้ไขไปใช้ เราจำเป็นต้องทราบที่มาของซอร์สโค้ด Python ที่ Log op และอินพุตตัวยึดตำแหน่ง Debugger V2 ให้การสนับสนุนชั้นหนึ่งสำหรับการติดตาม ops ของกราฟและเหตุการณ์การดำเนินการไปยังแหล่งที่มา เมื่อเราคลิกเมตริกซ์ Log:0 ใน Graph Executions ส่วน Stack Trace จะถูกเติมด้วยสแต็กเทรซดั้งเดิมของการสร้าง Log op การติดตามสแต็กค่อนข้างใหญ่เนื่องจากมีเฟรมจำนวนมากจากโค้ดภายในของ TensorFlow (เช่น gen_math_ops.py และ dumping_callback.py) ซึ่งเราสามารถละเว้นงานการดีบักส่วนใหญ่ได้อย่างปลอดภัย เฟรมเวิร์กที่น่าสนใจคือบรรทัดที่ 216 ของ debug_mnist_v2.py (เช่น ไฟล์ Python ที่เรากำลังพยายามแก้ไข) การคลิก "บรรทัดที่ 216" จะแสดงมุมมองของบรรทัดโค้ดที่เกี่ยวข้องในส่วนซอร์สโค้ด

ในที่สุดสิ่งนี้ก็นำเราไปสู่ซอร์สโค้ดที่สร้าง Log op ที่มีปัญหาจาก probs ที่มีปัญหา นี่คือฟังก์ชันการสูญเสียเอนโทรปีแบบแบ่งหมวดหมู่ที่เรากำหนดเองซึ่งตกแต่งด้วย @tf.function และด้วยเหตุนี้จึงแปลงเป็นกราฟ TensorFlow Placeholder op probs สอดคล้องกับอาร์กิวเมนต์อินพุตแรกในฟังก์ชันการสูญเสีย Log op ถูกสร้างขึ้นด้วยการเรียก API tf.math.log()
การแก้ไขค่าสำหรับจุดบกพร่องนี้จะมีลักษณะดังนี้:
diff = -(labels *
tf.math.log(tf.clip_by_value(probs), 1e-6, 1.))
จะแก้ไขความไม่แน่นอนของตัวเลขในโปรแกรม TF2 นี้ และทำให้ MLP ฝึกได้สำเร็จ อีกวิธีหนึ่งในการแก้ไขความไม่เสถียรของตัวเลขคือการใช้ tf.keras.losses.CategoricalCrossentropy
นี่เป็นการสรุปการเดินทางของเราจากการสังเกตจุดบกพร่องของโมเดล TF2 ไปจนถึงการเปลี่ยนแปลงโค้ดที่แก้ไขจุดบกพร่อง โดยได้รับความช่วยเหลือจากเครื่องมือ Debugger V2 ซึ่งให้ทัศนวิสัยอย่างครบถ้วนเกี่ยวกับประวัติความกระตือรือร้นและการดำเนินการกราฟของโปรแกรม TF2 ที่ติดตั้งเครื่องมือวัด รวมถึงข้อมูลสรุปที่เป็นตัวเลข ของค่าเทนเซอร์และความสัมพันธ์ระหว่าง ops, tensor และซอร์สโค้ดดั้งเดิม
ความเข้ากันได้ของฮาร์ดแวร์ของดีบักเกอร์ V2
Debugger V2 รองรับฮาร์ดแวร์การฝึกอบรมหลักรวมถึง CPU และ GPU รองรับการฝึกอบรม Multi-GPU ด้วย tf.distributed.MirroredStrategy การรองรับ TPU ยังอยู่ในช่วงเริ่มต้นและต้องมีการโทร
tf.config.set_soft_device_placement(True)
ก่อนที่จะเรียก enable_dump_debug_info() อาจมีข้อจำกัดอื่นๆ เกี่ยวกับ TPU เช่นกัน หากคุณประสบปัญหาในการใช้ Debugger V2 โปรดรายงานจุดบกพร่องใน หน้าปัญหา GitHub ของเรา
ความเข้ากันได้ของ API ของดีบักเกอร์ V2
Debugger V2 ถูกใช้งานในระดับที่ค่อนข้างต่ำของซอฟต์แวร์ stack ของ TensorFlow และด้วยเหตุนี้จึงเข้ากันได้กับ tf.keras , tf.data และ API อื่นๆ ที่สร้างขึ้นจากระดับที่ต่ำกว่าของ TensorFlow Debugger V2 ยังเข้ากันได้กับ TF1 แม้ว่า Eager Execution Timeline จะว่างเปล่าสำหรับบันทึกการดีบักที่สร้างโดยโปรแกรม TF1
เคล็ดลับการใช้ API
คำถามที่พบบ่อยเกี่ยวกับการดีบัก API นี้คือตำแหน่งใดในโค้ด TensorFlow ควรแทรกการเรียก enable_dump_debug_info() โดยทั่วไปแล้ว ควรเรียก API ให้เร็วที่สุดในโปรแกรม TF2 ของคุณ โดยเฉพาะหลังจากการนำเข้า Python และก่อนเริ่มสร้างกราฟและดำเนินการ วิธีนี้จะช่วยรับประกันความครอบคลุมของ ops และกราฟทั้งหมดที่ขับเคลื่อนโมเดลของคุณและการฝึก
tensor_debug_modes ที่รองรับในปัจจุบัน ได้แก่ NO_TENSOR , CURT_HEALTH , CONCISE_HEALTH , FULL_HEALTH และ SHAPE โดยจะแตกต่างกันไปตามปริมาณข้อมูลที่ดึงมาจากเมตริกซ์แต่ละตัวและค่าใช้จ่ายด้านประสิทธิภาพไปยังโปรแกรมที่แก้ไขข้อบกพร่อง โปรดดู ส่วน args ของเอกสารของ enable_dump_debug_info()
ค่าโสหุ้ยด้านประสิทธิภาพ
API การดีบักจะแนะนำโอเวอร์เฮดด้านประสิทธิภาพให้กับโปรแกรม TensorFlow ที่ติดตั้งเครื่องมือวัด ค่าใช้จ่ายจะแตกต่างกันไปตาม tensor_debug_mode ประเภทของฮาร์ดแวร์ และลักษณะของโปรแกรม TensorFlow ที่ใช้เครื่องมือ ตามจุดอ้างอิง บน GPU โหมด NO_TENSOR เพิ่มโอเวอร์เฮด 15% ระหว่างการฝึก โมเดล Transformer ภายใต้ขนาดแบตช์ 64 เปอร์เซ็นต์โอเวอร์เฮดสำหรับ tensor_debug_modes อื่นๆ จะสูงกว่า: ประมาณ 50% สำหรับ CURT_HEALTH , CONCISE_HEALTH , FULL_HEALTH และ SHAPE โหมด สำหรับซีพียู โอเวอร์เฮดจะต่ำกว่าเล็กน้อย สำหรับ TPU ค่าใช้จ่ายในปัจจุบันสูงขึ้น
ความสัมพันธ์กับ API การดีบัก TensorFlow อื่นๆ
โปรดทราบว่า TensorFlow มีเครื่องมือและ API อื่นๆ สำหรับการดีบัก คุณสามารถเรียกดู API ดังกล่าวได้ภายใต้ เนมสเปซ tf.debugging.* ที่หน้าเอกสาร API ในบรรดา API เหล่านี้ tf.print() ที่ใช้บ่อยที่สุดคือ เมื่อใดควรใช้ Debugger V2 และเมื่อใดควร tf.print() แทน tf.print() จะสะดวกในกรณีที่
- เรารู้ดีว่าควรพิมพ์เทนเซอร์ตัวไหน
- เราทราบตำแหน่งที่แน่นอนในซอร์สโค้ดที่จะแทรกคำสั่ง
tf.print()เหล่านั้น - จำนวนเทนเซอร์ดังกล่าวไม่มากเกินไป
สำหรับกรณีอื่นๆ (เช่น การตรวจสอบค่าเทนเซอร์หลายๆ ค่า การตรวจสอบค่าเทนเซอร์ที่สร้างโดยโค้ดภายในของ TensorFlow และการค้นหาที่มาของความไม่เสถียรของตัวเลขดังที่เราได้แสดงไว้ข้างต้น) Debugger V2 ให้วิธีการดีบักที่เร็วขึ้น นอกจากนี้ Debugger V2 ยังให้แนวทางที่เป็นหนึ่งเดียวในการตรวจสอบการกระตือรือร้นและกราฟเทนเซอร์ นอกจากนี้ยังให้ข้อมูลเกี่ยวกับโครงสร้างกราฟและตำแหน่งของโค้ด ซึ่งเกินความสามารถของ tf.print()
API อื่นที่สามารถใช้ในการดีบักปัญหาที่เกี่ยวข้องกับ ∞ และ NaN ได้คือ tf.debugging.enable_check_numerics() ต่างจาก enable_dump_debug_info() enable_check_numerics() จะไม่บันทึกข้อมูลการดีบักบนดิสก์ แต่จะตรวจสอบ ∞ และ NaN ระหว่างรันไทม์ TensorFlow และเกิดข้อผิดพลาดกับตำแหน่งโค้ดต้นทางทันทีที่ op สร้างค่าตัวเลขที่ไม่ดีดังกล่าว มันมีค่าใช้จ่ายด้านประสิทธิภาพที่ต่ำกว่าเมื่อเปรียบเทียบกับ enable_dump_debug_info() แต่ไม่สามารถติดตามประวัติการดำเนินการของโปรแกรมได้อย่างสมบูรณ์ และไม่มีส่วนต่อประสานกับผู้ใช้แบบกราฟิก เช่น Debugger V2