Bangun alat dalam proyek pembelajaran mesin, gambaran umum

Saya bertanya-tanya tentang pembelajaran mesin / struktur proyek sains data / alur kerja dan membaca pendapat yang berbeda tentang subjek tersebut. Dan ketika orang mulai berbicara tentang alur kerja mereka ingin alur kerja mereka dapat direproduksi. Ada banyak posting di luar sana yang menyarankan untuk menggunakan make agar alur kerja dapat direproduksi. Meskipun make sangat stabil dan banyak digunakan, saya pribadi suka solusi lintas platform. Lagi pula, ini tahun 2019, bukan tahun 1977. Orang dapat berargumen bahwa membuat itu sendiri adalah cross-platform, tetapi pada kenyataannya Anda akan mengalami kesulitan dan akan menghabiskan waktu untuk memperbaiki alat Anda daripada melakukan pekerjaan yang sebenarnya. Jadi saya memutuskan untuk melihat-lihat dan memeriksa alat apa yang tersedia. Ya, saya memutuskan untuk meluangkan waktu pada alat.

gambar

Posting ini lebih merupakan undangan untuk dialog daripada tutorial. Mungkin solusi Anda sempurna. Jika ya maka akan menarik untuk mendengarnya.

Dalam posting ini saya akan menggunakan proyek Python kecil dan akan melakukan tugas otomasi yang sama dengan sistem yang berbeda:


Akan ada tabel perbandingan di akhir posting.

Sebagian besar alat saya akan melihat dikenal sebagai membangun perangkat lunak otomatisasi atau membangun sistem . Ada banyak sekali dari mereka dalam semua rasa, ukuran dan kompleksitas yang berbeda. Idenya sama: pengembang menetapkan aturan untuk menghasilkan beberapa hasil dengan cara yang otomatis dan konsisten. Misalnya, hasil mungkin berupa gambar dengan grafik. Untuk membuat gambar ini, seseorang harus mengunduh data, membersihkan data, dan melakukan beberapa manipulasi data (contoh klasik, sungguh). Anda dapat mulai dengan beberapa skrip shell yang akan melakukan pekerjaan. Setelah Anda kembali ke proyek setahun kemudian, akan sulit untuk mengingat semua langkah dan urutan yang harus Anda ambil untuk membuat gambar itu. Solusi yang jelas adalah mendokumentasikan semua langkah. Kabar baik! Membangun sistem memungkinkan Anda mendokumentasikan langkah-langkah dalam bentuk program komputer. Beberapa sistem build seperti skrip shell Anda, tetapi dengan lonceng dan peluit tambahan.

Landasan posting ini adalah serangkaian posting oleh Mateusz Bednarski tentang alur kerja otomatis untuk proyek pembelajaran mesin. Mateusz menjelaskan pandangannya dan menyediakan resep untuk menggunakan make . Saya mendorong Anda untuk pergi dan memeriksa posnya terlebih dahulu. Saya kebanyakan akan menggunakan kodenya, tetapi dengan sistem build yang berbeda.

Jika Anda ingin tahu lebih banyak tentang make , berikut ini adalah referensi untuk beberapa posting. Brooke Kennedy memberikan ikhtisar tingkat tinggi dalam 5 Langkah Mudah untuk Membuat Proyek Ilmu Data Anda Dapat Diproduksi. Zachary Jones memberikan rincian lebih lanjut tentang sintaks dan kapabilitas bersama dengan tautan ke pos lain. David Stevens menulis posting yang sangat hype tentang mengapa Anda benar-benar harus mulai menggunakan make segera. Dia memberikan contoh yang bagus membandingkan cara lama dan cara baru . Samuel Lampa , di sisi lain, menulis tentang mengapa menggunakan make adalah ide yang buruk.

Pilihan sistem bangun saya tidak komprehensif atau tidak bias. Jika Anda ingin membuat daftar, Wikipedia mungkin merupakan titik awal yang baik. Seperti yang dinyatakan di atas, saya akan membahas CMake , PyBuilder , pynt , Paver , doit dan Luigi . Sebagian besar alat dalam daftar ini berbasis python dan masuk akal karena proyek ini menggunakan Python. Posting ini tidak akan membahas cara menginstal alat. Saya berasumsi bahwa Anda cukup mahir dalam Python.

