Pytorch: RFC: Tambahkan bendera torch.deterministic untuk memaksa algoritma deterministik

Dibuat pada 18 Des 2018  ·  67Komentar  ·  Sumber: pytorch/pytorch

Fitur

Kita harus menambahkan variabel global untuk memaksa PyTorch menggunakan algoritma deterministik bitwise. Soumith menyarankan untuk menambahkan flag ke sub-paket torch.experimental karena kami tidak yakin tentang beberapa detailnya.

Motivasi

Determinisme bitwise antar run terkadang berguna untuk debugging. Namun, sulit untuk menulis algoritma deterministik yang efisien untuk beberapa operasi.

Melempar

Ketika torch.experimental.deterministic adalah False (default), PyTorch harus menggunakan algoritme tercepat yang tersedia untuk operasi tertentu. Ketika torch.experimental.deterministic adalah True , PyTorch hanya boleh menggunakan algoritme deterministik. PyTorch harus mengeluarkan peringatan jika kita tidak memiliki algoritme deterministik yang tersedia untuk operasi tertentu dan torch.experimental.deterministic adalah True .

cuDNN

Kita sudah memiliki flag torch.backends.cudnn.deterministic untuk mengontrol pilihan algoritma cuDNN. Kita harus menyimpan flag ini untuk saat ini dan membatasi cuDNN ke algo deterministik jika torch.backends.cudnn.deterministic atau torch.experimental.deterministic adalah True.

Tanpa gol

Kami hanya bertujuan untuk determinisme bitwise antara berjalan pada mesin dengan arsitektur dan konfigurasi yang sama. Misalnya, bahkan ketika torch.experimental.deterministic Benar, kami tidak bertujuan untuk determinisme bitwise ketika salah satu dari berikut ini bervariasi:

  • Versi PyTorch
  • Arsitektur CPU (misalnya x86 dengan AVX vs. ARM)
  • Arsitektur GPU (misalnya AMD vs NVIDIA atau P100 vs V100)
  • Ketergantungan perpustakaan (misalnya OpenBLAS vs. MKL)
  • Jumlah utas OpenMP

Saran implementasi

Saya sarankan menambahkan fitur ini dalam dua langkah. Langkah pertama adalah menambahkan flag torch.backends.cudnn.deterministic dan menambahkan peringatan ke operasi non-deterministik. Langkah kedua adalah menambahkan implementasi deterministik untuk operasi non-deterministik.

Ada sebagian daftar operasi non-deterministik di dokumen PyTorch .

Pertanyaan-pertanyaan terbuka

Bagaimana seharusnya torch.experimental.deterministic berinteraksi dengan benih RNG? Haruskah itu menetapkan seed default jika tidak ada seed manual yang disetel? Haruskah itu mengeluarkan peringatan jika tidak ada seed manual yang disetel?

cc @ezyang @gchanan @zou3519

feature high priority determinism internals triaged

Komentar yang paling membantu

Hai, saya ingin berbicara tentang rencana ke depan untuk torch.deterministic . Ada beberapa pertanyaan tingkat tinggi yang perlu kita jawab:

  1. Apa semantik dari torch.deterministic ? Apa yang diharapkan pengguna? Apakah upaya terbaik benar-benar bermanfaat bagi pengguna? Jika tidak berguna, apakah lebih baik mendefinisikan torch.deterministic dalam hal operasi apa yang dikendalikannya?
  2. Sekarang kita memiliki flag torch.deterministic , apakah masuk akal untuk menghilangkan argumen kata kunci deterministic= sepenuhnya dari API yang dihadapi publik ( bmm , saya melihat Anda).
  3. Apa permainan akhir untuk pekerjaan ini? Berapa banyak dari ini yang akan Anda (@kurtamohler) kerjakan, versus komunitas umum, dan ketika kita sampai pada akhir tugas Anda di sini, seperti apa keadaan yang masuk akal?

