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

การดีบักปัญหาเชิงตัวเลขในโปรแกรม TensorFlow โดยใช้ TensorBoard Debugger V2

เหตุการณ์ภัยพิบัติที่เกี่ยวข้องกับ NaN บางครั้งสามารถเกิดขึ้นได้ในระหว่างโปรแกรม TensorFlow ซึ่งทำให้กระบวนการการฝึกอบรมแบบจำลองล้มเหลว สาเหตุที่แท้จริงของเหตุการณ์ดังกล่าวมักจะไม่ชัดเจนโดยเฉพาะอย่างยิ่งสำหรับแบบจำลองที่มีขนาดและความซับซ้อนน้อยมาก เพื่อให้ง่ายต่อการตรวจแก้จุดบกพร่องของตัวแบบประเภทนี้ TensorBoard 2.3+ (ร่วมกับ TensorFlow 2.3+) ให้แดชบอร์ดพิเศษที่เรียกว่า Debugger V2 ที่นี่เราแสดงให้เห็นถึงวิธีการใช้เครื่องมือนี้โดยการทำงานผ่านข้อผิดพลาดจริงที่เกี่ยวข้องกับ NaNs ในเครือข่ายประสาทที่เขียนใน TensorFlow

เทคนิคที่แสดงในบทช่วยสอนนี้ใช้กับกิจกรรมการดีบักประเภทอื่นเช่นการตรวจสอบรูปร่างเทนเซอร์ของเทนเซอร์ในโปรแกรมที่ซับซ้อน บทช่วยสอนนี้มุ่งเน้นไปที่ NaNs เนื่องจากความถี่ของการเกิดค่อนข้างสูง

สังเกตข้อผิดพลาด

ซอร์สโค้ดของโปรแกรม TF2 ที่เราจะทำการดีบักนั้น มีอยู่ใน GitHub ตัวอย่างของโปรแกรมนั้นได้รับการบรรจุลงในแพ็คเกจ pip ของทอร์โฟลว (เวอร์ชั่น 2.3+) และสามารถเรียกใช้โดย:

 python -m tensorflow.python.debug.examples.v2.debug_mnist_v2
 