Saya sebagian besar tertarik untuk menguji fungsi ini:

  1. Menentukan beberapa target dengan dependensi. Saya ingin melihat bagaimana melakukannya dan betapa mudahnya.
  2. Memeriksa apakah build tambahan mungkin dilakukan. Ini berarti sistem build tidak akan membangun kembali apa yang belum diubah sejak dijalankan terakhir, yaitu Anda tidak perlu mengunduh ulang data mentah Anda. Hal lain yang saya akan cari adalah membangun bertahap ketika ketergantungan berubah. Bayangkan kita memiliki grafik dependensi A -> B -> C Apakah target C akan dibangun kembali jika B berubah? Jika a?
  3. Memeriksa apakah pembangunan kembali akan dipicu jika kode sumber diubah, mis. Kita mengubah parameter grafik yang dihasilkan, lain kali kita membangun gambar harus dibangun kembali.
  4. Memeriksa cara-cara membersihkan artefak build, yaitu menghapus file yang telah dibuat selama build dan memutar kembali ke kode sumber bersih.

Saya tidak akan menggunakan semua target pembangunan dari pos Mateusz, hanya tiga di antaranya untuk menggambarkan prinsip-prinsip tersebut.

Semua kode tersedia di GitHub .

CMake


CMake adalah generator skrip build, yang menghasilkan file input untuk berbagai sistem build. Dan namanya adalah singkatan dari cross-platform make. CMake adalah alat rekayasa perangkat lunak. Perhatian utama adalah tentang membangun executable dan perpustakaan. Jadi CMake tahu bagaimana membangun target dari kode sumber dalam bahasa yang didukung. CMake dieksekusi dalam dua langkah: konfigurasi dan generasi. Selama konfigurasi dimungkinkan untuk mengkonfigurasi bangunan masa depan sesuai dengan satu kebutuhan. Misalnya, variabel yang disediakan pengguna diberikan selama langkah ini. Pembuatan file biasanya mudah dan menghasilkan file yang dapat digunakan untuk membangun sistem. Dengan CMake, Anda masih bisa menggunakan make , tetapi alih-alih menulis makefile secara langsung, Anda menulis file CMake, yang akan menghasilkan makefile untuk Anda.

Konsep penting lainnya adalah bahwa CMake mendorong pembangunan di luar sumber . Pembuatan di luar sumber menjauhkan kode sumber dari artefak apa pun yang dihasilkannya. Ini masuk akal bagi executable di mana basis kode sumber tunggal dapat dikompilasi di bawah arsitektur CPU yang berbeda dan sistem operasi. Namun, pendekatan ini dapat bertentangan dengan cara kerja banyak ilmuwan data. Menurut saya komunitas sains data cenderung memiliki data, kode, dan hasil yang tinggi.

Mari kita lihat apa yang kita butuhkan untuk mencapai tujuan kita dengan CMake. Ada dua kemungkinan untuk mendefinisikan hal-hal khusus di CMake: target kustom dan perintah kustom. Sayangnya kita perlu menggunakan keduanya, yang menghasilkan lebih banyak pengetikan dibandingkan dengan vanila makefile. Target khusus dianggap selalu ketinggalan zaman, yaitu jika ada target untuk mengunduh data mentah, CMake akan selalu mengunduh ulang. Kombinasi perintah khusus dengan target kustom memungkinkan untuk menjaga target tetap terbaru.

Untuk proyek kami, kami akan membuat file bernama CMakeLists.txt dan meletakkannya di root proyek. Mari kita periksa isinya:

 cmake_minimum_required(VERSION 3.14.0 FATAL_ERROR) project(Cmake_in_ml VERSION 0.1.0 LANGUAGES NONE) 

Bagian ini mendasar. Baris kedua mendefinisikan nama proyek Anda, versi, dan menentukan bahwa kami tidak akan menggunakan dukungan bahasa bawaan apa pun (karena kami akan memanggil skrip Python).