Dimulai dengan (1), dokumentasi saat ini untuk torch.deterministic mengatakan:

     r"""Sets a global flag to force all operations to use a deterministic
    implementation if available. If an operation that does not have a
    deterministic implementation is called while this setting is True, the
    operation will throw a RuntimeError.

    Note that deterministic operations tend to have worse performance than
    non-deterministic operations.

Meskipun ini mungkin benar untuk keadaan akhir akhirnya, ini secara tidak akurat mewakili situasi saat ini, di mana banyak operasi belum diaudit dan untuk model tertentu, kami tidak tahu apakah torch.deterministic benar-benar akan melakukan apa yang seharusnya dilakukan. tertulis di kaleng dan buat model Anda deterministik / naikkan kesalahan saat Anda menekan nondet. Jadi pada dasarnya, implementasi kami bermasalah sehubungan dengan semantik ini, dan akan terus bermasalah di masa mendatang. Ini bukan keadaan yang bagus untuk berada.

Kita bisa mengubah dokumentasi torch.deterministic untuk memperbaiki ini. Beberapa kemungkinan perubahan:

  • torch.deterministic adalah upaya terbaik , tetapi tolong laporkan bug jika Anda melihatnya tidak menangkap beberapa non-determinisme
  • torch.deterministic mengaktifkan perilaku operator ini (dan kemudian memberikan daftar lengkap operator yang diaktifkannya)

Poin-poin kedua mengarah ke (2): jika torch.deterministic sekarang ada sebagai cara untuk mengaktifkan determinisme, jauh lebih penting untuk mendukung determinisme secara langsung di API pengguna. Jadi kita mungkin seharusnya tidak menambahkan argumen deterministic ke bmm. Kami mungkin mempertimbangkan untuk mengekspos fungsi internal jika Anda ingin mengaktifkan sesuatu secara langsung, tetapi deterministic seharusnya tidak tersedia langsung pada fungsi itu sendiri.

Bagaimana menurut anda? Saya pikir mengubah dokumen mungkin adalah cara termudah untuk mendapatkan jalur yang berkelanjutan. Ada beberapa detail lain, seperti cara mengisi daftar lengkap, tetapi semantik ini mungkin lebih masuk akal daripada semantik "ideal" yang sebenarnya tidak benar.

cc @gchanan @mruberry

Semua 67 komentar

Ini acungan jempol dari saya. Masalah utamanya adalah bagaimana benar-benar meluncurkan ini di mana-mana di basis kode; tidak ada yang lebih buruk untuk mengklaim bahwa kita deterministik, tetapi kemudian diam-diam tidak :)

Saya setuju dan pendekatan saya adalah menandai operasi dan kesalahan ketika deterministik aktif dan kami tahu mereka tidak.

Saya pikir kesalahan pada operasi non-deterministik terlalu keras. Peringatan sepertinya pengalaman yang lebih lancar

Saya pikir defaultnya adalah melempar, tetapi saya kira kita dapat mendukung properti multi-nilai di sana (non-deterministik tidak apa-apa, peringatkan, lempar).

Saya harus mengakui bahwa saya tidak benar-benar melihat kasus penggunaan peringatan. Ketika orang cukup peduli dengan deterministik untuk mengaktifkannya, mereka mungkin mengharapkan kesalahan. Anda selalu dapat mematikannya untuk panggilan tertentu untuk mengatakan bahwa Anda setuju dengan nondeterminisme apa pun yang ada di sana.

Kesalahan, peringatan, dokumentasi yang tepat...
Yang terakhir adalah suatu keharusan.
Peringatan atau kesalahan? Aku akan pergi dengan kesalahan.

melempar tampak hebat. Saya setuju dengan Adam bahwa memberikan opsi untuk memperingatkan daripada melempar tampaknya masuk akal.

Terima kasih telah menimbang. Pada akhirnya, upaya utama untuk bendera ternary adalah bendera itu sendiri, dan itu tidak sulit.
Saya akan menambahkan bendera ke Context.h dan memercikkan (melalui fungsi utilitas) AT_ERROR dan AT_CHECK.

Halo,
Ada berita tentang bendera ini?
Determinisme sangat penting.
Dari pengalaman saya, versi saat ini memungkinkan determinisme lebih dari satu gpu, hingga presisi 1e-16 , menggunakan benih tetap. Perhatikan bahwa perbedaan yang sangat kecil dapat diperbesar dan hasilnya divergen.

Tolong, pertimbangkan kasus multigpu juga (setidaknya untuk K gpus tetap, perilakunya harus deterministik. Saya dapat mencapai semacam determinisim yang rusak dari waktu ke waktu karena suatu alasan saya tidak mengerti untuk saat ini (menggunakan nightly build 1.2.0.dev20190616 ).). Saya berjuang dengan itu sekarang ( 1 , 2 ).

Terima kasih!

@t-vi apakah Anda aktif mengerjakan ini?

Saya tidak ingin mencegah Anda melakukannya.

@t-vi Maaf jika saya tidak jelas, saya tidak berencana untuk mengerjakan ini :) . Hanya mencoba memahami apakah ada orang yang secara aktif melakukannya.

Setelah hampir satu tahun, masalah interpolasi non-deterministik masih belum terpecahkan.

Semoga komunitas dapat menambahkan fitur ini :)

Mungkin interpolasi deterministik akan sangat membantu pengguna.

~ Saya belum benar-benar mengiklankannya, tetapi mengingat bahwa tampaknya ada lebih banyak minat pengguna daripada sumber daya pengembang yang dialokasikan, saya mencantumkan ini sebagai proyek yang dapat Anda pilih di halaman sponsor github saya ketika saya mengatur ini.
Saya cukup yakin kami dapat membuat kemajuan yang baik pada akhir tahun dan interpolasi tentu saja adalah salah satu hal yang saya punya rencana untuk memperbaikinya (mirip dengan pseudocode untuk fold bahwa saya ada di suatu tempat dalam masalah) tetapi tidak tidak termasuk dalam daftar prioritas saya sendiri.~
Ternyata tidak menarik.

interpolasi deterministik akan sangat membantu. tautan

Prioritas menabrak, terutama untuk CUDA, berdasarkan umpan balik pengguna

Saya senang sudah diperbaiki, terima kasih!

@t-vi untuk bersikap adil, saya tidak berpikir "prioritas menabrak" sama dengan "itu sedang diperbaiki" :).

Menantikan solusi

colesbury menyebutkan bahwa satu alasan pembunuh untuk algoritme deterministik bukanlah karena determinisme sebenarnya masalahnya, tetapi Anda dapat mengesampingkannya saat Anda mengaktifkannya;)

Bagaimana seharusnya torch.experimental.deterministic berinteraksi dengan benih RNG? Haruskah itu menetapkan seed default jika tidak ada seed manual yang disetel? Haruskah itu mengeluarkan peringatan jika tidak ada seed manual yang disetel?

Saya sarankan untuk tidak menyetel benih jika tidak ada yang disetel oleh pengguna. Untuk satu karena itu memasangkan dua antarmuka yang tidak diperlukan (pengguna yang peduli dengan determinisme akan memahami RNG dengan sangat baik, menurut saya). Lebih penting lagi, ini sangat sulit dilakukan dengan andal; seseorang dapat menggunakan RNG dalam aplikasi multi-proses/utas, memiliki subkelas torch.Generator , menggunakan numpy.random juga, dll.

Tidak yakin tentang peringatan, hanya jika ada tempat yang waras untuk mengaturnya (misalnya apakah Anda kemudian memaksa untuk menyemai sebelum determinism=True daripada di modul/fungsi yang sama di mana RNG digunakan?).

Saya hanya ingin tahu bahwa ketika saya menetapkan torch.backends.cudnn.deterministic=True , operator interpolasi masih tidak dapat deterministik. Apakah interpolasi pytorch tidak menggunakan cudnn ?

Mungkin tidak. Anda dapat nvprof menjalankan interpolasi Anda untuk memeriksa dengan pasti.

Saya bertanya-tanya apakah kita harus terus memberikan argumen deterministic dalam panggilan fungsi setelah torch.experimental.deterministic diimplementasikan. Mungkin kita harus melakukannya, karena pengguna mungkin lebih suka determinisme untuk beberapa operasi dan kecepatan untuk operasi lain.

Jika kita melakukan menjaga argumen, maka apa yang terjadi jika torch.experimental.deterministic dan fungsi ini deterministic bendera menentang satu sama lain. Haruskah torch.experimental.deterministic = True berarti "gunakan determinisme dalam semua kasus, apa pun yang terjadi", atau haruskah itu berarti "gunakan determinisme sebagai nilai default, tetapi jika argumen deterministic ditentukan dalam pemanggilan fungsi, maka gunakan pengaturan itu untuk panggilan fungsi tertentu itu." Dengan kata lain, bagaimana seharusnya kode di bawah ini ditangani? Adakah yang tahu bagaimana flag torch.backends.cudnn.deterministic bekerja dalam situasi yang sama?

torch.experimental.deterministic = True
torch.some_operation(deterministic=False)

@kurtamohler Pertanyaan bagus. Saya pikir perbaikan termudah adalah membuatnya bool? deterministic=None , dan kemudian menafsirkan None berarti "menghormati torch.experimental.deterministic ", dan jika tidak, gunakan persis apa yang diminta pengguna.

Kami memiliki situasi yang mirip dengan konvolusi, tetapi cara yang dilakukan adalah bahwa ada convolution tanpa argumen benchmark , dan kemudian _convolution dengan argumen eksplisit patokan.

Saya pikir salah satu dari solusi ini dapat diterima; namun, pendekatan konvolusi memiliki manfaat tambahan karena tidak membocorkan flag deterministic internal ke API yang terlihat oleh pengguna (kecuali jika mereka menggunakan API internal).

Apa alasan untuk "Saya ingin menjadi deterministik di mana-mana, tetapi _tidak di operator khusus ini_"? Apakah ini benar-benar seharusnya menjadi kasus penggunaan yang cukup umum untuk menjamin penambahan input tambahan ke banyak operator kami (dan sebagian besar yang kompleks)? IMO akan lebih baik untuk menyediakan manajer konteks untuk mengaktifkan determinisme.

@apaszke , ya saya pikir Anda benar bahwa akan lebih baik menggunakan pengelola konteks untuk mengaktifkan determinisme. Saya tidak akan mengatakan bahwa kita harus menambahkan argumen deterministic ke operator mana pun, tetapi beberapa operator sudah memilikinya. Apakah lebih baik untuk menghapus semua itu dan menghancurkan BC, atau akan lebih baik untuk menyimpannya dan membiarkannya menimpa torch.experimental.deterministic ?

Saya akan mengatakan bahwa kita harus menghapusnya atau menjadikannya pribadi setidaknya (yaitu awalan garis bawah atau sth).

Saya ingin tahu apakah fitur deterministik untuk fungsi interpolasi ditutup dan tidak akan diimplementasikan?

Tidak, kami menerima versi deterministik dari SEMUA fungsi di PyTorch

@ezyang versi pytorch mana yang memiliki fungsi F.interpolate deterministik? apakah ini mulai dari pytorch 1.6? atau tersedia dalam versi stabil terbaru (1.5)? atau apakah saya harus mengunduh dan menginstal Pytorch dari sumber?

Saya akan senang untuk mulai mengerjakan ini

Komit di atas hanya menambahkan bendera, itu belum memengaruhi operasi apa pun. Saya akan menghargai jika seseorang dapat meluangkan waktu beberapa menit untuk melihatnya dan memberi tahu saya jika saya melakukan sesuatu yang salah atau jika ada yang dapat diperbaiki sejauh ini. Saya mendasarkan ini dari bagaimana torch.backends.cudnn.deterministic diimplementasikan.

Ini terlihat OK, tetapi saya merasa penamaan internal tidak boleh menyertakan eksperimental (karena, seolah-olah, suatu hari Anda ingin membuatnya tidak eksperimental, dan itu tidak harus melibatkan harus mengganti nama semua bit implementaiton!)

@ezyang , ya itu masuk akal, saya akan mengganti nama.

Saya menambahkan torch.experimental.deterministic_error_level , mirip dengan apa yang dilakukan @t-vi dalam karya sebelumnya tentang masalah ini. deterministic_error_level mengontrol perilaku kesalahan/peringatan jika deterministic == True dan fungsi yang diberikan tidak memiliki implementasi deterministik. Itu dapat diatur ke 2 (kesalahan), 1 (peringatan), atau 0 (diam).

Jika pengguna menyetelnya ke nilai lain, saya ingin melempar pengecualian runtime python yang dapat ditangkap. Biasanya, saya akan menggunakan TORCH_CHECK() untuk perilaku seperti itu, tetapi dalam kasus ini, pengecualian tidak dapat ditangkap dan saya tidak yakin mengapa. Inilah panggilan TORCH_CHECK() : tautan

Inilah yang terjadi ketika pemeriksaan itu gagal:

>>> import torch
>>> try:
...     torch.experimental.deterministic_error_level=50
... except:
...     print('exception caught')
... 
terminate called after throwing an instance of 'c10::Error'
  what():  error level 50 is invalid, must be one of 0: None, 1: Warn, or 2: Error
Exception raised from longToErrorLevel at ../aten/src/ATen/Context.cpp:85 (most recent call first):
frame #0: c10::Error::Error(c10::SourceLocation, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) + 0x58 (0x7f53e2cc0878 in /work/kurtamohler/development/pytorch-deterministic-flag/torch/lib/libc10.so)
frame #1: at::Context::longToErrorLevel(long) + 0x122 (0x7f53f6d61a82 in /work/kurtamohler/development/pytorch-deterministic-flag/torch/lib/libtorch_cpu.so)
frame #2: THPModule_setDeterministicErrorLevel(_object*, _object*) + 0x31 (0x7f53fb5625d1 in /work/kurtamohler/development/pytorch-deterministic-flag/torch/lib/libtorch_python.so)
<omitting python frames>
frame #23: __libc_start_main + 0xe7 (0x7f5432d62b97 in /lib/x86_64-linux-gnu/libc.so.6)

Aborted (core dumped)

Jika ada yang tahu bagaimana saya bisa memperbaikinya, beri tahu saya.

@kurtamohler adalah THPModule_setDeterministicErrorLevel hilang HANDLE_TH_ERRORS / END_ HANDLE_TH_ERRORS makro? Mereka diperlukan untuk menangkap pengecualian C++ dan menerjemahkannya ke dalam pengembalian kesalahan Python.

Ah itu dia, terima kasih @colesbury!

Saya mulai menambahkan peringatan non-deterministik ke semua penelepon atomicAdd . Saya perhatikan bahwa beberapa penelepon hanya menggunakan atomicAdd dalam kasus tertentu. Misalnya, adaptive_avg_pool3d_backward hanya digunakan jika (isizeW%osizeW != 0) || (isizeH%osizeH != 0) || (isizeT%osizeT != 0) benar. Haruskah saya hanya memperingatkan dalam kasus-kasus ini dan mencoba menyampaikannya dalam pesan kesalahan, atau apakah tidak apa-apa untuk hanya memperingatkan setiap kali fungsi-fungsi ini dipanggil apakah atomicAdd akhirnya digunakan atau tidak?

Mungkin lebih mudah diterapkan dan lebih mudah dipahami jika Anda waspada tanpa syarat.

@ngimel , saya sudah memikirkan cara menggunakan CUBLAS_WORKSPACE_CONFIG untuk memastikan penggunaan aliran deterministik, dan saya pikir ada dua pendekatan utama yang harus dipertimbangkan.

Jika seseorang menggunakan salah satu versi CUDA yang terpengaruh (10.2 atau lebih tinggi saat ini), dan torch.set_deterministic(True) dipanggil, gunakan std::getenv untuk memastikan bahwa CUBLAS_WORKSPACE_CONFIG adalah :16:8 atau :4096:8 . Jika tidak, lakukan salah satu (1) atau (2):

  1. Lempar kesalahan yang memberi tahu pengguna untuk mengatur variabel dengan tepat.

  2. Secara otomatis mengatur variabel dengan putenv ( _putenv pada Windows). Namun, ada beberapa keputusan desain lebih lanjut yang terkait dengan ini. Haruskah kita memilih :16:8 (kinerja lebih rendah, tetapi penggunaan memori lebih sedikit) atau :4096:8 (kinerja lebih tinggi, tetapi penggunaan memori lebih banyak)? Juga, jika pengguna menyetel variabel ke beberapa nilai non-deterministik lainnya, kita harus melacak nilai asli dan mengembalikannya jika torch.set_deterministic(False) dipanggil, atau kita dapat membuat kesalahan yang memberi tahu pengguna bahwa mereka perlu menghapus variabel, atau skema lainnya.

Juga, saya tidak tahu apakah menyetel variabel saat aplikasi sedang berjalan akan benar-benar memengaruhi, jadi saya tidak tahu pasti apakah opsi (2) memungkinkan. Variabel mungkin hanya diperiksa sekali, saat runtime CUDA dimulai atau saat handle cuBLAS dibuat. Saya tidak dapat menemukan informasi tentang ini, jadi saya mungkin harus mencari tahu secara eksperimental (saya harus menggunakan pereproduksi penggunaan aliran non-deterministik untuk menulis tes, jadi saya akan melihat ini) . Saya juga mencari panggilan API, daripada menggunakan variabel lingkungan, tetapi CUDA sepertinya tidak menawarkannya.

Apakah Anda memiliki pendapat yang kuat tentang opsi mana yang lebih baik? Opsi (2) mungkin akan lebih ramah pengguna, tetapi mungkin kurang transparan daripada opsi (1).

Saya tidak tahu apakah menyetel variabel saat aplikasi sedang berjalan akan benar-benar berpengaruh

Untuk menindaklanjuti pertanyaan ini, menyetel variabel lingkungan di dalam skrip pytorch tampaknya tidak memengaruhi determinisme aliran CUDA. Saya memodifikasi skrip dari https://github.com/pytorch/pytorch/issues/39849 untuk dijalankan beberapa kali dan membandingkan statistik pelatihan untuk memeriksa perilaku non-deterministik. Ia mencoba mengatur CUBLAS_WORKSPACE_CONFIG=:4096:8 untuk memastikan penggunaan aliran deterministik: https://github.com/kurtamohler/pytorch-perf-test-scripts/blob/master/nondeterministic_alert/cuda_stream_nondeterminism.py

Menjalankannya menunjukkan bahwa kita tidak mendapatkan perilaku deterministik dari menyetel variabel di dalam skrip:

$ python cuda_stream_nondeterminism.py 
Before setting var: not deterministic
After setting var: not deterministic
After restoring old var: not deterministic

Tetapi menjalankannya dengan variabel lingkungan yang disetel di luar skrip membuatnya deterministik:

$ CUBLAS_WORKSPACE_CONFIG=:4096:8 python cuda_stream_nondeterminism.py 
Before setting var: possibly deterministic
After setting var: possibly deterministic
After restoring old var: possibly deterministic

Catatan, ini mencetak "mungkin deterministik" karena saya hanya menjalankan fungsi pelatihan 5 kali, dan mungkin untuk beruntung bahkan jika perilaku tidak benar-benar deterministik.

Mungkin jika saya dapat menginisialisasi ulang aliran cuda, itu akan memaksanya untuk menghormati variabel CUBLAS_WORKSPACE_CONFIG diubah. Saya ingin mencobanya, tetapi saya tidak tahu bagaimana atau apakah mungkin melakukannya saat runtime. Jika seseorang tahu, tolong beri tahu saya.

Saya menemukan bahwa saya dapat membuat dan menggunakan aliran baru dengan:

with  torch.cuda.stream(torch.cuda.Stream()):

Tetapi aliran baru tidak menghormati pengaturan variabel lingkungan yang diubah. Saya juga menemukan torch.cuda.init() , tetapi sayangnya, itu tidak boleh dilakukan jika cuda telah diinisialisasi.

Jadi, kecuali kami dapat memikirkan sesuatu yang lain untuk dicoba, sepertinya kami mungkin tidak dapat mengubah konfigurasi ruang kerja secara otomatis, jadi kami mungkin hanya perlu membuat kesalahan yang memberi tahu pengguna untuk mengaturnya.

Yap, pengaturan variabel lingkungan setelah konteks cuda telah diinisialisasi tidak berpengaruh, jadi sayangnya itu semua atau tidak sama sekali solusi. Melempar kesalahan yang memberi tahu pengguna untuk mengaturnya terdengar masuk akal.

Saat ini, sepertinya tidak mungkin untuk memeriksa versi CUDA dari file yang dikompilasi non-nvcc, jadi saya yakin saya harus menambahkannya ke aten/src/ATen/cuda/detail/CUDAHooks.h (memeriksa versi cuDNN adalah bagian dari antarmuka itu) . Jika ada yang tahu lebih baik, tolong beri tahu saya.

Komit di atas menambahkan kesalahan. Tapi saya perlu mencari tahu apa yang harus dilakukan dengan unit test sekarang. Ada dua masalah:

  • Untuk menguji bahwa kesalahan dilemparkan ke dalam kasus yang benar (cuda >= 10.2 dan CUBLAS_WORKSPACE_CONFIG tidak disetel dengan benar), infrastruktur pengujian harus dapat secara otomatis mengubah variabel lingkungan sebelum menjalankan pengujian
  • Untuk memastikan bahwa pengujian torch.set_deterministic yang ada tidak rusak, kita perlu menyetel CUBLAS_WORKSPACE_CONFIG dengan benar. Kami berpotensi hanya mengatur variabel ini secara default di semua pekerjaan CI yang menggunakan cuda >= 10.2.

Saya menemukan bahwa saya dapat mengatur variabel lingkungan dari skrip python, lalu memuat ulang modul obor untuk membuatnya menghormati nilai baru:

>>> import torch
>>> torch.set_deterministic(True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/work/kurtamohler/development/pytorch-deterministic-flag-cuda-env-var/torch/__init__.py", line 306, in set_deterministic
    _C._set_deterministic(d)
RuntimeError: To enable deterministic behavior with CUDA >= 10.2, you must set environment variable CUBLAS_WORKSPACE_CONFIG=:4096:8 or CUBLAS_WORKSPACE_CONFIG=:16:8. For more information, go to https://docs.nvidia.com/cuda/cublas/index.html#cublasApi_reproducibility
>>> import os
>>> os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8'
>>> from importlib import reload
>>> torch = reload(torch)
>>> torch.set_deterministic(True)

Saya tidak tahu apakah memuat ulang obor juga akan menyebabkan CUDA menghormati perubahan ini, tetapi setidaknya ini memberi kami cara untuk menguji unit untuk pesan kesalahan. Meskipun saya harus bertanya, mungkinkah ada masalah dengan memuat ulang modul obor di dalam unit test?

EDIT: Ternyata saya tidak perlu memuat ulang obor untuk membuatnya melihat variabel lingkungan yang berubah. Juga, memuat ulang setelah mengubah variabel tidak memengaruhi runtime CUDA.

Komit di atas membahas semua masalah yang saya sebutkan dalam komentar saya sebelumnya. Saya menambahkan dekorator untuk membungkus tes API apa pun yang memanggil torch.set_deterministic() , untuk sementara menyetel CUBLAS_WORKSPACE_CONFIG=:4096:8 hanya jika diperlukan. Ini juga mengembalikan flag deterministik dan pengaturan CUBLAS_WORKSPACE_CONFIG ke keadaan sebelum pengujian dijalankan.

Saya menyadari bahwa dokumen reproduktifitas menyebutkan bahwa perilaku CuDNN deterministik memerlukan:

torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

Apakah seseorang di utas ini tahu persis apa benchmark itu, dan mengapa torch.backends.cudnn.deterministic = True tidak cukup?

Kami mungkin ingin memaksa benchmark dimatikan jika torch.is_deterministic() == True . Dengan kata lain, alih-alih meneruskan ctx.benchmarkCuDNN() langsung ke at::_convolution() , mungkin seharusnya ctx.benchmarkCuDNN() && !ctx.deterministic() pada baris ini: https://github.com/pytorch/pytorch/blob/ master/aten/src/ATen/native/Convolution.cpp#L602

Jika kami tidak melakukan perubahan ini, sepertinya orang yang menggunakan set_deterministic dan CuDNN harus melakukan ini:

torch.set_deterministic(True)
torch.backends.cudnn.benchmark = False

Artinya set_deterministic() saja tidak akan mencakup semuanya, yang menurut saya membingungkan.

cc @ezyang @colesbury @t-vi @ngimel

Saat menghadapi konfigurasi konvolusi baru, benchmark=True menjalankan semua implementasi cudnn yang tersedia dan memilih yang tercepat, menyimpan implementasi yang dipilih, sehingga semua panggilan berikutnya ke konvolusi dengan parameter yang sama akan menggunakannya. Jadi, jika deterministic juga disetel ke True hasilnya akan deterministik selama cache ini tetap ada, yaitu selama Anda berada dalam proses yang sama. Jika ada implementasi dengan runtime yang dekat, saat berikutnya Anda memulai proses dan menjalankan benchmarking lagi, implementasi lain mungkin menang, dan hasilnya (sementara masih deterministik dalam pengertian yang dijelaskan di atas) akan berbeda dari run sebelumnya. Jadi, untuk menjamin determinisme antar run, Anda harus mematikan benchmarking.

Jadi begitu. Jadi mungkin hanya determinisme dalam proses, bukan determinisme lintas-proses, yang penting untuk beberapa aplikasi, sehingga orang dapat merasa berguna untuk tetap dapat menggunakan pembandingan jika mereka menetapkan torch.set_deterministic(True) . Dalam hal ini, saya seharusnya tidak mengubah perilaku saat ini. Selama saya memperbarui dokumen untuk memperjelasnya, saya tidak melihat masalah dengan itu.

Saya membuat halaman wiki untuk membantu kontributor PyTorch menambahkan dukungan untuk torch.set_deterministic() : https://github.com/pytorch/pytorch/wiki/How-to-support-%60torch.set_deterministic ()%60-in- PyTorch-operator

Setiap perbaikan dipersilakan.

Juga, saya tidak yakin apakah bagian "Fungsi yang saat ini tidak didukung" harus ada di wiki ini atau apakah akan lebih baik sebagai masalah github baru (halaman wiki dapat ditautkan ke sana). Apakah ada yang punya preferensi?

Hai, saya ingin berbicara tentang rencana ke depan untuk torch.deterministic . Ada beberapa pertanyaan tingkat tinggi yang perlu kita jawab:

  1. Apa semantik dari torch.deterministic ? Apa yang diharapkan pengguna? Apakah upaya terbaik benar-benar bermanfaat bagi pengguna? Jika tidak berguna, apakah lebih baik mendefinisikan torch.deterministic dalam hal operasi apa yang dikendalikannya?
  2. Sekarang kita memiliki flag torch.deterministic , apakah masuk akal untuk menghilangkan argumen kata kunci deterministic= sepenuhnya dari API yang dihadapi publik ( bmm , saya melihat Anda).
  3. Apa permainan akhir untuk pekerjaan ini? Berapa banyak dari ini yang akan Anda (@kurtamohler) kerjakan, versus komunitas umum, dan ketika kita sampai pada akhir tugas Anda di sini, seperti apa keadaan yang masuk akal?

Dimulai dengan (1), dokumentasi saat ini untuk torch.deterministic mengatakan:

     r"""Sets a global flag to force all operations to use a deterministic
    implementation if available. If an operation that does not have a
    deterministic implementation is called while this setting is True, the
    operation will throw a RuntimeError.

    Note that deterministic operations tend to have worse performance than
    non-deterministic operations.

