Halaman ini diterjemahkan oleh Cloud Translation API.
Switch to English

Debugging Masalah Numerik dalam Program TensorFlow Menggunakan TensorBoard Debugger V2

Peristiwa bencana yang melibatkan NaN kadang-kadang dapat terjadi selama program TensorFlow, melumpuhkan proses pelatihan model. Akar penyebab peristiwa semacam itu seringkali tidak jelas, terutama untuk model ukuran dan kompleksitas yang tidak sepele. Untuk mempermudah debug model bug jenis ini, TensorBoard 2.3+ (bersama dengan TensorFlow 2.3+) menyediakan dasbor khusus yang disebut Debugger V2. Di sini kami mendemonstrasikan cara menggunakan alat ini dengan bekerja melalui bug nyata yang melibatkan NaNs dalam jaringan saraf yang ditulis dalam TensorFlow.

Teknik-teknik yang diilustrasikan dalam tutorial ini berlaku untuk jenis lain dari aktivitas debugging seperti memeriksa bentuk tensor runtime dalam program yang kompleks. Tutorial ini berfokus pada NaN karena frekuensi kemunculannya yang relatif tinggi.

Mengamati bug

Kode sumber program TF2 yang akan kami debug tersedia di GitHub . Program contoh juga dikemas ke dalam paket pip tensorflow (versi 2.3+) dan dapat dipanggil oleh:

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

Program TF2 ini menciptakan persepsi multi-layer (MLP) dan melatihnya untuk mengenali gambar MNIST . Contoh ini sengaja menggunakan API tingkat rendah TF2 untuk menentukan konstruksi lapisan kustom, fungsi kehilangan, dan loop pelatihan, karena kemungkinan bug NaN lebih tinggi ketika kita menggunakan API yang lebih fleksibel tetapi lebih rentan kesalahan ini daripada saat kita menggunakan yang lebih mudah -untuk menggunakan tetapi API tingkat tinggi yang sedikit kurang fleksibel seperti tf.keras .

Program mencetak akurasi tes setelah setiap langkah pelatihan. Kita dapat melihat di konsol bahwa keakuratan pengujian macet di tingkat peluang hampir (~ 0,1) setelah langkah pertama. Ini tentu bukan bagaimana model pelatihan diharapkan untuk berperilaku: kami mengharapkan akurasi untuk secara bertahap mendekati 1,0 (100%) ketika langkah meningkat.

 Accuracy at step 0: 0.216
Accuracy at step 1: 0.098
Accuracy at step 2: 0.098
Accuracy at step 3: 0.098
...
 

Dugaan terpelajar adalah bahwa masalah ini disebabkan oleh ketidakstabilan numerik, seperti NaN atau infinity. Namun, bagaimana kita mengkonfirmasi ini benar-benar terjadi dan bagaimana kita menemukan operasi TensorFlow (op) bertanggung jawab untuk menghasilkan ketidakstabilan numerik? Untuk menjawab pertanyaan-pertanyaan ini, mari kita instrumen program kereta dengan Debugger V2.

Menginstruksikan kode TensorFlow dengan Debugger V2

tf.debugging.experimental.enable_dump_debug_info() adalah titik masuk API dari Debugger V2. Ini instrumen program TF2 dengan satu baris kode. Misalnya, menambahkan baris berikut di dekat awal program akan menyebabkan informasi debug ditulis ke direktori log (logdir) di / tmp / tfdbg2_logdir. Informasi debug mencakup berbagai aspek runtime TensorFlow. Dalam TF2, itu termasuk sejarah lengkap dari eksekusi yang cepat, pembuatan grafik yang dilakukan oleh @ tf.fungsi , eksekusi grafik, nilai tensor yang dihasilkan oleh peristiwa eksekusi, serta lokasi kode (jejak tumpukan Python) dari peristiwa tersebut . Kekayaan informasi debug memungkinkan pengguna untuk mempersempit bug yang tidak jelas.

 tf.debugging.experimental.enable_dump_debug_info(
    logdir="/tmp/tfdbg2_logdir",
    tensor_debug_mode="FULL_HEALTH",
    circular_buffer_size=-1)
 

Argumen tensor_debug_mode mengontrol informasi apa yang tensor_debug_mode Debugger V2 dari setiap tensor yang diinginkan atau dalam grafik. “FULL_HEALTH” adalah modus yang menangkap berikut informasi tentang setiap floating-jenis tensor (misalnya, float32 sering dilihat dan kurang umum bfloat16 dtype):

  • DType
  • Pangkat
  • Total jumlah elemen
  • Rincian elemen tipe mengambang ke dalam kategori berikut: finite negatif ( - ), nol ( 0 ), finite positif ( + ), infinity negatif ( -∞ ), infinity positif ( +∞ ), dan NaN .