Target pertama kami akan mengunduh dataset IRIS:

 SET(IRIS_URL "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data" CACHE STRING "URL to the IRIS data") set(IRIS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/data/raw) set(IRIS_FILE ${IRIS_DIR}/iris.csv) ADD_CUSTOM_COMMAND(OUTPUT ${IRIS_FILE} COMMAND ${CMAKE_COMMAND} -E echo "Downloading IRIS." COMMAND python src/data/download.py ${IRIS_URL} ${IRIS_FILE} COMMAND ${CMAKE_COMMAND} -E echo "Done. Checkout ${IRIS_FILE}." WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) ADD_CUSTOM_TARGET(rawdata ALL DEPENDS ${IRIS_FILE}) 

Baris pertama mendefinisikan parameter IRIS_URL , yang diekspos kepada pengguna selama langkah konfigurasi. Jika Anda menggunakan CMake GUI, Anda dapat mengatur variabel ini melalui GUI:



Selanjutnya, kami mendefinisikan variabel dengan lokasi unduhan dataset IRIS. Kemudian kami menambahkan perintah khusus, yang akan menghasilkan IRIS_FILE sebagai outputnya. Pada akhirnya, kami menetapkan rawdata target kustom yang bergantung pada IRIS_FILE berarti bahwa untuk membangun rawdata IRIS_FILE harus dibangun. Opsi ALL dari target kustom mengatakan bahwa rawdata akan menjadi salah satu target default untuk dibangun. Perhatikan bahwa saya menggunakan CMAKE_CURRENT_SOURCE_DIR untuk menjaga data yang diunduh di folder sumber dan tidak di folder build. Ini hanya untuk membuatnya sama dengan Mateusz.

Baiklah, mari kita lihat bagaimana kita bisa menggunakannya. Saat ini saya menjalankannya di WIndows dengan kompiler MinGW yang diinstal. Anda mungkin perlu menyesuaikan pengaturan generator untuk kebutuhan Anda (jalankan cmake --help untuk melihat daftar generator yang tersedia). Jalankan terminal dan buka folder induk dari kode sumber, lalu:

 mkdir overcome-the-chaos-build cd overcome-the-chaos-build cmake -G "MinGW Makefiles" ../overcome-the-chaos 

hasil
- Konfigurasi selesai
- Menghasilkan selesai
- File Build telah ditulis ke: C: / home / workspace / mengatasi-kekacauan-build

Dengan CMake modern kita dapat membangun proyek langsung dari CMake. Perintah ini akan memanggil build all command:

 cmake --build . 

hasil
Memindai dependensi rawdata target
[100%] Membangun rawdata target

Kami juga dapat melihat daftar target yang tersedia:

 cmake --build . --target help 

Dan kita dapat menghapus file yang diunduh dengan:

 cmake --build . --target clean 

Lihat bahwa kita tidak perlu membuat target bersih secara manual.

Sekarang mari kita pindah ke target berikutnya - data IRIS yang telah diproses. Mateusz membuat dua file dari satu fungsi: processed.pickle dan processed.xlsx . Anda dapat melihat bagaimana ia pergi dengan membersihkan file Excel ini dengan menggunakan rm dengan wildcard. Saya pikir ini bukan pendekatan yang sangat baik. Di CMake, kami memiliki dua opsi tentang cara menghadapinya. Opsi pertama adalah menggunakan properti direktori ADDITIONAL_MAKE_CLEAN_FILES . Kode tersebut akan:

 SET(PROCESSED_FILE ${CMAKE_CURRENT_SOURCE_DIR}/data/processed/processed.pickle) ADD_CUSTOM_COMMAND(OUTPUT ${PROCESSED_FILE} COMMAND python src/data/preprocess.py ${IRIS_FILE} ${PROCESSED_FILE} --excel data/processed/processed.xlsx WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS rawdata ${IRIS_FILE} ) ADD_CUSTOM_TARGET(preprocess DEPENDS ${PROCESSED_FILE}) # Additional files to clean set_property(DIRECTORY PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/processed/processed.xlsx ) 