Meskipun ini mungkin benar untuk keadaan akhir akhirnya, ini secara tidak akurat mewakili situasi saat ini, di mana banyak operasi belum diaudit dan untuk model tertentu, kami tidak tahu apakah torch.deterministic benar-benar akan melakukan apa yang seharusnya dilakukan. tertulis di kaleng dan buat model Anda deterministik / naikkan kesalahan saat Anda menekan nondet. Jadi pada dasarnya, implementasi kami bermasalah sehubungan dengan semantik ini, dan akan terus bermasalah di masa mendatang. Ini bukan keadaan yang bagus untuk berada.

Kita bisa mengubah dokumentasi torch.deterministic untuk memperbaiki ini. Beberapa kemungkinan perubahan:

  • torch.deterministic adalah upaya terbaik , tetapi tolong laporkan bug jika Anda melihatnya tidak menangkap beberapa non-determinisme
  • torch.deterministic mengaktifkan perilaku operator ini (dan kemudian memberikan daftar lengkap operator yang diaktifkannya)

Poin-poin kedua mengarah ke (2): jika torch.deterministic sekarang ada sebagai cara untuk mengaktifkan determinisme, jauh lebih penting untuk mendukung determinisme secara langsung di API pengguna. Jadi kita mungkin seharusnya tidak menambahkan argumen deterministic ke bmm. Kami mungkin mempertimbangkan untuk mengekspos fungsi internal jika Anda ingin mengaktifkan sesuatu secara langsung, tetapi deterministic seharusnya tidak tersedia langsung pada fungsi itu sendiri.

