Cree herramientas en proyectos de aprendizaje autom谩tico, una descripci贸n general

Me preguntaba sobre la estructura del proyecto de aprendizaje autom谩tico / ciencia de datos / flujo de trabajo y estaba leyendo diferentes opiniones sobre el tema. Y cuando las personas comienzan a hablar sobre el flujo de trabajo, quieren que sus flujos de trabajo sean reproducibles. Hay muchas publicaciones que sugieren usar make para mantener el flujo de trabajo reproducible. Aunque make es muy estable y ampliamente utilizado, personalmente me gustan las soluciones multiplataforma. Despu茅s de todo, es 2019, no 1977. Se puede argumentar que hacerse es multiplataforma, pero en realidad tendr谩 problemas y pasar谩 tiempo arreglando su herramienta en lugar de hacer el trabajo real. As铆 que decid铆 echar un vistazo y ver qu茅 otras herramientas est谩n disponibles. S铆, decid铆 dedicar algo de tiempo a las herramientas.

imagen

Esta publicaci贸n es m谩s una invitaci贸n a un di谩logo que un tutorial. Quiz谩s tu soluci贸n sea perfecta. Si es as铆, ser谩 interesante escucharlo.

En esta publicaci贸n usar茅 un peque帽o proyecto de Python y har茅 las mismas tareas de automatizaci贸n con diferentes sistemas:


Habr谩 una tabla de comparaci贸n al final de la publicaci贸n.

La mayor铆a de las herramientas que analizar茅 se conocen como software de automatizaci贸n de compilaci贸n o sistemas de compilaci贸n . Hay miles de ellos en todos los sabores, tama帽os y complejidades. La idea es la misma: el desarrollador define reglas para producir algunos resultados de manera automatizada y consistente. Por ejemplo, un resultado podr铆a ser una imagen con un gr谩fico. Para hacer esta imagen, uno necesitar铆a descargar los datos, limpiar los datos y hacer algunas manipulaciones de datos (ejemplo cl谩sico, realmente). Puede comenzar con un par de scripts de shell que har谩n el trabajo. Una vez que regrese al proyecto un a帽o despu茅s, ser谩 dif铆cil recordar todos los pasos y el orden que debe seguir para crear esa imagen. La soluci贸n obvia es documentar todos los pasos. Buenas noticias! Los sistemas de construcci贸n le permiten documentar los pasos en forma de programa de computadora. Algunos sistemas de compilaci贸n son como sus scripts de shell, pero con campanas y silbatos adicionales.

La base de esta publicaci贸n es una serie de publicaciones de Mateusz Bednarski sobre flujo de trabajo automatizado para un proyecto de aprendizaje autom谩tico. Mateusz explica sus puntos de vista y proporciona recetas para usar make . Te animo a que vayas y revises sus publicaciones primero. Usar茅 principalmente su c贸digo, pero con diferentes sistemas de compilaci贸n.

Si desea obtener m谩s informaci贸n sobre make , a continuaci贸n encontrar谩 referencias para un par de publicaciones. Brooke Kennedy ofrece una descripci贸n general de alto nivel en 5 sencillos pasos para hacer que su proyecto de ciencia de datos sea reproducible. Zachary Jones brinda m谩s detalles sobre la sintaxis y las capacidades junto con los enlaces a otras publicaciones. David Stevens escribe una publicaci贸n muy publicitaria sobre por qu茅 absolutamente tienes que comenzar a usar make inmediato. Proporciona buenos ejemplos que comparan la manera antigua y la nueva . Samuel Lampa , por otro lado, escribe sobre por qu茅 usar make es una mala idea.

Mi selecci贸n de sistemas de compilaci贸n no es exhaustiva ni imparcial. Si desea hacer su lista, Wikipedia podr铆a ser un buen punto de partida. Como se indic贸 anteriormente, cubrir茅 CMake , PyBuilder , pynt , Paver , doit y Luigi . La mayor铆a de las herramientas en esta lista est谩n basadas en Python y tiene sentido ya que el proyecto est谩 en Python. Esta publicaci贸n no cubrir谩 c贸mo instalar las herramientas. Supongo que eres bastante competente en Python.