Mode "FULL_HEALTH" cocok untuk debugging bug yang melibatkan NaN dan infinity. Lihat di bawah untuk tensor_debug_mode s lainnya yang didukung.

Argumen circular_buffer_size mengontrol berapa banyak peristiwa tensor yang disimpan ke logdir. Nilai standarnya adalah 1000, yang hanya menyebabkan 1000 tensor terakhir sebelum akhir program TF2 yang diinstrumentasi disimpan ke disk. Perilaku default ini mengurangi overhead debugger dengan mengorbankan kelengkapan data debug. Jika kelengkapan lebih disukai, seperti dalam kasus ini, kita dapat menonaktifkan buffer melingkar dengan menetapkan argumen ke nilai negatif (misalnya, -1 di sini).

Contoh debug_mnist_v2 memanggil enable_dump_debug_info() dengan mengirimkan flag-command-line padanya. Untuk menjalankan program TF2 bermasalah kami lagi dengan instrumentasi debugging ini diaktifkan, lakukan:

 python -m tensorflow.python.debug.examples.v2.debug_mnist_v2 \
    --dump_dir /tmp/tfdbg2_logdir --dump_tensor_debug_mode FULL_HEALTH
 

Memulai GUI Debugger V2 di TensorBoard

Menjalankan program dengan instrumentasi debugger menciptakan logdir di / tmp / tfdbg2_logdir. Kita dapat memulai TensorBoard dan mengarahkannya ke logdir dengan:

 tensorboard --logdir /tmp/tfdbg2_logdir
 

Di browser web, navigasikan ke halaman TensorBoard di http: // localhost: 6006. Plugin "Debugger V2" harus diaktifkan secara default, menampilkan halaman yang terlihat seperti berikut:

Debugger V2 tangkapan layar tampilan penuh

Menggunakan Debugger V2 GUI untuk menemukan akar penyebab NaNs

Debugger V2 GUI di TensorBoard disusun menjadi enam bagian:

  • Peringatan : Bagian kiri atas ini berisi daftar peristiwa "peringatan" yang terdeteksi oleh debugger dalam data debug dari program TensorFlow yang diinstrumentasi. Setiap peringatan menunjukkan anomali tertentu yang membutuhkan perhatian. Dalam kasus kami, bagian ini menyoroti 499 acara NaN / with dengan warna merah muda-merah yang menonjol. Ini mengkonfirmasi kecurigaan kami bahwa model gagal untuk belajar karena adanya NaNs dan / atau infinitas dalam nilai tensor internal. Kami akan mempelajari lansiran ini segera.
  • Timeline Eksekusi Python : Ini adalah bagian atas dari bagian atas-tengah. Ini menyajikan sejarah penuh dari pelaksanaan ops dan grafik. Setiap kotak timeline ditandai dengan huruf awal dari op atau nama grafik (misalnya, "T" untuk "TensorSliceDataset" op, "m" untuk "model" tf.function ). Kita dapat menavigasi timeline ini dengan menggunakan tombol navigasi dan scrollbar di atas timeline.
  • Eksekusi Grafik : Terletak di sudut kanan atas GUI, bagian ini akan menjadi pusat tugas debugging kami. Ini berisi sejarah semua tensor floating-dtype yang dihitung di dalam grafik (yaitu, dikompilasi oleh @tf-function s).
  • Struktur Grafik (bagian bawah dari bagian tengah-atas), Kode Sumber (bagian kiri bawah), dan Stack Trace (bagian kanan bawah) pada awalnya kosong. Konten mereka akan diisi ketika kita berinteraksi dengan GUI. Tiga bagian ini juga akan memainkan peran penting dalam tugas debugging kami.

Setelah mengorientasikan diri pada organisasi UI, mari kita mengambil langkah-langkah berikut untuk memahami mengapa NaN muncul. Pertama, klik lansiran NaN / ∞ di bagian Alerts. Ini secara otomatis menggulir daftar 600 tensor grafik di bagian Eksekusi Grafik dan berfokus pada # 88, yang merupakan tensor bernama "Log: 0" yang dihasilkan oleh op Log (natural logarithm) op. Warna merah muda-merah yang menonjol menyoroti elemen -∞ di antara 1000 elemen tensor float32 2D. Ini adalah tensor pertama dalam sejarah runtime program TF2 yang berisi NaN atau infinity: tensor yang dihitung sebelum tidak mengandung NaN atau ∞; banyak (pada kenyataannya, sebagian besar) tensor yang dihitung setelahnya mengandung NaNs. Kami dapat mengkonfirmasi ini dengan menggulir ke atas dan ke bawah daftar Eksekusi Grafik. Pengamatan ini memberikan petunjuk kuat bahwa Log op adalah sumber ketidakstabilan numerik dalam program TF2 ini.

