32

Tres recomendaciones para depurar cargas de trabajo de Machine Learning a escala de la nube

20989Puntos

hace un año

Supongamos que creó una maravillosa red neuronal convolucional hecha a mano que funciona perfectamente en el conjunto de datos basado en disco duro. Ahora está listo para llevar esta obra maestra a la nube para trabajar en un conjunto datos mucho más grande en máquinas más sólidas… que no es algo que busque.

  • “¿Cómo puedo enviar los retazos de conda (o venv) donde instalé todo el código de entrenamiento o inferencia necesario ASÍ COMO todo lo demás que pensé que iba a necesitar durante el proceso?”
  • “No quiero malgastar el dinero del proceso de la nube en cosas que no estoy seguro de que van a funcionar a la primera”.
  • Básicamente, “Ya terminé lo que tenía que hacer y no me interesa las tareas anexas del trabajo”.

No hay por qué temer, querido lector, porque este artículo está pensado para ayudarlo a trasladar su gloriosa obra a la nube (y más allá), usando su entorno local como si fuera la nube misma.

Traslado a la nube

Como conozco Azure y sus servicios de aprendizaje automático, me voy a centrar principalmente en esa tecnología. Pero los principios tendrían que ser universales e incluir sugerencias que funcionen en cualquier otro lugar. El código está totalmente disponible si quiere leer detenidamente lo que he creado. Básicamente se trata de una red neuronal convolucional para clasificar tacos y burritos (porque, seamos sinceros… ¿a quién no le gusta la comida mexicana?).

Sugerencia 1: Entrenamiento en la nube (pero localmente)

El primer paso para trasladar el entrenamiento a la nube es describir la configuración de la ejecución real. Esto incluye qué se va a ejecutar y cómo ejecutarlo. En Azure Machine Learning Service (para abreviar, ALM), hay varias formas divertidas de describir esto. Aquí me voy a centrar principalmente en la CLI y el SDK de Python. El primer concepto importante es ScriptRunConfig que se muestra a continuación:

defmain():# what to run
        script = ScriptRunConfig(source_directory=".", 
                                 script="train.py", 
                                 arguments=[
                                     "-d", "/data", 
                                     "-e", "1"])
        
        # running the script
        config = RunConfiguration()
        
        # tie everything together
        config.environment = create_env()
        config.target = "local"
    
        script.run_config = config

Básicamente une el qué (ejecutar train.py con -d /data -e 1) con el cómo (RunConfiguration). La primera parte esencial de RunConfiguration se describe en el entorno de Python:

defcreate_env(is_local=True):# environment
        env = Environment(name="foodai-pytorch")
        env.python.conda_dependencies = dependencies()
    
        # more here soon - this is the cool partreturn env

Aquí no hay nada muy interesante, salvo la construcción de CondaDependencies:

defdependencies():
        conda_dep = CondaDependencies()
        conda_dep.add_conda_package("matplotlib")
        conda_dep.add_pip_package("numpy")
        conda_dep.add_pip_package("pillow")
        conda_dep.add_pip_package("requests")
        conda_dep.add_pip_package("torchvision")
        conda_dep.add_pip_package("onnxruntime")
        conda_dep.add_pip_package("azureml-defaults")
        return conda_dep
    

Ejecutar esto directamente en el área de trabajo de AML no tarda mucho más:

# get ref to AML Workspace
    ws = Workspace.from_config()
    
    # name the experiment (or cloud training run)
    exp = Experiment(workspace=ws, name="foodai")
    
    run = exp.submit(config=script)
    run.wait_for_completion(show_output=True)

Esto (en teoría) ejecutará su increíble modelo en la nube (el módulo donde obtiene los datos. Esto lo dejaré para otra ocasión). A veces, la ejecución tarda un poco en completarse y cada error agregará más tiempo a todo el proceso. En esencia, necesita copiar el script, recompilar los entornos desde cero, cargar el contenido, ejecutarlo y paf, ERROR. Esto me pasó un par de veces porque se me olvidó matplotlib o no agregué requests o algo más trivial (siempre fue mi culpa, aunque nunca se lo reconocí al equipo del producto).

