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.
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:
- Menentukan beberapa target dengan dependensi. Saya ingin melihat bagaimana melakukannya dan betapa mudahnya.
- 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 -> CApakah targetCakan dibangun kembali jikaBberubah? Jika a?
- 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.
- 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 . 
hasilMemindai 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})  
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:
 
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.