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.
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.
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?).
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.
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).
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.
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).
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
]
Para revisar las tres sugerencias:
Autor: Seth Juarez
¡Excelente información!
Muy bueno! gracias
Increible articulo, muy didactico