Bagaimana menurut anda? Saya pikir mengubah dokumen mungkin adalah cara termudah untuk mendapatkan jalur yang berkelanjutan. Ada beberapa detail lain, seperti cara mengisi daftar lengkap, tetapi semantik ini mungkin lebih masuk akal daripada semantik "ideal" yang sebenarnya tidak benar.

cc @gchanan @mruberry

@zou3519 berpotongan dengan Q juga di https://github.com/pytorch/pytorch/pull/38683#issuecomment -662590937

Saya senang Anda mengajukan pertanyaan ini @ezyang , @zou3519 , dan @mruberry. Saya setuju bahwa dokumentasi yang saya tulis adalah representasi palsu dari keadaan saat ini.

Saya menyukai gagasan untuk membuat daftar lengkap semua fungsi yang dipengaruhi oleh torch.set_deterministic() , sehingga kami tidak membohongi pengguna. Terima kasih telah menambahkannya ke 1.6.0, @zou3519.

Saya setuju bahwa kita tidak boleh menawarkan pengaturan deterministic sebagai argumen fungsi langsung.

Untuk permainan akhir, saya senang untuk terus mengerjakan ini selama diperlukan, tetapi ini harus diatur sehingga siapa pun dapat dengan cepat mempelajari cara membantu.