Estoy principalmente interesado en probar esta funcionalidad:

  1. Especificar un par de objetivos con dependencias. Quiero ver c贸mo hacerlo y lo f谩cil que es.
  2. Verificando si son posibles las construcciones incrementales. Esto significa que el sistema de compilaci贸n no reconstruir谩 lo que no ha cambiado desde la 煤ltima ejecuci贸n, es decir, no necesita volver a descargar sus datos sin procesar. Otra cosa que buscar茅 es compilaciones incrementales cuando cambie la dependencia. Imagina que tenemos un gr谩fico de dependencias A -> B -> C 驴Se reconstruir谩 el objetivo C si B cambia? Si a?
  3. Verificando si la reconstrucci贸n se activar谩 si se cambia el c贸digo fuente, es decir, si cambiamos el par谩metro del gr谩fico generado, la pr贸xima vez que construyamos la imagen debe reconstruirse.
  4. Verificando las formas de limpiar los artefactos de compilaci贸n, es decir, eliminar los archivos que se crearon durante la compilaci贸n y volver al c贸digo fuente limpio.

No usar茅 todos los objetivos de compilaci贸n de la publicaci贸n de Mateusz, solo tres de ellos para ilustrar los principios.

Todo el c贸digo est谩 disponible en GitHub .

CMake


CMake es un generador de scripts de compilaci贸n, que genera archivos de entrada para varios sistemas de compilaci贸n. Y su nombre significa marca multiplataforma. CMake es una herramienta de ingenier铆a de software. Su principal preocupaci贸n es la construcci贸n de ejecutables y bibliotecas. Entonces CMake sabe c贸mo construir objetivos a partir del c贸digo fuente en los idiomas compatibles. CMake se ejecuta en dos pasos: configuraci贸n y generaci贸n. Durante la configuraci贸n, es posible configurar la compilaci贸n futura seg煤n las necesidades. Por ejemplo, las variables proporcionadas por el usuario se dan durante este paso. La generaci贸n normalmente es sencilla y produce archivos con los que los sistemas de compilaci贸n pueden trabajar. Con CMake, a煤n puede usar make , pero en lugar de escribir makefile directamente, escribe un archivo CMake, que generar谩 el makefile por usted.

Otro concepto importante es que CMake fomenta las compilaciones fuera de la fuente . Las compilaciones fuera de la fuente mantienen el c贸digo fuente alejado de cualquier artefacto que produzca. Esto tiene mucho sentido para los ejecutables donde la base de c贸digo de fuente 煤nica puede compilarse bajo diferentes arquitecturas de CPU y sistemas operativos. Sin embargo, este enfoque puede contradecir la forma en que trabajan muchos cient铆ficos de datos. Me parece que la comunidad de ciencia de datos tiende a tener un alto acoplamiento de datos, c贸digo y resultados.

Veamos qu茅 necesitamos para lograr nuestros objetivos con CMake. Hay dos posibilidades para definir cosas personalizadas en CMake: objetivos personalizados y comandos personalizados. Desafortunadamente, necesitaremos usar ambos, lo que da como resultado una mayor tipificaci贸n en comparaci贸n con el archivo MAKE vanila. Se considera que un objetivo personalizado siempre est谩 desactualizado, es decir, si hay un objetivo para descargar datos sin procesar, CMake siempre lo volver谩 a descargar. Una combinaci贸n de comando personalizado con objetivo personalizado permite mantener los objetivos actualizados.

Para nuestro proyecto crearemos un archivo llamado CMakeLists.txt y lo colocaremos en la ra铆z del proyecto. Veamos el contenido:

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

Esta parte es b谩sica. La segunda l铆nea define el nombre de su proyecto, versi贸n y especifica que no usaremos ning煤n soporte de lenguaje incorporado (llamaremos scripts Python).

Nuestro primer objetivo descargar谩 el conjunto de datos 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}) 

La primera l铆nea define el par谩metro IRIS_URL , que se expone al usuario durante el paso de configuraci贸n. Si usa CMake GUI, puede establecer esta variable a trav茅s de la GUI:



A continuaci贸n, definimos variables con la ubicaci贸n descargada del conjunto de datos IRIS. Luego agregamos un comando personalizado, que producir谩 IRIS_FILE como su salida. Al final, definimos un rawdata destino personalizado que depende de IRIS_FILE lo que significa que para construir rawdata IRIS_FILE debe construir rawdata . La opci贸n ALL del objetivo personalizado dice que los datos rawdata ser谩n uno de los objetivos predeterminados para construir. Tenga en cuenta que uso CMAKE_CURRENT_SOURCE_DIR para mantener los datos descargados en la carpeta de origen y no en la carpeta de compilaci贸n. Esto es solo para que sea lo mismo que Mateusz.

Muy bien, veamos c贸mo podemos usarlo. Actualmente lo estoy ejecutando en Windows con el compilador MinGW instalado. Es posible que deba ajustar la configuraci贸n del generador seg煤n sus necesidades (ejecute cmake --help para ver la lista de generadores disponibles). Encienda la terminal y vaya a la carpeta principal del c贸digo fuente, luego:

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

resultado
- Configuraci贸n realizada
- Generando hecho
- Los archivos de compilaci贸n se han escrito en: C: / home / workspace / superar-el-caos-build

Con CMake moderno podemos construir el proyecto directamente desde CMake. Este comando invocar谩 el comando build all :

 cmake --build . 

resultado
An谩lisis de dependencias de rawdata de destino
[100%] Datos de destino construidos

Tambi茅n podemos ver la lista de objetivos disponibles:

 cmake --build . --target help 

Y podemos eliminar el archivo descargado por:

 cmake --build . --target clean 

Vea que no necesitamos crear el objetivo limpio manualmente.

Ahora pasemos al siguiente objetivo: datos IRIS preprocesados. Mateusz crea dos archivos desde una sola funci贸n: processed.pickle y processed.xlsx . Puede ver c贸mo se va con la limpieza de este archivo de Excel utilizando rm con comod铆n. Creo que este no es un muy buen enfoque. En CMake, tenemos dos opciones de c贸mo tratarlo. La primera opci贸n es usar la propiedad de directorio ADDITIONAL_MAKE_CLEAN_FILES . El c贸digo ser谩:

 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 ) 

La segunda opci贸n es especificar una lista de archivos como salida de comando personalizada:

 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}) 

Vea que en este caso cre茅 la lista, pero no la utilic茅 dentro del comando personalizado. No conozco una manera de hacer referencia a los argumentos de salida del comando personalizado dentro de 茅l.

Otra cosa interesante a tener en cuenta es que el uso de depends en este comando personalizado. Establecemos la dependencia no solo de un objetivo personalizado, sino tambi茅n su salida y el script de Python. Si no agregamos dependencia a IRIS_FILE , la modificaci贸n manual de iris.csv no dar谩 como resultado la reconstrucci贸n del objetivo IRIS_FILE al IRIS_FILE . Bueno, no debes modificar los archivos en tu directorio de compilaci贸n manualmente en primer lugar. Solo dej谩ndote saber. M谩s detalles en la publicaci贸n de Sam Thursday . La dependencia del script python es necesaria para reconstruir el destino si el script python cambia.

Y finalmente el tercer objetivo:

 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}) 

Este objetivo es b谩sicamente el mismo que el segundo.

Para terminar. CMake parece desordenado y m谩s dif铆cil que Make. De hecho, mucha gente critica a CMake por su sintaxis. En mi experiencia, la comprensi贸n llegar谩 y es absolutamente posible dar sentido incluso a los archivos CMake muy complicados.

Todav铆a se pegar谩 mucho, ya que deber谩 pasar las variables correctas. No veo una manera f谩cil de hacer referencia a la salida de un comando personalizado en otro. Parece que es posible hacerlo a trav茅s de objetivos personalizados.

Pybuilder


La parte de PyBuilder es muy corta. Utilic茅 Python 3.7 en mi proyecto y la versi贸n actual de PyBuilder 0.11.17 no lo admite. La soluci贸n propuesta es usar la versi贸n de desarrollo. Sin embargo, esa versi贸n est谩 limitada a pip v9. Pip es v19.3 en el momento de la escritura. Bummer Despu茅s de jugar un poco con eso, no me funcion贸 en absoluto. La evaluaci贸n de PyBuilder fue de corta duraci贸n.

pynt


Pynt est谩 basado en Python, lo que significa que podemos usar las funciones de Python directamente. No es necesario ajustarlos con un clic y proporcionar una interfaz de l铆nea de comandos. Sin embargo, pynt tambi茅n es capaz de ejecutar comandos de shell. Usar茅 las funciones de Python.

Los comandos de compilaci贸n se dan en un archivo build.py . Los objetivos / tareas se crean con decoradores de funciones. Las dependencias de tareas se proporcionan a trav茅s del mismo decorador.

Como me gustar铆a usar las funciones de Python, necesito importarlas en el script de compilaci贸n. Pynt no incluye el directorio actual como script de Python, por lo que escribir algo as铆:

 from src.data.download import pydownload_file 

No funcionar谩. Tenemos que hacer:

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

Mi archivo build.py inicial era as铆:

 #!/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') 

Y el objetivo del preprocess no funcion贸. Constantemente se quejaba de los argumentos de entrada de la funci贸n pypreprocess . Parece que Pynt no maneja muy bien los argumentos de funciones opcionales. Tuve que eliminar el argumento para hacer el archivo de Excel. Tenga esto en cuenta si su proyecto tiene funciones con argumentos opcionales.

Podemos ejecutar pynt desde la carpeta del proyecto y enumerar todos los objetivos disponibles:

 pynt -l 

resultado
 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. 


Hagamos la distribuci贸n por pares:

 pynt exploratory 

resultado
 [ 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" ] 


Si ahora ejecutamos el mismo comando nuevamente (es decir, pynt exploratory ) habr谩 una reconstrucci贸n completa. Pynt no hizo un seguimiento de que nada ha cambiado.

Adoqu铆n


Pavimentadora se ve casi exactamente como Pynt. Es ligeramente diferente en una forma en que uno define dependencias entre objetivos (otro decorador @needs ). Paver realiza una reconstrucci贸n completa cada vez y no funciona bien con funciones que tienen argumentos opcionales. Las instrucciones de compilaci贸n se encuentran en el archivo pavement.py .

doit


Doit parece un intento de crear una verdadera herramienta de automatizaci贸n de compilaci贸n en python. Puede ejecutar c贸digo python y comandos de shell. Se ve bastante prometedor. Lo que parece perderse (en el contexto de nuestros objetivos espec铆ficos) es la capacidad de manejar las dependencias entre objetivos. Digamos que queremos hacer una peque帽a tuber铆a donde la salida del objetivo A se usa como entrada del objetivo B. Y digamos que estamos usando archivos como salidas, por lo que el objetivo A crea un archivo llamado outA .



Para hacer esta canalizaci贸n, necesitaremos especificar el archivo outA dos veces en el objetivo A (como resultado de un objetivo, pero tambi茅n devolver su nombre como parte de la ejecuci贸n del objetivo). Luego, tendremos que especificarlo como entrada para el objetivo B. Por lo tanto, hay 3 lugares en total en los que necesitamos proporcionar informaci贸n sobre la outA archivo outA E incluso despu茅s de hacerlo, la modificaci贸n del archivo outA no conducir谩 a la reconstrucci贸n autom谩tica del objetivo B. Esto significa que si le pedimos a Doit que construya el objetivo B, solo verificar谩 si el objetivo B est谩 actualizado sin verificar ninguna de las dependencias Para superar esto, necesitaremos especificar outA 4 veces, tambi茅n como dependencia de archivo del objetivo B. Veo esto como un inconveniente. Tanto Make como CMake pueden manejar tales situaciones correctamente.

Las dependencias en doit se basan en archivos y se expresan como cadenas. Esto significa que las dependencias ./myfile.txt y myfile.txt se consideran diferentes. Como escrib铆 anteriormente, me parece un poco extra帽o la forma de pasar informaci贸n de un objetivo a otro (cuando se usan objetivos de Python). El objetivo tiene una lista de artefactos que va a producir, pero otro objetivo no puede usarlo. En cambio, la funci贸n python, que constituye el objetivo, debe devolver un diccionario, al que se puede acceder desde otro objetivo. Ve谩moslo en un ejemplo:

 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, } 

Aqu铆 el preprocess objetivo depende de los datos sin rawdata . La dependencia se proporciona a trav茅s de la propiedad getargs . Dice que el argumento input_file de la funci贸n doit_pypreprocess es el filename de filename de salida de los datos rawdata destino. Eche un vistazo al ejemplo completo en el archivo dodo.py.

Puede valer la pena leer las historias de 茅xito del uso de doit. Definitivamente tiene caracter铆sticas agradables como la capacidad de proporcionar una verificaci贸n de objetivos actualizada personalizada.

Luigi


Luigi se mantiene alejado de otras herramientas, ya que es un sistema para construir tuber铆as complejas. Apareci贸 en mi radar despu茅s de que un colega me dijo que intent贸 hacer Make, que nunca pudo usarlo en Windows / Linux y se mud贸 a Luigi.

Luigi apunta a sistemas listos para la producci贸n. Viene con un servidor, que se puede utilizar para visualizar sus tareas o para obtener un historial de ejecuciones de tareas. El servidor se llama un programador central . Un planificador local est谩 disponible para fines de depuraci贸n.

Luigi tambi茅n es diferente de otros sistemas en la forma en que se crean las tareas. Lugi no act煤a en alg煤n archivo predefinido (como dodo.py , dodo.py o makefile). M谩s bien, uno tiene que pasar un nombre de m贸dulo de Python. Entonces, si intentamos usarlo de manera similar a otras herramientas (colocar un archivo con tareas en la ra铆z del proyecto), no funcionar谩. Tenemos que instalar nuestro proyecto o modificar la variable de entorno PYTHONPATH agregando la ruta al proyecto.

Lo bueno de luigi es la forma de especificar dependencias entre tareas. Cada tarea es una clase. La output m茅todo le dice a Luigi d贸nde terminar谩n los resultados de la tarea. Los resultados pueden ser un solo elemento o una lista. El m茅todo requires especifica dependencias de tareas (otras tareas; aunque es posible hacer una dependencia de s铆 mismo). Y ya est谩. Lo que se especifica como output en la tarea A se pasar谩 como entrada a la tarea B si la tarea B se basa en la tarea A.


A Luigi no le importan las modificaciones de archivos. Se preocupa por la existencia de archivos. Por lo tanto, no es posible activar reconstrucciones cuando cambia el c贸digo fuente. Luigi no tiene una funcionalidad limpia incorporada.

Las tareas de Luigi para este proyecto est谩n disponibles en el archivo luigitasks.py . Los ejecuto desde la terminal:

 luigi --local-scheduler --module luigitasks Exploratory 

Comparaci贸n


La siguiente tabla resume c贸mo funcionan los diferentes sistemas con respecto a nuestros objetivos espec铆ficos.
Definir objetivo con dependenciaConstrucciones incrementalesCompilaciones incrementales si se cambia el c贸digo fuenteCapacidad para descubrir qu茅 artefactos eliminar durante clean comando de clean
CMakesisisisi
Pyntsinonono
Adoqu铆nsinonono
doitAlgo sisisisi
Luigisinonono

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


All Articles