En Parte 1 De esta serie, hablamos sobre la creación de activos de código reutilizable que se pueden implementar en múltiples proyectos. Aprovechar un repositorio centralizado de pasos comunes de ciencia de datos asegura que los experimentos se puedan llevar a cabo más rápido y con mayor confianza en los resultados. Una fase de experimentación simplificada es fundamental para garantizar que entregue valor a la empresa lo más rápido posible.
En este artículo quiero centrarme en cómo puede aumentar la velocidad a la que puede experimentar. Es posible que tenga 10S-100 de ideas para diferentes configuraciones que desea probar, y llevarlas de manera eficiente aumentará en gran medida su productividad. Llevar a cabo una reentrenamiento completo cuando el rendimiento del modelo decae y explorar la inclusión de nuevas características cuando están disponibles son solo algunas situaciones en las que poder iterar rápidamente sobre los experimentos se convierte en una gran bendición.
Necesitamos hablar de cuadernos (nuevamente)
Si bien los cuadernos de Jupyter son una excelente manera de enseñarse sobre bibliotecas y conceptos, pueden ser mal utilizados fácilmente y convertirse en una muleta que se interpone activamente en el desarrollo rápido del modelo. Considere el caso de un científico de datos que se mueve a un nuevo proyecto. Los primeros pasos suelen abrir un nuevo cuaderno y comenzar algunos análisis de datos exploratorios. Comprender qué tipo de datos tiene disponible para usted, hacer algunas estadísticas resumidas simples, comprender su resultado y finalmente algunas visualizaciones simples para comprender la relación entre las características y el resultado. Estos pasos son un esfuerzo útil, ya que la comprensión de sus datos es crítico antes de comenzar el proceso de experimentación.
El problema con esto no está en el EDA en sí, sino lo que viene después. Lo que normalmente sucede es que el científico de datos avanza y abre instantáneamente un nuevo cuaderno para comenzar a escribir su marco de experimentos, normalmente comenzando con las transformaciones de datos. Esto generalmente se realiza mediante fragmentos de código de reutilización de su cuaderno EDA copiando de uno a otro. Una vez que tienen su primer cuaderno listo, se ejecuta y los resultados se guardan localmente o se escriben en una ubicación externa. Luego, estos datos son recogidos por otro cuaderno y se procesan más, como por selección de características y luego se escriben. Este proceso se repite hasta que su canalización de experimentos se forme de 5-6 cuadernos que deben ser activados secuencialmente por un científico de datos para que se ejecute un solo experimento.
Con un enfoque tan manual para la experimentación, iterando sobre ideas y probar diferentes escenarios se convierte en una tarea intensiva en mano de obra. Termina con la paralelización a nivel humano, donde los equipos enteros de científicos de datos se dedican a ejecutar experimentos al tener copias locales de los cuadernos y editar diligentemente su código para probar diferentes configuraciones. Los resultados se agregan a un informe, donde una vez que la experimentación ha terminado, la configuración de mejor rendimiento se encuentra entre todos los demás.
Todo esto no es sostenible. Los miembros del equipo salen de enfermos o tomando vacaciones, ejecutando experimentos durante la noche con la esperanza de que el cuaderno no se bloquee y olvida las configuraciones experimentales que ha hecho y aún debe hacer. Estos no deben ser preocupaciones que tengas al ejecutar un experimento. Afortunadamente, hay una mejor manera que implica poder iterar sobre ideas de manera estructurada y metódica a escala. Todo esto simplificará en gran medida la fase de experimentación de su proyecto y disminuirá en gran medida su tiempo de valor.
Abrazar las secuencias de comandos para crear su tubería experimental
El primer paso para acelerar su capacidad para experimentar es ir más allá de los cuadernos y comenzar a secuencias de comandos. Esta debería ser la parte más simple del proceso, simplemente coloca su código en un archivo .py en lugar de los bloques de celdas de un .IPYNB. Desde allí puede invocar su script desde la línea de comando, por ejemplo:
python src/main.py
if __name__ == "__main__":
input_data = ""
output_loc = ""
dataprep_config = {}
featureselection_config = {}
hyperparameter_config = {}
data = DataLoader().load(input_data)
data_train, data_val = DataPrep().run(data, dataprep_config)
features_to_keep = FeatureSelection().run(data_train, data_val, featureselection_config)
model_hyperparameters = HyperparameterTuning().run(data_train, data_val, features_to_keep, hyperparameter_config)
evaluation_metrics = Evaluation().run(data_train, data_val, features_to_keep, model_hyperparameters)
ArtifactSaver(output_loc).save([data_train, data_val, features_to_keep, model_hyperparameters, evaluation_metrics])
Tenga en cuenta que adherirse al principio de controlar su flujo de trabajo pasando argumentos en funciones puede simplificar en gran medida el diseño de su tubería experimental. Tener un guión como este ya ha mejorado su capacidad para ejecutar experimentos. Ahora solo necesita una sola invocación de script en lugar de la naturaleza de parada de arranque de ejecutar múltiples cuadernos en secuencia.
Es posible que desee agregar algunos argumentos de entrada a este script, como poder señalar una ubicación de datos particular o especificar dónde almacenar artefactos de salida. Puede extender fácilmente su script para tomar algunos argumentos de línea de comando:
python src/main_with_arguments.py --input_data <loc> --output_loc <loc>
if __name__ == "__main__":
input_data, output_loc = parse_input_arguments()
dataprep_config = {}
featureselection_config = {}
hyperparameter_config = {}
data = DataLoader().load(input_data)
data_train, data_val = DataPrep().run(data, dataprep_config)
features_to_keep = FeatureSelection().run(data_train, data_val, featureselection_config)
model_hyperparameters = HyperparameterTuning().run(data_train, data_val, features_to_keep, hyperparameter_config)
evaluation_metrics = Evaluation().run(data_train, data_val, features_to_keep, model_hyperparameters)
ArtifactSaver(output_loc).save([data_train, data_val, features_to_keep, model_hyperparameters, evaluation_metrics])
En este punto, tienes el comienzo de una buena tubería; Puede establecer la ubicación de entrada y salida e invocar su script con un solo comando. Sin embargo, probar nuevas ideas sigue siendo un esfuerzo relativamente manual, debe entrar en su base de código y realizar cambios. Como se mencionó anteriormente, el cambio entre diferentes configuraciones de experimentos debería ser idealmente tan simple como modificar el argumento de entrada a una función de envoltura que controla lo que debe llevarse a cabo. Podemos llevar todos estos argumentos diferentes a una sola ubicación para garantizar que la modificación de su configuración experimental se vuelva trivial. La forma más simple de implementar esto es con un archivo de configuración.
Configure sus experimentos con un archivo separado
El almacenamiento de todos sus argumentos de funciones relevantes en un archivo separado viene con varios beneficios. La división de la configuración de la base de código principal hace que sea más fácil probar diferentes configuraciones experimentales. Simplemente edite los campos relevantes con cualquiera que sea su nueva idea y esté listo para comenzar. Incluso puede intercambiar archivos de configuración completos con facilidad. También tiene una supervisión completa sobre cuál fue exactamente su configuración experimental. Si mantiene un archivo separado por experimento, puede volver a experimentos anteriores y ver exactamente lo que se llevó a cabo.
Entonces, ¿cómo se ve un archivo de configuración y cómo interactúa con el script de tuberías de experimentos que ha creado? Una implementación simple de un archivo de configuración es usar notación YAML y establecerla de la siguiente manera:
- Las banderas booleanas de nivel superior para encender y apagar las diferentes partes de su tubería
- Para cada paso de su tubería, defina qué cálculos desea realizar
file_locations:
input_data: ""
output_loc: ""
pipeline_steps:
data_prep: True
feature_selection: False
hyperparameter_tuning: True
evaluation: True
data_prep:
nan_treatment: "drop"
numerical_scaling: "normalize"
categorical_encoding: "ohe"
Esta es una forma flexible y liviana de controlar cómo se ejecutan sus experimentos. Luego puede modificar su script para cargar en esta configuración y usarlo para controlar el flujo de trabajo de su canalización:
python src/main_with_config –config_loc <loc>
if __name__ == "__main__":
config_loc = parse_input_arguments()
config = load_config(config_loc)
data = DataLoader().load(config["file_locations"]["input_data"])
if config["pipeline_steps"]["data_prep"]:
data_train, data_val = DataPrep().run(data,
config["data_prep"])
if config["pipeline_steps"]["feature_selection"]:
features_to_keep = FeatureSelection().run(data_train,
data_val,
config["feature_selection"])
if config["pipeline_steps"]["hyperparameter_tuning"]:
model_hyperparameters = HyperparameterTuning().run(data_train,
data_val,
features_to_keep,
config["hyperparameter_tuning"])
if config["pipeline_steps"]["evaluation"]:
evaluation_metrics = Evaluation().run(data_train,
data_val,
features_to_keep,
model_hyperparameters)
ArtifactSaver(config["file_locations"]["output_loc"]).save([data_train,
data_val,
features_to_keep,
model_hyperparameters,
evaluation_metrics])
Ahora hemos desacoplado por completo la configuración de nuestro experimento del código que lo ejecuta. La configuración experimental que queremos probar ahora está completamente determinada por el archivo de configuración, lo que hace que sea trivial probar nuevas ideas. Incluso podemos controlar los pasos que queremos llevar a cabo, permitiendo escenarios como:
- Ejecutar la preparación de datos y la selección de características solo para generar un conjunto de datos procesado inicial que pueda formar la base de una experimentación más detallada en probar diferentes modelos e hiperparámetros relacionados
Aproveche la automatización y el paralelismo
Ahora tenemos la capacidad de configurar diferentes configuraciones experimentales a través de un archivo de configuración y lanzar un experimento completo de extremo a extremo con una sola invocación de línea de comando. Todo lo que queda por hacer es escalar la capacidad de iterar en diferentes configuraciones de experimentos lo más rápido posible. La clave de esto es:
- Automatización para modificar programáticamente el archivo de configuración
- Ejecución paralela de experimentos
El paso 1) es relativamente trivial. Podemos escribir un script de shell o incluso un script de Python secundario cuyo trabajo es iterativo sobre diferentes configuraciones experimentales que el usuario proporciona y luego inicia una tubería con cada nueva configuración.
#!/bin/bash
for nan_treatment in drop impute_zero impute_mean
do
update_config_file($nan_treatment, <config_loc>)
python3 ./src/main_with_config.py --config_loc <config_loc>
done;
El paso 2) es una propuesta más interesante y depende mucho de la situación. Todos los experimentos que ejecutas son autodenominados y no dependen mutuamente. Esto significa que en teoría podemos lanzarlos a todos al mismo tiempo. Prácticamente se basa en que tenga acceso a un cálculo externo, ya sea interno o aunque un proveedor de servicios en la nube. Si este es el caso, cada experimento se puede lanzar como un trabajo separado en su cálculo, suponiendo que tenga acceso a usar estos recursos. Sin embargo, esto implica otras consideraciones, como la implementación de imágenes de Docker para garantizar un entorno consistente entre los experimentos y descubrir cómo integrar su código dentro del cálculo externo. Sin embargo, una vez que esto se resuelve, ahora está en condiciones de lanzar tantos experimentos como desee, solo está limitado por los recursos de su proveedor de cómputo.
Incrustar registradores y rastreadores de experimentos para una fácil supervisión
Tener la capacidad de lanzar 100 de experimentos paralelos en cómputo externo es una clara victoria en el camino para reducir el tiempo de valor de los proyectos de ciencia de datos. Sin embargo, abstraer este proceso viene con el costo de que no es tan fácil de interrogar, especialmente si algo sale mal. La naturaleza interactiva de los cuadernos permitió ejecutar un bloque de celdas y ver al instante el resultado.
El seguimiento del progreso de su tubería se puede realizar utilizando un registrador en su experimento. Puede capturar resultados clave, como las características elegidas por el proceso de selección, o usarlo para firmar lo que está ejecutando actualmente en la tubería. Si algo salía mal, puede hacer referencia a las entradas de registro que ha creado para descubrir dónde ocurrió el problema, y luego posiblemente incrustar más registros para comprender y resolver mejor el problema.
logger.info("Splitting data into train and validation set")
df_train, df_val = create_data_split(df, method = 'random')
logger.info(f"training data size: {df_train.shape[0]}, validation data size: {df_val.shape[0]}")
logger.info(f"treating missing data via: {missing_method}")
df_train = treat_missing_data(df_train, method = missing_method)
logger.info(f"scaling numerical data via: {scale_method}")
df_train = scale_numerical_features(df_train, method = scale_method)
logger.info(f"encoding categorical data via: {encode_method}")
df_train = encode_categorical_features(df_train, method = encode_method)
logger.info(f"number of features after encoding: {df_train.shape[1]}")
El aspecto final del lanzamiento de experimentos paralelos a gran escala es encontrar formas eficientes de analizarlos para encontrar rápidamente la configuración de mejor rendimiento. Leer a través de registros de eventos o tener que abrir archivos de rendimiento para cada experimento individualmente deshacerá rápidamente todo el trabajo duro que ha realizado para garantizar un proceso experimental optimizado.
Lo más fácil de hacer es incrustar un rastreador de experimentos en su script de tuberías. Hay una variedad de 1calle y 3rd Las herramientas de fiesta disponibles para usted que le permiten configurar un espacio de proyecto y luego registrar las métricas de rendimiento importantes de cada configuración experimental que considere. Normalmente vienen una parte delantera configurable que permite a los usuarios crear gráficos simples para la comparación. Esto hará que encontrar el experimento de mejor rendimiento sea un esfuerzo mucho más simple.
Conclusión
En este artículo hemos explorado cómo crear tuberías que faciliten la capacidad de llevar a cabo sin esfuerzo el Experimentación proceso. Esto ha implicado salir de los cuadernos y convertir su proceso de experimento en un solo script. Este script está respaldado por un archivo de configuración que controla la configuración de su experimento, lo que hace que sea trivial llevar a cabo diferentes configuraciones. El cálculo externo se aprovecha para paralelizar la ejecución de los experimentos. Finalmente, hablamos sobre el uso de registradores y rastreadores de experimentos para mantener la supervisión de sus experimentos y rastrear más fácilmente sus resultados. Todo esto permitirá a los científicos de datos acelerar en gran medida su capacidad para ejecutar experimentos, permitiéndoles reducir el tiempo de valor de sus proyectos y entregar resultados al negocio más rápido.