โปรแกรม TF2 นี้สร้างการรับรู้แบบหลายชั้น (MLP) และฝึกให้จดจำภาพ MNIST ตัวอย่างนี้ตั้งใจใช้ API ระดับต่ำของ TF2 เพื่อกำหนดโครงสร้างเลเยอร์ที่กำหนดเองฟังก์ชั่นการสูญเสียและการฝึกซ้อมเนื่องจากความน่าจะเป็นของข้อบกพร่อง NaN จะสูงกว่าเมื่อเราใช้ API ที่ยืดหยุ่นมากขึ้น แต่ผิดพลาดมากกว่าเมื่อเราใช้ - สำหรับการใช้งาน แต่มีความยืดหยุ่นน้อยกว่า 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) ที่รับผิดชอบในการสร้างความไม่แน่นอนเชิงตัวเลขได้อย่างไร เพื่อตอบคำถามเหล่านี้เรามาจัดโปรแกรม buggy กับ 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(
    logdir="/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 มันเริ่มต้นที่ 1,000 ซึ่งทำให้เฉพาะ 1000 เมตริกซ์ล่าสุดก่อนที่จะสิ้นสุดของโปรแกรม TF2 instrumented จะถูกบันทึกลงดิสก์ พฤติกรรมเริ่มต้นนี้ช่วยลดค่าใช้จ่ายในการดีบักเกอร์โดยลดความสมบูรณ์ของข้อมูลดีบัก หากต้องการความสมบูรณ์เช่นในกรณีนี้เราสามารถปิดการใช้งานบัฟเฟอร์แบบวงกลมโดยตั้งค่าอาร์กิวเมนต์เป็นค่าลบ (เช่น -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 ควรเปิดใช้งานปลั๊กอิน“ Debugger V2” ตามค่าเริ่มต้นโดยแสดงหน้าเว็บที่มีลักษณะดังนี้:

หน้าจอมุมมองแบบเต็ม Debugger V2

ใช้ Debugger V2 GUI เพื่อค้นหาสาเหตุของ NaN

Debugger V2 GUI ใน TensorBoard แบ่งออกเป็นหกส่วน:

  • การแจ้งเตือน : ส่วนด้านบนซ้ายนี้มีรายการเหตุการณ์“ เตือน” ที่ตรวจพบโดยตัวดีบั๊กในข้อมูลการดีบักจากโปรแกรม TensorFlow ที่ใช้เครื่องมือ การแจ้งเตือนแต่ละครั้งจะระบุความผิดปกติบางอย่างที่รับประกันความสนใจ ในกรณีของเราส่วนนี้ไฮไลต์ 499 NaN / ∞เหตุการณ์ที่มีสีแดงชมพูชมพู สิ่งนี้เป็นการยืนยันความสงสัยของเราว่าโมเดลไม่สามารถเรียนรู้ได้เนื่องจากการมี NaNs และ / หรือ infinities ในค่าเทนเซอร์ภายใน เราจะเจาะลึกการแจ้งเตือนเหล่านี้ในไม่ช้า
  • Python Exeline Timeline : นี่คือครึ่งบนของส่วนบนกลาง มันนำเสนอประวัติเต็มรูปแบบของการดำเนินการกระตือรือร้นของกราฟและกราฟ แต่ละกล่องของเส้นเวลาถูกทำเครื่องหมายด้วยตัวอักษรเริ่มต้นของชื่อ op หรือกราฟ (เช่น“ T” สำหรับ“ TensorSliceDataset” op,“ m” สำหรับ“ model” tf.function ) เราสามารถนำทางไทม์ไลน์นี้ได้โดยใช้ปุ่มนำทางและแถบเลื่อนด้านบนไทม์ไลน์
  • การใช้งานกราฟ : ตั้งอยู่ที่มุมบนขวาของ GUI ส่วนนี้จะเป็นศูนย์กลางในการแก้ไขข้อบกพร่องของเรา มันมีประวัติของเทนเซอร์ทศนิยมที่คำนวณในกราฟ (เช่นรวบรวมโดย @tf-function s)
  • โครงสร้างกราฟ (ครึ่งล่างของส่วนบนกลาง), ซอร์สโค้ด (ส่วนล่างซ้าย), และ สแต็กเทรซ (ส่วนล่างขวา) ว่างเปล่าในตอนแรก เนื้อหาของพวกเขาจะได้รับการบรรจุเมื่อเราโต้ตอบกับ GUI สามส่วนเหล่านี้จะมีบทบาทสำคัญในงานการดีบักของเรา

ให้ความสำคัญกับองค์กรของ UI ลองทำตามขั้นตอนต่อไปนี้เพื่อดูว่าทำไม NaNs ถึงปรากฏขึ้น ก่อนอื่นคลิกการแจ้งเตือน NaN / in ในส่วนการแจ้งเตือน โดยอัตโนมัติเลื่อนรายการ 600 tensors กราฟในส่วนของการดำเนินการกราฟและมุ่งเน้นไปที่ # 88 ซึ่งเป็นเมตริกซ์ที่ชื่อ“เข้าสู่ระบบ: 0” สร้างขึ้นโดย Log (ลอการิทึมธรรมชาติ) สหกรณ์ สีแดงชมพูชมพูเน้นองค์ประกอบ-∞ใน 1,000 องค์ประกอบของ 2D float32 tensor นี่คือเมตริกซ์แรกในประวัติรันไทม์ของโปรแกรม TF2 ที่มี NaN หรืออินฟินิตี้ใด ๆ : เมตริกซ์ที่คำนวณก่อนที่มันจะไม่มี NaN หรือ∞; จำนวนมาก (ในความเป็นจริงส่วนใหญ่) ตัวคำนวณที่นับหลังจากนั้นมี NaNs เราสามารถยืนยันสิ่งนี้ได้โดยการเลื่อนรายการกราฟการดำเนินการขึ้นและลง การสังเกตนี้ให้คำแนะนำที่ดีว่า Log op เป็นที่มาของความไม่แน่นอนเชิงตัวเลขในโปรแกรม TF2 นี้

ดีบักเกอร์ V2: การแจ้งเตือน Nan / Infinity และรายการดำเนินการกราฟ

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

ดีบักเกอร์ V2: มุมมองโครงสร้างกราฟและการติดตามไปยังเทนเซอร์

การดูอย่างละเอียดมากขึ้นเกี่ยวกับการแยกย่อยตัวเลขของ“ logits: 0” เทนเซอร์ในแถว 85 แสดงให้เห็นว่าเหตุใด Consumer Log:0 ของมัน Log:0 สร้าง-∞: ในบรรดาองค์ประกอบ 1,000 รายการของ“ logits: 0” หนึ่งองค์ประกอบมีค่า 0 -∞เป็นผลมาจากการคำนวณลอการิทึมธรรมชาติของ 0! หากเราสามารถมั่นใจได้ว่า Log op ได้รับการสัมผัสกับอินพุตที่เป็นบวกเท่านั้นเราจะสามารถป้องกัน NaN / ∞ไม่ให้เกิดขึ้นได้ สิ่งนี้สามารถทำได้โดยการใช้การตัด (เช่นโดยใช้ tf.clip_by_value () ) ในตัวยึดตำแหน่งตัวยึดบันทึก

เราเข้าใกล้การแก้ไขข้อบกพร่องมากขึ้น แต่ยังไม่เสร็จ เพื่อที่จะใช้การแก้ไขเราจำเป็นต้องรู้ว่าในซอร์สโค้ดของ Python นั้นมี op log และตัวยึดตำแหน่งมาจากแหล่งใด Debugger V2 ให้การสนับสนุนชั้นหนึ่งสำหรับการติดตามกราฟ ops และเหตุการณ์การดำเนินการกับแหล่งที่มา เมื่อเราคลิก Log:0 tensor ใน Graph Executions ส่วน Stack Trace จะถูกเติมด้วย stack trace ดั้งเดิมของการสร้าง Log op การติดตามสแต็กมีขนาดค่อนข้างใหญ่เนื่องจากมีเฟรมจำนวนมากจากรหัสภายในของ TensorFlow (เช่น gen_math_ops.py และ dumping_callback.py) ซึ่งเราสามารถเพิกเฉยต่อการแก้ไขข้อบกพร่องส่วนใหญ่ได้อย่างปลอดภัย เฟรมที่น่าสนใจคือ Line 216 ของ debug_mnist_v2.py (เช่นไฟล์ Python ที่เรากำลังพยายามแก้ไขอยู่) การคลิก“ บรรทัด 204” จะแสดงมุมมองของรหัสที่เกี่ยวข้องในส่วนของรหัสที่มา

ดีบักเกอร์ V2: ซอร์สโค้ดและการติดตามสแต็ก

ในที่สุดก็นำเราไปสู่ซอร์สโค้ดที่สร้าง op log ที่เป็นปัญหาจากอินพุตของ logits นี่คือฟังก์ชั่นการสูญเสียข้ามเอนโทรปีแบบกำหนดเองของเราที่ตกแต่งด้วย @tf.function และดังนั้นจึงถูกแปลงเป็นกราฟ TensorFlow ตัวยึดตำแหน่ง "บันทึก" สอดคล้องกับอาร์กิวเมนต์อินพุตแรกกับฟังก์ชันการสูญเสีย Log op สร้างขึ้นด้วยการเรียก tf.math.log () API

การแก้ไขค่าการแก้ไขจุดบกพร่องนี้จะมีลักษณะดังนี้:

   diff = -(labels *
           tf.clip_by_value(tf.math.log(logits), 1e-6, 1.))
 

มันจะแก้ปัญหาความไม่แน่นอนเชิงตัวเลขในโปรแกรม TF2 นี้และทำให้ MLP สามารถฝึกอบรมได้สำเร็จ อีกวิธีที่เป็นไปได้ในการแก้ไขความไม่แน่นอนเชิงตัวเลขคือการใช้ tf.keras.losses.CategoricalCrossentropy

นี่เป็นการสรุปการเดินทางของเราจากการสังเกตข้อผิดพลาดรุ่น TF2 ไปจนถึงการเปลี่ยนแปลงรหัสที่แก้ไขข้อบกพร่องซึ่งได้รับความช่วยเหลือจากเครื่องมือ Debugger V2 ซึ่งให้การมองเห็นที่เต็มไปด้วยประวัติความกระตือรือร้นและกราฟการทำงานของโปรแกรม TF2 รวมถึงสรุปตัวเลข ของค่าเทนเซอร์และความสัมพันธ์ระหว่าง ops, เทนเซอร์และซอร์สโค้ดดั้งเดิม

ความเข้ากันได้ของฮาร์ดแวร์ของ Debugger 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 ที่เข้ากันได้ของ Debugger V2

Debugger V2 มีการนำมาใช้ในระดับที่ค่อนข้างต่ำของสแต็กซอฟต์แวร์ของ TensorFlow และเข้ากันได้กับ tf.keras , tf.data และ API อื่น ๆ ที่สร้างขึ้นจากระดับที่ต่ำกว่าของ TensorFlow ดีบักเกอร์ V2 สามารถใช้งานร่วมกับ TF1 ได้เช่นกันแม้ว่า Eager Execution Timeline จะว่างเปล่าสำหรับ logdirs ดีบักที่สร้างโดยโปรแกรม 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% ในช่วงการฝึกอบรมเป็น รุ่นหม้อแปลง ภายใต้ชุดขนาด 64 ค่าใช้จ่ายร้อยละ tensor_debug_modes อื่น ๆ จะสูงขึ้นประมาณ 50% สำหรับ CURT_HEALTH , CONCISE_HEALTH , FULL_HEALTH และ SHAPE โหมด บน CPUs ค่าใช้จ่ายจะลดลงเล็กน้อย บน TPUs ค่าใช้จ่ายในปัจจุบันสูงขึ้น

เกี่ยวข้องกับ API การดีบัก TensorFlow อื่น ๆ

โปรดทราบว่า TensorFlow มีเครื่องมือและ API อื่น ๆ สำหรับการดีบัก คุณสามารถเรียกดู API ดังกล่าวได้ภายใต้ tf.debugging.* namespace ที่หน้าเอกสาร API ในบรรดา API เหล่านี้ที่ใช้บ่อยที่สุดคือ tf.print() เมื่อใดควรใช้ Debugger V2 และเมื่อใดควรใช้ tf.print() แทน tf.print() สะดวกในกรณีที่

  1. เรารู้แน่ชัดว่าจะพิมพ์แบบไหน
  2. เรารู้ว่าอยู่ตรงไหนในซอร์สโค้ดเพื่อแทรกคำสั่ง tf.print() เหล่านั้น
  3. จำนวนเทนเซอร์ดังกล่าวไม่ใหญ่เกินไป

สำหรับกรณีอื่น ๆ (เช่นการตรวจสอบค่าเทนเซอร์หลายค่าการตรวจสอบค่าเทนเซอร์ที่สร้างขึ้นโดยรหัสภายในของ 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