Debugger V2: Lansiran Nan / Infinity dan daftar eksekusi grafik

Mengapa Log op ini memuntahkan -∞? Menjawab pertanyaan itu membutuhkan memeriksa input ke op. Mengklik pada nama tensor ("Log: 0") menampilkan visualisasi sederhana tetapi informatif dari sekitar Log op dalam grafik TensorFlow di bagian Struktur Grafik. Perhatikan arah arus informasi dari atas ke bawah. Op itu sendiri ditampilkan dalam huruf tebal di tengah. Tepat di atasnya, kita bisa melihat op Placeholder menyediakan satu-satunya input ke Log op. Di mana tensor dihasilkan oleh Placeholder log ini dalam daftar Eksekusi Grafik? Dengan menggunakan warna latar belakang kuning sebagai alat bantu visual, kita dapat melihat bahwa logits:0 tensor logits:0 adalah dua baris di atas Log:0 tensor Log:0 , yaitu, di baris 85.

Debugger V2: Tampilan struktur grafik dan penelusuran ke input tensor

Pandangan yang lebih cermat pada perincian numerik dari tensor "log: 0" pada baris 85 mengungkapkan mengapa konsumennya Log:0 menghasilkan a -: Di antara 1000 elemen "log: 0", satu elemen memiliki nilai 0. -∞ adalah hasil dari komputasi logaritma natural 0! Jika kita entah bagaimana dapat memastikan bahwa Log op hanya terkena input positif, kita akan dapat mencegah NaN / ∞ terjadi. Ini dapat dicapai dengan menerapkan kliping (misalnya, dengan menggunakan tf.clip_by_value () ) pada tensor logit tensor.

Kami semakin dekat untuk menyelesaikan bug, tetapi belum selesai. Untuk menerapkan perbaikan, kita perlu tahu di mana dalam kode sumber Python Log op dan input Placeholder-nya berasal. Debugger V2 menyediakan dukungan kelas satu untuk melacak operasi grafik dan peristiwa eksekusi ke sumbernya. Ketika kami mengklik Log:0 tensor dalam Eksekusi Grafik, bagian Stack Trace diisi dengan jejak stack asli dari ciptaan Log op. Jejak tumpukan agak besar karena mencakup banyak bingkai dari kode internal TensorFlow (misalnya, gen_math_ops.py dan dumping_callback.py), yang dapat kita abaikan dengan aman untuk sebagian besar tugas debugging. Kerangka yang menarik adalah Baris 216 dari debug_mnist_v2.py (yaitu, file Python yang sebenarnya sedang kami coba debug). Mengklik "Baris 204" menampilkan pandangan dari baris kode yang sesuai di bagian Kode Sumber.

Debugger V2: Kode sumber dan jejak tumpukan

Ini akhirnya membawa kita ke kode sumber yang menciptakan op Log bermasalah dari input logitnya. Ini adalah fungsi kehilangan lintas-entropi kategoris khusus kami yang didekorasi dengan @tf.function dan karenanya diubah menjadi grafik TensorFlow. “Log” op Placeholder sesuai dengan argumen input pertama ke fungsi kehilangan. Log op dibuat dengan panggilan API tf.math.log ().

Perbaikan kliping nilai untuk bug ini akan terlihat seperti:

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

Ini akan menyelesaikan ketidakstabilan angka dalam program TF2 ini dan menyebabkan MLP berhasil berlatih. Pendekatan lain yang mungkin untuk memperbaiki ketidakstabilan angka adalah dengan menggunakan tf.keras.losses.CategoricalCrossentropy .

Ini menyimpulkan perjalanan kami dari mengamati bug model TF2 hingga menghasilkan perubahan kode yang memperbaiki bug, dibantu oleh alat Debugger V2, yang memberikan visibilitas penuh ke dalam sejarah eksekusi grafik yang bersemangat dan bersemangat dari program TF2 yang diinstrumentasi, termasuk ringkasan numerik dari nilai tensor dan hubungan antara ops, tensor dan kode sumber aslinya.

Kompatibilitas perangkat keras Debugger V2

