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 -> C
Apakah target C
akan dibangun kembali jika B
berubah? 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.