Entonces ¿cómo puedo ejecutar esto de manera local como si se estuviese ejecutando en la nube? Esta es la pregunta de los cinco millones de la moneda que quiera. ¿El resultado? Una característica entretenida que nos permite establecer dónde se realiza la ejecución. Ahora bien, si no sabe mucho sobre Docker, piense en que es como una máquina pequeña que solo tiene integrados los componentes esenciales para ejecutar nada más que el código de entrenamiento. Resulta que la configuración RunConfiguration que se compiló anteriormente no es más que instrucciones sobre cómo crear una imagen de Docker que se ejecuta como un contenedor en la nube. ¿Qué ocurre si solo podríamos ejecutar localmente el contenedor para asegurarnos de que funciona? Pues sí se puede:

defcreate_env(is_local=True):# environment
        env = Environment(name="foodai-pytorch")
        env.python.conda_dependencies = dependencies()
    
        # the GOOD PART WE LEFT OUT# BUT ARE ADDING IN AGAINif is_local:
            # local docker settings
            env.docker.enabled = True
            env.docker.shared_volumes = True
            env.docker.arguments = [
                "-v", "C:\\projects\\FoodAI\\data:/data"
            ]
        return env

La parte que más me gusta es donde le indicamos al contenedor en ejecución cómo acceder a los datos. Con env.docker.arguments = [ ... ], básicamente le indicamos al contenedor que se ejecute localmente para montar mi carpeta local de datos en /data, que se va a usar para el entrenamiento (si conoce Docker, en realidad puede pasar todo lo que quiera). ¡Voilá!

En esencia, ejecutó el experimento de aprendizaje automático como si estuviese en la nube (pero de manera local con Docker). La buena noticia es que si todo funciona bien en la ejecución local (la nube falsa), debería funcionar sin ningún problema como una ejecución real en la nube.

Sugerencia 2: Inferencia en la nube (pero localmente)

Hasta ahora, ya creó un modelo increíble, lo serializó y está preparado para hacer alguna inferencia. Como ya dije, es probable que ya haya escrito el código para hacerlo y sabe que funciona localmente. ¿Cómo hacemos que esta obra maestra funcione en la nube? Usaremos el mismo principio que se mostró anteriormente: lo ejecutaremos desde un contenedor como si estuviera en la nube.

Lo primero es lo primero: AML espera que la puntuación/inferencia se hagan de una manera específica. Todo lo que necesita es una función init() y una función run(), nada más. Esta es la mía (usa un modelo ONNX):

import json
    import time
    import requests
    import datetime
    import numpy as np
    from PIL import Image
    from io import BytesIO
    import onnxruntime as rt
    from torchvision import transforms
    
    # azureml importsfrom azureml.core.model import Model
    
    definit():global session, transform, classes, input_name
    
        try:
            model_path = Model.get_model_path('foodai')
        except:
            model_path = 'model.onnx'
    
        classes = ['burrito', 'tacos']
        session = rt.InferenceSession(model_path) 
        input_name = session.get_inputs()[0].name
        transform = transforms.Compose([
                transforms.Resize(256),
                transforms.CenterCrop(224),
                transforms.ToTensor(),
                transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
            ])
    
    defrun(raw_data):
        prev_time = time.time()
    
        post = json.loads(raw_data)
        image_url = post['image']
        response = requests.get(image_url)
        img = Image.open(BytesIO(response.content))
        v = transform(img)
        pred_onnx = session.run(None, {input_name: v.unsqueeze(0).numpy()})[0][0]
    
        current_time = time.time()
        inference_time = datetime.timedelta(seconds=current_time - prev_time)
    
        predictions = {}
        for i in range(len(classes)):
            predictions[classes[i]] = str(pred_onnx[i])
    
        payload = {
            'time': str(inference_time.total_seconds()),
            'prediction': classes[int(np.argmax(pred_onnx))],
            'scores': predictions
        }
    
        return payload
    