Debugger V2 mendukung perangkat keras pelatihan umum termasuk CPU dan GPU. Pelatihan multi-GPU dengan tf.distributed.MirroredStrategy juga didukung. Dukungan untuk TPU masih dalam tahap awal dan membutuhkan panggilan

 tf.config.set_soft_device_placement(True)
 

sebelum memanggil enable_dump_debug_info() . Ini mungkin memiliki batasan lain pada TPU juga. Jika Anda mengalami masalah dalam menggunakan Debugger V2, silakan laporkan bug pada halaman masalah GitHub kami.

Kompatibilitas API dari Debugger V2

Debugger V2 diimplementasikan pada tingkat yang relatif rendah dari tumpukan perangkat lunak TensorFlow, dan karenanya kompatibel dengan tf.keras , tf.data , dan API lainnya yang dibangun di atas tingkat yang lebih rendah dari TensorFlow. Debugger V2 juga kompatibel dengan TF1, meskipun Timeline Eksekusi Eager akan kosong untuk debug logdir yang dihasilkan oleh program TF1.

Kiat penggunaan API

Pertanyaan yang sering ditanyakan tentang API debugging ini adalah di mana dalam kode TensorFlow orang harus memasukkan panggilan untuk enable_dump_debug_info() . Biasanya, API harus dipanggil sedini mungkin dalam program TF2 Anda, lebih disukai setelah garis impor Python dan sebelum pembuatan grafik dan eksekusi dimulai. Ini akan memastikan cakupan penuh dari semua ops dan grafik yang mendukung model Anda dan pelatihannya.

Tensor_debug_modes yang saat ini didukung adalah: NO_TENSOR , CURT_HEALTH , CONCISE_HEALTH , FULL_HEALTH , dan SHAPE . Mereka berbeda dalam jumlah informasi yang diekstraksi dari masing-masing tensor dan overhead kinerja ke program debugged. Silakan merujuk ke bagian args pada enable_dump_debug_info() ].

Overhead kinerja

API debugging memperkenalkan overhead kinerja untuk program TensorFlow yang diinstrumentasi. Biaya overhead bervariasi menurut tensor_debug_mode , jenis perangkat keras, dan sifat dari program TensorFlow yang diinstrumentasi. Sebagai titik referensi, pada GPU, mode NO_TENSOR menambahkan overhead 15% selama pelatihan model Transformer di bawah ukuran batch 64. Persentase overhead untuk tensor_debug_modes lainnya lebih tinggi: sekitar 50% untuk CURT_HEALTH , CONCISE_HEALTH , FULL_HEALTH dan SHAPE mode. Pada CPU, overhead sedikit lebih rendah. Pada TPU, biaya overhead saat ini lebih tinggi.

Relasi dengan API debugging TensorFlow lainnya

Perhatikan bahwa TensorFlow menawarkan alat dan API lain untuk debugging. Anda dapat menelusuri API semacam itu di bawah tf.debugging.* Namespace di halaman API docs. Di antara API ini yang paling sering digunakan adalah tf.print() . Kapan satu harus menggunakan Debugger V2 dan kapan harus tf.print() digunakan sebagai gantinya? tf.print() mudah digunakan di mana

  1. kami tahu persis tensor mana yang akan dicetak,
  2. kita tahu di mana tepatnya dalam kode sumber untuk memasukkan pernyataan tf.print() ,
  3. jumlah tensor semacam itu tidak terlalu besar.

Untuk kasus lain (misalnya, memeriksa banyak nilai tensor, memeriksa nilai tensor yang dihasilkan oleh kode internal TensorFlow, dan mencari asal-usul ketidakstabilan numerik seperti yang kami tunjukkan di atas), Debugger V2 menyediakan cara debug yang lebih cepat. Selain itu, Debugger V2 menyediakan pendekatan terpadu untuk memeriksa eager dan grafik tensor. Ini juga memberikan informasi tentang struktur grafik dan lokasi kode, yang berada di luar kemampuan tf.print() .

API lain yang dapat digunakan untuk men-debug masalah yang melibatkan ∞ dan NaN adalah tf.debugging.enable_check_numerics() . Tidak seperti enable_dump_debug_info() , enable_check_numerics() tidak menyimpan informasi debug pada disk. Sebagai gantinya, ia hanya memonitor ∞ dan NaN selama runtuhnya TensorFlow dan kesalahan dengan lokasi kode asal segera setelah op apa pun menghasilkan nilai numerik yang buruk. Ini memiliki overhead kinerja yang lebih rendah dibandingkan dengan enable_dump_debug_info() , tetapi tidak mampu melacak penuh sejarah eksekusi program dan tidak datang dengan antarmuka pengguna grafis seperti Debugger V2.