Cara menulis pipeline machine learning yang rapi

Halo, Habr.



Topik pipelining dan paralelisasi machine learning telah menjadi pekerjaan kami sejak lama. Secara khusus, saya bertanya-tanya apakah buku khusus dengan penekanan pada Python sudah cukup untuk ini, atau jika diperlukan gambaran yang lebih luas dan mungkin literatur yang kompleks. Kami memutuskan untuk menerjemahkan artikel pengantar tentang Machine Learning Pipelines, yang mencakup pertimbangan arsitektural dan lebih banyak terapan. Mari kita bahas apakah penelusuran ke arah ini relevan.





Pernahkah Anda menulis pipeline machine learning yang membutuhkan waktu lama untuk dijalankan? Atau lebih buruk lagi: Apakah Anda sudah sampai pada titik di mana Anda perlu menyimpan bagian antara pipeline ke disk sehingga Anda dapat menjelajahi tahapan pipeline satu per satu, berdasarkan pos pemeriksaan? Atau lebih buruk lagi: pernahkah Anda mencoba mengubah kode pembelajaran mesin yang menjijikkan seperti itu sebelum memasukkan kode itu ke dalam produksi - dan ternyata butuh waktu berbulan-bulan? Ya, siapa pun yang telah lama mengerjakan pipeline machine learning harus berurusan dengan ini. Jadi mengapa tidak membangun jalur pipa yang baik yang memberi kita cukup fleksibilitas dan kemampuan untuk dengan mudah memfaktor ulang kode untuk pengiriman nanti ke produksi?



Pertama, mari kita tentukan pipeline machine learning dan diskusikan ide penggunaan breakpoint di antara tahapan pipeline. Kemudian mari kita lihat bagaimana kita dapat mengimplementasikan breakpoint tersebut agar tidak membuat diri kita terhambat saat memindahkan pipa ke dalam produksi. Kami juga akan membahas streaming data dan trade-off yang terkait dengan enkapsulasi pemrograman berorientasi objek (OOP) yang harus Anda lalui dalam pipeline saat menentukan hyperparameter.



APA ITU KONVEYOR?



KonveyorMerupakan urutan langkah-langkah dalam transformasi data. Itu dibuat sesuai dengan pola desain pipa-dan-filter lama (ingat, misalnya, perintah unix bash dengan "|" pipa atau ">" operator pengalihan). Namun, pipeline adalah objek dalam kode. Karenanya, Anda dapat memiliki kelas untuk setiap filter (yaitu, untuk setiap tahapan dalam pipeline), serta kelas lain untuk menggabungkan semua tahapan ini ke dalam pipeline jadi. Beberapa pipeline dapat menggabungkan pipeline lain secara seri atau paralel, memiliki banyak input atau output, dll. Lebih mudah untuk menganggap pipeline machine learning sebagai:



  • Saluran dan filter . Tahapan proses pipeline data, dan tahapan mengelola status internal mereka, yang dapat dipelajari dari data.
  • . ; , . , – .
  • (DAG). , . : , , , , (, fit_transform ), , ( RNN). , , .




Metode



Konveyor Konveyor (atau tahapan saluran pipa) harus memiliki dua metode berikut:



  • " Cocok " untuk melatih data dan memperoleh status (misalnya, status seperti itu adalah bobot jaringan neural)
  • " Transformasi " (atau "prediksi") untuk benar-benar memproses data dan menghasilkan prediksi.
  • Catatan: Jika tahapan pipeline tidak memerlukan salah satu metode ini, maka tahapan tersebut dapat mewarisi dari NonFittableMixin atau NonTransformableMixin, yang akan menyediakan implementasi salah satu metode ini secara default sehingga tidak melakukan apa pun.




Metode berikut juga dapat ditentukan secara opsional dalam tahapan pipeline :



  • fit_transform” , , , .
  • " Penyiapan " yang akan memanggil metode "penyiapan" di setiap tahapan pipeline ini. Misalnya, jika tahapan pipeline berisi jaringan neural TensorFlow, PyTorch, atau Keras, tahapan ini dapat membuat grafik neuralnya sendiri dan mendaftar untuk bekerja dengan GPU dalam metode "penyiapan" sebelum dipasang. Tidak disarankan untuk membuat grafik secara langsung di konstruktor panggung sebelum pemasangan; ada beberapa alasan untuk ini. Misalnya, sebelum memulai, langkah-langkah dapat disalin berkali-kali dengan hyperparameter berbeda sebagai bagian dari algoritme Pembelajaran Mesin Otomatis, yang mencari hyperparameter terbaik untuk Anda.
  • " Teardown ", metode ini secara fungsional kebalikan dari "setup": metode ini menghancurkan sumber daya.