Dalam jangka panjang, saya pikir memberikan daftar lengkap fungsi yang terpengaruh adalah keputusan yang valid, tetapi saya tidak berpikir bahwa strategi saja akan memaksimalkan kegunaan flag deterministik. Kami dapat mengkategorikan fungsi (dalam satu lingkungan tertentu) seperti ini:

  1. Deterministik
  2. Nondeterministik secara default, tetapi memiliki dukungan untuk flag deterministik (baik kesalahan atau implementasi alternatif)
  3. Nondeterministik dan tidak memiliki dukungan untuk bendera deterministik

Tentu saja kasus yang ideal adalah menghilangkan kategori 3 sepenuhnya, dan kemudian daftar fungsi kategori 2 sudah cukup. Namun, fungsi kategori 3 akan tetap ada untuk jangka waktu yang signifikan (atau mungkin selamanya, jika tidak semua kontributor mengetahui pertanyaan determinisme, atau komit secara tidak sengaja menghilangkan determinisme untuk suatu fungsi, dll.). Jadi meskipun kita memiliki daftar lengkap dari semua fungsi kategori 2, pengguna tidak memiliki cara sederhana untuk mengetahui apakah fungsi yang tidak muncul dalam daftar adalah deterministik atau tidak (bisa kategori 1 atau 3). Misalnya, torch.add tidak muncul di daftar, jadi bagaimana pengguna tahu bahwa itu deterministik?