Opsi kedua adalah menentukan daftar file sebagai output perintah khusus:

 LIST(APPEND PROCESSED_FILE "${CMAKE_CURRENT_SOURCE_DIR}/data/processed/processed.pickle" "${CMAKE_CURRENT_SOURCE_DIR}/data/processed/processed.xlsx" ) ADD_CUSTOM_COMMAND(OUTPUT ${PROCESSED_FILE} COMMAND python src/data/preprocess.py ${IRIS_FILE} data/processed/processed.pickle --excel data/processed/processed.xlsx WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS rawdata ${IRIS_FILE} src/data/preprocess.py ) ADD_CUSTOM_TARGET(preprocess DEPENDS ${PROCESSED_FILE}) 

Lihat bahwa dalam kasus ini saya membuat daftar, tetapi tidak menggunakannya di dalam perintah kustom. Saya tidak tahu cara untuk referensi argumen keluaran perintah kustom di dalamnya.

Hal lain yang menarik untuk diperhatikan adalah penggunaan depends dalam perintah khusus ini. Kami menetapkan ketergantungan tidak hanya dari target kustom, tetapi juga output dan skrip python. Jika kami tidak menambahkan ketergantungan pada IRIS_FILE , memodifikasi iris.csv secara manual tidak akan menghasilkan pembangunan kembali target preprocess . Yah, Anda tidak boleh memodifikasi file di direktori build Anda secara manual di tempat pertama. Hanya memberi tahu Anda. Lebih detail di pos Sam Thursfield . Ketergantungan pada skrip python diperlukan untuk membangun kembali target jika skrip python berubah.

Dan akhirnya target ketiga:

 SET(EXPLORATORY_IMG ${CMAKE_CURRENT_SOURCE_DIR}/reports/figures/exploratory.png) ADD_CUSTOM_COMMAND(OUTPUT ${EXPLORATORY_IMG} COMMAND python src/visualization/exploratory.py ${PROCESSED_FILE} ${EXPLORATORY_IMG} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${PROCESSED_FILE} src/visualization/exploratory.py ) ADD_CUSTOM_TARGET(exploratory DEPENDS ${EXPLORATORY_IMG}) 

Target ini pada dasarnya sama dengan yang kedua.

Untuk menyelesaikan. CMake terlihat berantakan dan lebih sulit daripada Make. Memang, banyak orang mengkritik CMake karena sintaksisnya. Dalam pengalaman saya, pemahaman akan datang dan benar-benar mungkin untuk memahami bahkan file CMake yang sangat rumit.

Anda masih akan melakukan banyak pengeleman karena Anda harus melewati variabel yang benar. Saya tidak melihat cara mudah merujuk keluaran dari satu perintah kustom di perintah lain. Sepertinya itu mungkin dilakukan melalui target kustom.

Pybuilder


Bagian PyBuilder sangat pendek. Saya menggunakan Python 3.7 dalam proyek saya dan PyBuilder versi saat ini 0.11.17 tidak mendukungnya. Solusi yang diusulkan adalah menggunakan versi pengembangan. Namun versi itu terbatas pada pip v9. Pip adalah v19.3 pada saat penulisan. Nyebelin. Setelah mengotak-atiknya sedikit, itu tidak berhasil sama sekali bagi saya. Evaluasi PyBuilder adalah yang berumur pendek.

pynt


Pynt adalah berbasis python, yang berarti kita dapat menggunakan fungsi python secara langsung. Tidak perlu membungkusnya dengan klik dan untuk menyediakan antarmuka baris perintah. Namun, pynt juga mampu menjalankan perintah shell. Saya akan menggunakan fungsi python.

Perintah build diberikan dalam file build.py . Target / tugas dibuat dengan dekorator fungsi. Ketergantungan tugas diberikan melalui dekorator yang sama.

Karena saya ingin menggunakan fungsi python, saya perlu mengimpornya dalam skrip build. Pynt tidak menyertakan direktori saat ini sebagai skrip python, jadi menulis sesuatu seperti ini:

 from src.data.download import pydownload_file 

tidak akan bekerja Yang harus kita lakukan:

 import os import sys sys.path.append(os.path.join(os.path.dirname(__file__), '.')) from src.data.download import pydownload_file 