Metode berikut disediakan secara default, menyediakan kontrol hyperparameter:







Pemasangan ulang pipeline, batch mini, dan pembelajaran online



Untuk algoritme yang menggunakan mini-batching, seperti mempelajari jaringan neural dalam (DNN), atau algoritme yang belajar online, seperti pembelajaran penguatan (RL), untuk pipeline atau tahapannya yang ideal sangat cocok untuk merantai beberapa panggilan agar mengikuti tepat satu demi satu, dan dengan cepat menyesuaikan dengan ukuran batch mini. Fitur ini didukung di beberapa pipeline dan di beberapa tahap pipeline, tetapi di beberapa tahap kecocokan yang dicapai dapat disetel ulang karena fakta bahwa metode "fit" akan dipanggil lagi. Itu semua tergantung pada bagaimana Anda memprogram tahap pipeline Anda. Idealnya, tahapan pipeline hanya boleh dibilas setelah memanggil metode "pembongkaran" dan kemudian memanggil "setupSampai kecocokan berikutnya, dan data tidak dibilas antara alat kelengkapan atau selama konversi.



MENGGUNAKAN CHECK POIN DI CONVEYORS



Merupakan praktik yang baik untuk menggunakan breakpoint dalam pipeline sampai Anda perlu menggunakan kode ini untuk tujuan lain dan mengubah data. Jika Anda tidak menggunakan abstraksi yang benar dalam kode Anda, Anda mungkin gagal.



Pro dan kontra menggunakan pos pemeriksaan di jalur pipa:



  • Breakpoint dapat mempercepat alur kerja jika langkah pemrograman dan debugging berada di tengah atau di akhir pipeline. Ini menghilangkan kebutuhan untuk menghitung ulang tahap pertama pipa setiap kali.
  • ( , ), , . , , – . , , , , , .
  • Mungkin Anda memiliki sumber daya komputasi yang terbatas, dan satu-satunya pilihan yang layak bagi Anda adalah menjalankan selangkah demi selangkah pada perangkat keras yang tersedia. Anda dapat menggunakan breakpoint, kemudian menambahkan beberapa langkah lagi setelahnya, dan kemudian data akan digunakan dari tempat Anda tinggalkan jika Anda ingin menjalankan ulang seluruh struktur.




Kerugian menggunakan breakpoint di pipelines:



  • Ini menggunakan disk, jadi jika Anda melakukannya dengan salah, kode Anda dapat melambat. Untuk mempercepat, Anda setidaknya dapat menggunakan RAM Disk atau memasang folder cache ke RAM Anda.
  • Ini bisa memakan banyak ruang disk. Atau banyak ruang RAM saat menggunakan direktori yang dipasang ke RAM.
  • Status yang disimpan di disk lebih sulit untuk dikelola: program Anda memiliki kompleksitas tambahan yang diperlukan untuk membuat kode berjalan lebih cepat. Perhatikan bahwa dari perspektif pemrograman fungsional, fungsi dan kode Anda tidak akan lagi bersih, karena Anda perlu mengelola efek samping yang terkait dengan penggunaan disk. Efek samping yang terkait dengan pengelolaan status disk (cache Anda) dapat menjadi tempat berkembang biak untuk semua jenis bug aneh
...



Beberapa bug tersulit dalam pemrograman diketahui muncul dari masalah pembatalan cache.



Dalam Ilmu Komputer, hanya ada dua hal yang sangat rumit: pembatalan cache dan penamaan entitas. - Phil Carlton




Saran tentang cara mengelola status dan cache di pipeline dengan benar.



Diketahui bahwa kerangka kerja pemrograman dan pola desain dapat menjadi faktor pembatas - karena alasan sederhana bahwa mereka mengatur aturan tertentu. Mudah-mudahan, ini dilakukan untuk menjaga tugas manajemen kode Anda sesederhana mungkin, sehingga Anda sendiri terhindar dari kesalahan dan kode Anda tidak berakhir berantakan. Berikut adalah lima sen saya tentang mendesain dalam konteks saluran pipa dan manajemen negara:



TAHAP KONVEYOR TIDAK HARUS MENGONTROL SETELAN TEST POINT DALAM DATA YANG DITERBITKAN OLEH




Untuk mengelolanya, perpustakaan pipelining khusus harus digunakan yang dapat melakukan semua ini untuk Anda.



Mengapa?



Mengapa tahapan pipeline tidak mengontrol penempatan pos pemeriksaan dalam data yang mereka hasilkan? Untuk alasan bagus yang sama saat Anda menggunakan pustaka atau kerangka kerja saat bekerja, dan tidak mereproduksi sendiri fungsionalitas yang sesuai:



  • Anda akan memiliki sakelar sakelar sederhana yang akan membuatnya mudah untuk sepenuhnya mengaktifkan atau menonaktifkan breakpoint sebelum menerapkan ke produksi.
  • , , , : , , . , .
  • / (I/O) . , . : , . ?
  • , , – . , , .
  • , , , , , , . , . .
  • , , (, , ) . , ( , ) . , , , , , , . , . , .




Itu keren. Dengan abstraksi yang tepat, Anda sekarang dapat memprogram pipeline machine learning untuk mempercepat fase penyetelan hyperparameter secara dramatis; untuk melakukan ini, Anda perlu meng-cache hasil perantara dari setiap pengujian, melewati tahapan pipeline berulang kali, saat hyperparameter tahapan pipeline perantara tetap tidak berubah. Selain itu, saat Anda siap untuk merilis kode ke produksi, Anda dapat langsung menonaktifkan cache sepenuhnya, daripada memfaktorkan ulang kode untuk ini selama sebulan penuh.



Jangan menabrak tembok ini.



STREAMING DATA DI CONVEYOR PEMBELAJARAN MESIN



Teori pemrosesan paralel menyatakan bahwa pipeline adalah alat streaming data yang memungkinkan Anda memparalelkan tahapan pipeline. Contoh laundrymenggambarkan dengan baik masalah ini dan solusinya. Misalnya, tahap kedua di pipeline mungkin mulai memproses informasi parsial dari tahap pertama pipeline, sedangkan tahap pertama terus menghitung data baru. Selain itu, agar pipeline tahap kedua berfungsi, tahap pertama tidak harus menyelesaikan tahap pemrosesan semua data sepenuhnya. Sebut saja streaming pipelines khusus ini (lihat di sini dan di sini ).



Jangan salah paham, bekerja dengan pipelines scikit-learn sangat menyenangkan. Tetapi mereka tidak diberi peringkat untuk streaming. Tidak hanya scikit-learn, tetapi sebagian besar library pipelined yang ada tidak memanfaatkan kapabilitas streaming saat mereka bisa. Ada masalah multithreading di seluruh ekosistem Python. Di sebagian besar pustaka pipelined, setiap tahap sepenuhnya memblokir dan memerlukan transformasi semua data sekaligus. Hanya ada beberapa perpustakaan streaming yang tersedia.



Mengaktifkan streaming bisa sesederhana menggunakan kelas, StreamingPipelinebukanPipelineuntuk menghubungkan tahapan satu per satu. Pada saat yang sama, ukuran tumpukan mini dan ukuran antrian ditunjukkan (untuk menghindari konsumsi RAM yang berlebihan, ini memastikan pekerjaan yang lebih stabil dalam produksi). Idealnya, struktur seperti itu juga akan membutuhkan antrian semaphore multithread , seperti yang dijelaskan dalam masalah pemasok-konsumen : untuk mengatur transfer informasi dari satu tahap pipeline ke tahap lainnya.



Di perusahaan kami, Neuraxle sudah melakukan satu hal yang lebih baik daripada scikit-learn: ini tentang pipeline berurutan yang dapat digunakan dengan kelas MiniBatchSequentialPipeline.... Sejauh ini, hal ini tidak multi-utas (tetapi ini ada dalam rencana). Minimal, ini sudah meneruskan data ke pipeline dalam bentuk tumpukan mini selama proses penyesuaian atau transformasi, sebelum mengumpulkan hasil, yang memungkinkan bekerja dengan pipeline besar seperti di scikit-learn , tapi kali ini menggunakan mini-batching, serta berbagai kemungkinan lainnya, termasuk: ruang hyperparameter, metode penginstalan, pembelajaran mesin otomatis, dan sebagainya.



Solusi Streaming Data Paralel kami dengan Python



  • Metode pemasangan dan / atau transformasi dapat dipanggil berkali-kali secara berurutan untuk meningkatkan pemasangan dengan tumpukan mini baru.
  • , -. , , .
  • , . , setup. , , . , TensorFlow, , , , C++, Python, GPU. joblib . .
  • , . , – , , , , .
  • . , , ; , . , , , , ( Joiner). , . , , , .




Selain itu, kami ingin memastikan bahwa objek apa pun dengan Python dapat dibagikan di antara utas sehingga dapat diserialkan dan dimuat ulang. Dalam kasus ini, kode dapat dikirim secara dinamis untuk diproses pada pekerja mana pun (dapat berupa komputer atau proses lain), meskipun kode yang diperlukan itu sendiri tidak ada pada pekerja ini. Ini dilakukan dengan menggunakan rangkaian serializers khusus untuk setiap kelas yang mewujudkan tahap pipeline. Secara default, setiap langkah ini memiliki serializer yang memungkinkan Anda memproses kode Python biasa, dan untuk kode yang lebih rumit, gunakan GPU dan impor kode dalam bahasa lain. Model hanya diserialkan menggunakan penabungnyadan kemudian dimuat ulang ke pekerja. Jika pekerja lokal, maka objek bisa diserialkan ke disk yang terletak di RAM, atau ke direktori yang dipasang di RAM.



KOMPROMI UNTUK INCAPSULASI



Ada satu lagi hal menjengkelkan yang melekat di sebagian besar pustaka untuk pembelajaran mesin pipelined. Ini tentang bagaimana hyperparameter ditangani. Ambil scikit-learn sebagai contoh. Ruang hyperparameter (alias distribusi statistik nilai hyperparameter ) sering kali perlu ditentukan di luar pipeline, dengan garis bawah sebagai pemisah antara tahapan pipeline. Sedangkan Pencarian Acak dan Pencarian Gridmemungkinkan Anda menjelajahi kisi hyperparameter atau ruang probabilitas hyperparameter seperti yang ditentukan dalam distribusi scipy , scikit-learn sendiri tidak menyediakan ruang hyperparameter default untuk setiap pengklasifikasi dan transformator. Tanggung jawab untuk menjalankan fungsi-fungsi ini dapat diberikan ke masing-masing objek dalam pipeline. Dengan demikian, objek akan mandiri dan akan berisi hyperparameternya sendiri. Hal ini tidak melanggar prinsip tanggung jawab tunggal, prinsip buka / tutup dan prinsip pemrograman berorientasi objek SOLID.



KOMPATIBILITAS DAN INTEGRASI Saat membuat kode



pipeline machine learning, perlu diingat bahwa keduanya harus kompatibel dengan banyak alat lain, khususnya scikit-learn., TensorFlow , Keras , PyTorch, dan banyak pustaka mesin dan pembelajaran mendalam lainnya.

Misalnya, kami menulis metode .tosklearn()yang memungkinkan kami mengubah tahapan pipeline atau seluruh pipeline menjadi BaseEstimatorobjek dasar pustaka scikit-learn. Sedangkan untuk pustaka pembelajaran mesin lainnya, tugasnya adalah menulis kelas baru yang diwarisi dari milik kita BaseStepdan menimpa dalam kode tertentu operasi pemasangan dan transformasi, serta, mungkin, menyetel dan menghancurkan. Anda juga perlu menentukan penghemat yang akan menyimpan dan memuat model Anda. Berikut adalah dokumentasi untuk kelas BaseStepdan contoh untuk kelas tersebut.



KESIMPULAN



Sebagai rangkuman, kami perhatikan bahwa kode pipeline machine learning, yang siap untuk diproduksi, harus memenuhi banyak kriteria kualitas, yang cukup dapat dicapai jika Anda mematuhi pola desain yang tepat dan menyusun kode dengan baik. Perhatikan hal-hal berikut:



  • Dalam kode machine learning, menggunakan pipeline, dan menentukan setiap tahap pipeline sebagai instance kelas merupakan hal yang wajar.
  • Seluruh struktur kemudian dapat dioptimalkan dengan breakpoint untuk membantu menemukan hyperparameter terbaik dan berulang kali mengeksekusi kode pada data yang sama (tetapi mungkin dengan hyperparameter berbeda atau kode sumber yang dimodifikasi).
  • , RAM. , .
  • , – BaseStep, , .



All Articles