Mungkin kita bisa memikirkan untuk mempertahankan daftar fungsi kategori 3 juga. Tetapi mempertahankan daftar ini secara manual akan sangat sulit karena berbagai alasan, jadi saya ingin tahu apakah kami dapat mengotomatiskannya. Kami berpotensi menyiapkan tugas CI yang menjalankan tes determinisme pada semua fungsi. Tidak mungkin untuk 100% membuktikan secara induktif bahwa suatu fungsi adalah deterministik, dan fungsi nondeterministik terkadang dapat memberikan hasil yang sama beberapa kali jika kita tidak beruntung. Tetapi semakin sering kita menjalankan tes ini, semakin yakin kita tentang kategori mana setiap fungsi menjadi bagiannya.

Ada juga pertanyaan tentang cara paling efisien menyampaikan kepada pengguna segala sesuatu yang kita ketahui dan tidak ketahui tentang setiap fungsi dan setiap platform. Mungkin kita bisa membuat tabel dari semua fungsi kategori 2 dan 3 pada setiap platform. Alangkah baiknya jika tes determinisme dapat secara otomatis memverifikasi bahwa tabel ini benar.

Hanya brainstorming, mungkin ide-ide ini lebih sulit daripada nilainya. Rencana yang lebih pragmatis dapat secara signifikan lebih berkelanjutan, bahkan jika kurang ideal.