File build.py awal saya seperti ini:

 #!/usr/bin/python import os import sys sys.path.append(os.path.join(os.path.dirname(__file__), '.')) from pynt import task from path import Path import glob from src.data.download import pydownload_file from src.data.preprocess import pypreprocess iris_file = 'data/raw/iris.csv' processed_file = 'data/processed/processed.pickle' @task() def rawdata(): '''Download IRIS dataset''' pydownload_file('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data', iris_file) @task() def clean(): '''Clean all build artifacts''' patterns = ['data/raw/*.csv', 'data/processed/*.pickle', 'data/processed/*.xlsx', 'reports/figures/*.png'] for pat in patterns: for fl in glob.glob(pat): Path(fl).remove() @task(rawdata) def preprocess(): '''Preprocess IRIS dataset''' pypreprocess(iris_file, processed_file, 'data/processed/processed.xlsx') 

Dan target preprocess tidak berhasil. Itu terus-menerus mengeluh tentang argumen input fungsi pypreprocess . Sepertinya Pynt tidak menangani argumen fungsi opsional dengan sangat baik. Saya harus menghapus argumen untuk membuat file excel. Ingatlah ini jika proyek Anda memiliki fungsi dengan argumen opsional.

Kami dapat menjalankan pynt dari folder proyek dan mendaftar semua target yang tersedia:

 pynt -l 

hasil
 Tasks in build file build.py: clean Clean all build artifacts exploratory Make an image with pairwise distribution preprocess Preprocess IRIS dataset rawdata Download IRIS dataset Powered by pynt 0.8.2 - A Lightweight Python Build Tool. 


Mari kita membuat distribusi berpasangan:

 pynt exploratory 

hasil
 [ build.py - Starting task "rawdata" ] Downloading from https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data to data/raw/iris.csv [ build.py - Completed task "rawdata" ] [ build.py - Starting task "preprocess" ] Preprocessing data [ build.py - Completed task "preprocess" ] [ build.py - Starting task "exploratory" ] Plotting pairwise distribution... [ build.py - Completed task "exploratory" ] 


Jika sekarang kita menjalankan perintah yang sama lagi (mis. pynt exploratory ) akan ada pembangunan kembali secara penuh. Pynt tidak melacak bahwa tidak ada yang berubah.

Paver


Paver terlihat hampir persis seperti Pynt. Ini sedikit berbeda dengan cara seseorang mendefinisikan ketergantungan antara target (penghias lainnya @needs ). Paver membuat pembangunan kembali penuh setiap kali dan tidak bermain dengan baik dengan fungsi yang memiliki argumen opsional. Instruksi pembuatan dapat ditemukan di file pavement.py .

lakukan


Doit sepertinya merupakan upaya untuk membuat alat otomasi yang benar-benar dibangun dengan python. Itu dapat mengeksekusi kode python dan perintah shell. Terlihat cukup menjanjikan. Apa yang tampaknya terlewatkan (dalam konteks tujuan khusus kami) adalah kemampuan untuk menangani ketergantungan antar target. Katakanlah kita ingin membuat pipa kecil di mana output dari target A digunakan sebagai input dari target B. Dan katakanlah kita menggunakan file sebagai output, jadi target A membuat file bernama outA .



Untuk membuat pipeline seperti itu, kita perlu menentukan file outA dua kali dalam target A (sebagai hasil dari target, tetapi juga mengembalikan namanya sebagai bagian dari eksekusi target). Maka kita perlu menentukannya sebagai input untuk target B. Jadi ada 3 tempat secara total di mana kita perlu memberikan informasi tentang file outA . Dan bahkan setelah kita melakukannya, modifikasi file outA tidak akan mengarah ke pembangunan kembali otomatis target B. Ini berarti bahwa jika kita meminta doit untuk membangun target B, doit hanya akan memeriksa apakah target B up-to-date tanpa memeriksa apa pun dari dependensi. Untuk mengatasinya, kita perlu menentukan outA 4 kali - juga sebagai ketergantungan file target B. Saya melihat ini sebagai kekurangan. Baik Make dan CMake mampu menangani situasi seperti itu dengan benar.