Puede que se pregunte sobre esto: model_path = Model.get_model_path('foodai'). ¡Excelente pregunta! AML tiene una característica en la que podemos registrar nuestros modelos y controlar sus versiones. Este script score.py simplemente carga el modelo mencionado que se registró en el servicio (a menos que no pueda hacerlo). Una vez que tenga el script de puntuación y el modelo registrado, puede usar la CLI de Azure ML para realizar la implementación:

    az ml model deploy --name foodai-local --model foodai:5 ^
        --entry-script score.py --runtime python --conda-file foodai_scoring.yml ^
        --compute-type local --port 32267 ^
        --overwrite
    

foodai_scoring.yml es un archivo de entorno conda estándar. Pero la belleza de esta implementación es que se ejecuta totalmente en un contenedor local. foodai:5 significa la versión 5 del modelo foodai registrado en AML. Al ejecutar este comando, el servicio en la nube se inicia localmente.

¡Funciona!

Como dije antes, la buena noticia es que si funciona localmente, debería funcionar sin problemas en Azure. Hay muchas opciones para dónde en Azure puede realizar la implementación (pero lo dejaré para otro momento).

Como referencia, el comando de la CLI para realmente hacer la implementación en la nube es muy similar:

    az ml model deploy --name foodai --model foodai:5 ^
        --entry-script score.py --runtime python --conda-file foodai_scoring.yml ^
        --deploy-config-file deployconfig.json --compute-target sauron ^
        --overwrite

La única diferencia es el archivo deployconfig.json que describe el destino de la nube para la implementación (los objetivos comunes son AKS y ACI).

Sugerencia 3: Registros, registros y más registros

Supongamos que todo funciona bien tanto en el entorno local como en los contenedores que se deberían ejecutar en la nube, pero de todos modos hay errores. Esto nos lleva a la última de las sugerencias: REGISTROS, REGISTROS Y MÁS REGISTROS.

Experimento

Toda ejecución de experimento que ocurra genera salidas (puede ver la salida anterior en mi ejecución local). Resulta que AML las guarda todas.

De todos los registros guardados, 70_driver_log.txt es el que almacena la salida del script de entrenamiento. Aquí es donde encontrará cualquier problema adicional una vez que migre a la nube (aunque si se ejecuta localmente en el contexto de AML, debería funcionar sin ningún problema en la nube).

Inferencia

Obtener los registros de un servicio de inferencia implementado es incluso más sencillo:

az ml service get-logs --name foodai

donde foodai es el nombre del servicio implementado. Si canalizamos la salida a un archivo, obtendremos algo similar a lo siguiente:

    [
      "2020-03-31T18:35:33,706420083+00:00 - gunicorn/run 
    2020-03-31T18:35:33,706724585+00:00 - iot-server/run 
    2020-03-31T18:35:33,706480083+00:00 - rsyslog/run 
    ... TRUNCATED ...
    2020-03-31 18:35:35.153610654 [W:onnxruntime:, graph.cc:2413 CleanUnusedInitializers] Removing initializer '0.layer2.0.bn1.num_batches_tracked'. It is not used by any node and should be removed from the model.
    Users's init has completed successfully
    Scoring timeout setting is not found. Use default timeout: 3600000 ms
    ",
      null
    ]

Revisión

Para revisar las tres sugerencias:

  1. Podemos ejecutar los experimentos de aprendizaje automático en un contexto de nube con Docker (sin tener que saber mucho sobre Docker).
  2. Podemos ejecutar los servicios de inferencia en un contexto de nube con Docker (sin tener que saber nada sobre Docker).
  3. Podemos ver registros completos tanto para los experimentos como para los servicios de inferencia.


Autor: Seth Juarez

Jose
Jose
padronjosef

20989Puntos

hace un año

Todas sus entradas
Escribe tu comentario
+ 2
Ordenar por:
1
16012Puntos

¡Excelente información!

1
23822Puntos

Increible articulo, muy didactico