Apakah torch.add deterministik?

import torch
n = 512
device = 'cuda'
a = torch.arange(n**3, device=device, dtype=torch.float32)
a = a.reshape((n, n, n))
b = torch.arange(n**3, device=device, dtype=torch.float32)
b = b.reshape((n, n, n))
out_zero = torch.zeros((n, n, n), device=device)
out_zero = out_zero.set_(out_zero.storage(), storage_offset=0, size=a.size(), stride=(1,1,1))
out_one = torch.zeros((n, n, n), device=device)
out_one = out_one.set_(out_one.storage(), storage_offset=0, size=a.size(), stride=(1,1,1))

torch.add(a, b, out=out_zero)
torch.add(a, b, out=out_one)
(out_zero == out_one).all()
: tensor(False, device='cuda:0')

Kita mungkin harus mendokumentasikan bahwa tensor yang tumpang tindih melanggar kontrak determinisme apa pun yang kita tuju.

Mencantumkan operasi yang dipengaruhi oleh bendera "determinisme" terdengar bagus. Namun, melangkah mundur sedikit, sepertinya kita benar-benar berbicara tentang dua hal:

  • Meminta versi operasi deterministik, jika tersedia ( use_deterministic ?)
  • Peringatan jika suatu operasi nondeterministik

Bendera untuk hal pertama tampaknya mudah. Yang kedua, bagaimanapun, sedikit lebih rumit. Saya khawatir sulit untuk mengetahui apakah operasi perpustakaan matematika seperti oneDNN, cuDNN, dan MAGMA bersifat deterministik, terutama di seluruh versi dan perangkat keras. Apakah Anda punya ide tentang cara terbaik untuk mengatasi ini, @kurtamohler? Mungkin kita bisa memperingatkan semua operasi non-deterministik asli dan juga memperingatkan ketika panggilan perpustakaan matematika dibuat juga? Peringatan sekali per proses seharusnya tidak terlalu mengganggu.

Pendekatan peringatan ini memerlukan peninjauan banyak algoritme dan memanggil situs sebelum ditayangkan, tetapi tidak perlu memblokir tanda untuk memilih algoritme deterministik jika tersedia.

(Hal ketiga yang sedang dibahas adalah cara terbaik untuk menyajikan pemilihan algo deterministik (melalui flag global atau sebagai kwargs pada fungsi), tetapi saya pikir kita dapat menunda diskusi itu sampai kita menentukan rencana untuk flag?)

Saya pikir kita seharusnya tidak membiarkan yang sempurna menjadi musuh yang baik di sini. Saya tidak tahu kapan 100% aman untuk menggunakan tensor yang tumpang tindih dengan PyTorch, dan kesan saya adalah bahwa bukan orang biasa yang menggunakannya.

Kesan saya dari forum adalah bahwa kebanyakan orang terkejut bahwa mereka menjalankan sesuatu dua kali dan mendapatkan gradien yang berbeda darinya, paling sering karena salah satu fungsi asli PyTorch menggunakan atomAdd.
Jika kami mendapat peringatan untuk itu, kami telah membahas sebagian besar kasus yang membuat orang bertanya-tanya. Sesuatu yang terasa seperti setengahnya sebenarnya dari upscaling ke belakang.

Saya pikir kita harus dengan jelas menyatakan bahwa ini adalah upaya terbaik sejauh menyangkut lib eksternal dan bahwa kita menambahkan peringatan saat kita mengetahui masalah, tetapi kesan saya adalah bahwa kernel asli kita sebenarnya adalah yang paling penting.

Saya tidak tahu kapan 100% aman untuk menggunakan tensor yang tumpang tindih dengan PyTorch, dan kesan saya adalah bahwa bukan orang biasa yang menggunakannya.