Ketergantungan dalam doit berbasis file dan dinyatakan sebagai string. Ini berarti dependensi ./myfile.txt dan myfile.txt dipandang berbeda. Seperti yang saya tulis di atas, saya menemukan cara menyampaikan informasi dari target ke target (ketika menggunakan target python) agak aneh. Target memiliki daftar artefak yang akan diproduksi, tetapi target lain tidak dapat menggunakannya. Sebaliknya fungsi python, yang merupakan target, harus mengembalikan kamus, yang dapat diakses di target lain. Mari kita lihat pada contoh:

 def task_preprocess(): """Preprocess IRIS dataset""" pickle_file = 'data/processed/processed.pickle' excel_file = 'data/processed/processed.xlsx' return { 'file_dep': ['src/data/preprocess.py'], 'targets': [pickle_file, excel_file], 'actions': [doit_pypreprocess], 'getargs': {'input_file': ('rawdata', 'filename')}, 'clean': True, } 

Di sini preprocess target tergantung pada data rawdata . Ketergantungan diberikan melalui properti getargs . Dikatakan bahwa argumen input_file dari fungsi doit_pypreprocess adalah filename keluaran dari rawdata target. Lihat contoh lengkap di file dodo.py.

Mungkin ada baiknya membaca kisah sukses menggunakan doit. Ini pasti memiliki fitur-fitur bagus seperti kemampuan untuk memberikan pemeriksaan target kustom terbaru.

Luigi


Luigi tetap terpisah dari alat-alat lain karena merupakan sistem untuk membangun jaringan pipa yang kompleks. Itu muncul di radar saya setelah seorang rekan mengatakan kepada saya bahwa ia mencoba Make, tidak pernah dapat menggunakannya di Windows / Linux dan pindah ke Luigi.

Luigi bertujuan untuk sistem yang siap produksi. Itu datang dengan server, yang dapat digunakan untuk memvisualisasikan tugas Anda atau untuk mendapatkan riwayat pelaksanaan tugas. Server disebut pusat schedler . Penjadwal lokal tersedia untuk tujuan debugging.

Luigi juga berbeda dari sistem lain dengan cara bagaimana tugas dibuat. Lugi tidak bertindak pada beberapa file yang telah ditentukan (seperti dodo.py , pavement.py atau makefile). Sebaliknya, kita harus melewati nama modul python. Jadi, jika kita mencoba menggunakannya dengan cara yang mirip dengan alat lain (menempatkan file dengan tugas di root proyek), itu tidak akan berfungsi. Kita harus menginstal proyek kita atau memodifikasi variabel lingkungan PYTHONPATH dengan menambahkan path ke proyek.

Apa yang hebat tentang luigi adalah cara menentukan ketergantungan antar tugas. Setiap tugas adalah kelas. output metode memberi tahu Luigi di mana hasil tugas akan berakhir. Hasil dapat berupa elemen tunggal atau daftar. Metode requires menentukan dependensi tugas (tugas lain; walaupun dimungkinkan untuk membuat dependensi dari dirinya sendiri). Dan itu saja. Apa pun yang ditentukan sebagai output dalam tugas A akan diteruskan sebagai input ke tugas B jika tugas B bergantung pada tugas A.


Luigi tidak peduli dengan modifikasi file. Itu peduli tentang keberadaan file. Jadi tidak mungkin untuk memicu pembangunan kembali ketika kode sumber berubah. Luigi tidak memiliki fungsionalitas bersih bawaan.

Tugas Luigi untuk proyek ini tersedia dalam file luigitasks.py . Saya menjalankannya dari terminal:

 luigi --local-scheduler --module luigitasks Exploratory 

Perbandingan


Tabel di bawah ini merangkum bagaimana sistem yang berbeda bekerja sehubungan dengan tujuan spesifik kami.
Tetapkan target dengan ketergantunganMembangun bertahapBuild tambahan jika kode sumber diubahKemampuan untuk mencari tahu artefak mana yang harus dihapus selama perintah clean
CMakeiyaiyaiyaiya
Pyntiyatidaktidaktidak
Paveriyatidaktidaktidak
lakukanAgaknya yaiyaiyaiya
Luigiiyatidaktidaktidak

Source: https://habr.com/ru/post/id451962/


All Articles