Ya, dan program apa pun yang mungkin diklasifikasikan sebagai kesalahan. Maksud saya, kita harus berhati-hati untuk mendokumentasikan kontrak apa pun yang kita buat untuk bendera-bendera ini.

Saya pikir kita harus dengan jelas menyatakan bahwa ini adalah upaya terbaik sejauh menyangkut lib eksternal dan bahwa kita menambahkan peringatan saat kita mengetahui masalah ...

Doc mungkin mengatakan sesuatu seperti, "panggilan perpustakaan matematika yang diketahui tidak deterministik..."?

Saya setuju dengan @t-vi (dan saya sangat menyukai pengamatan bahwa setengah dari nondeterminisme yang dilaporkan meningkat ke belakang). Secara khusus, saya pikir keadaan di mana kami memiliki sebagian fungsi yang didokumentasikan yang diketahui nondeterministik (atau bahkan sebagian mendokumentasikan beberapa fungsi menjadi deterministik) benar-benar lebih baik daripada yang kami tidak memberikan indikasi sama sekali--hal utama adalah untuk tidak mengklaim mendukung hal-hal yang tidak kami lakukan! Saya setuju bahwa ini adalah aktivitas yang berguna untuk memikirkan tentang bagaimana seseorang dapat melakukan pengujian determinisme, tetapi saya pikir ini sebagai aktivitas ortogonal untuk menandai API yang jelas-jelas non-deterministik.

Karena banyak ide telah beredar, izinkan saya mengikuti pemikiran spesifik saya tentang beberapa di antaranya:

  1. "Mungkin kita bisa memikirkan untuk mempertahankan daftar fungsi kategori 3 juga." Ini sepertinya banyak pekerjaan. Saya pikir itu mungkin hanya layak untuk fungsi di mana kami secara eksplisit membuat beberapa akomodasi untuk determinisme (kemungkinan besar, fungsi yang mendukung bendera deterministik)
  2. "Kami berpotensi menyiapkan pekerjaan CI yang menjalankan tes determinisme pada semua fungsi." Saya pikir hal seperti ini harus dilakukan dengan sangat hati-hati, karena pada dasarnya itu adalah pengujian untuk sesuatu yang nondeterministik, dan itu berarti bahwa tes determinisme itu sendiri "terkelupas" (kadang-kadang akan lulus dan gagal yang lain) . Alat pelaporan CI kami tidak menangani situasi seperti ini dengan baik.
  3. "Namun, yang kedua sedikit lebih rumit. Saya khawatir sulit untuk mengetahui apakah operasi perpustakaan matematika seperti oneDNN, cuDNN, dan MAGMA bersifat deterministik, terutama di seluruh versi dan perangkat keras." Upaya terbaik ini harus kita lakukan. Dalam banyak kasus, perpustakaan matematika secara eksplisit menentukan dalam dokumentasi mereka bahwa mereka deterministik atau tidak, dan kita harus dengan setia melaporkan apa yang dikatakan dokumen
  4. "Mungkin kita bisa memperingatkan semua operasi non-deterministik asli dan juga memperingatkan ketika panggilan perpustakaan matematika dilakukan juga?" Saya tidak berpikir kita harus melakukan ini. Ketika kita memperingatkan tentang nondeterminisme, itu seharusnya karena nondeterminisme sedang terjadi, bukan karena MUNGKIN terjadi. Jika Anda memperingatkan, orang akan mulai mengabaikan peringatan.

Saya tidak berpikir kita harus khawatir tentang determinisme lintas versi/perangkat keras -- semoga berhasil.

Ketika kita memperingatkan tentang nondeterminisme, itu seharusnya karena nondeterminisme sedang terjadi, bukan karena MUNGKIN terjadi. Jika Anda memperingatkan, orang akan mulai mengabaikan peringatan.

memang terlihat rumit. Misalnya bagaimana jika saya menjalankan beberapa operasi dan implementasi PyTorch bersifat deterministik, tetapi beberapa ekstensi telah menimpa sesuatu (melalui kunci pengiriman, fungsi obor, atau lainnya) dan sekarang saya tidak tahu. Jika itu benar-benar sumber non-determinisme saya, sepertinya menyedihkan untuk tidak diperingatkan.

Jika itu benar-benar sumber non-determinisme saya, sepertinya menyedihkan untuk tidak diperingatkan.

Tentu, tetapi pengguna juga bisa saja tidak melibatkan kami dalam kejahatan nondeterministik mereka, dan tentu saja Anda tidak akan berharap untuk diperingatkan saat itu;)

Saya yakin kami dapat menutup masalah ini sekarang karena API bendera ada dan didokumentasikan dengan baik.

@kurtamohler Kerja yang luar biasa. Terima kasih.

Apakah ini berarti, kita dapat menggunakan torch.manual_seed(111) untuk mengatur semua deterministik, termasuk operasi interpolation ?

Tidak. Lihatlah catatan Reproduksibilitas / Keacakan .
Sejauh ini, kami memiliki infrastruktur, menandai sumber yang diketahui tentang non-determinisme dan dokumentasi yang sangat ditingkatkan sehingga Anda dapat mengetahui apa yang terjadi.
Jika Anda mencapai operasi non-deterministik, Anda masih kurang beruntung, tetapi sekarang lebih masuk akal untuk mengerjakannya.

Interpolasi khususnya tampaknya sesuatu yang dapat dibuat deterministik dengan menulis kernel yang tidak terlalu rumit untuk mundur.

@t-vi Hai, Sekarang pytorch 1.7 dirilis, apakah kernel interpolasi mundur telah diperbarui?

Jadi kernel upsampling CUDA dan mundur terletak di aten/src/ATen/native/cuda/UpSample* . Sebuah grep menunjukkan bahwa linear, bilinear, kubik memiliki mundur nondeterministik (mereka memiliki penanda peringatan), tetapi terdekat tidak.
@kurtamohler akan menjadi orang yang jauh lebih baik untuk bertanya.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat