Compartir a través de


Implementar modelos de lenguaje en puntos de conexión por lotes

SE APLICA A:Extensión ML de la CLI de Azure v2 (actual)SDK de Python azure-ai-ml v2 (actual)

Los puntos de conexión de Batch se pueden usar para implementar modelos costosos, como modelos de lenguaje, sobre datos de texto. En este tutorial, aprenderá a implementar un modelo que puede realizar un resumen de secuencias largas de texto mediante un modelo de HuggingFace. También se muestra cómo realizar la optimización de inferencia mediante las bibliotecas de HuggingFace optimum y accelerate.

Acerca de este ejemplo

El modelo con el que vamos a trabajar se creó usando los conocidos transformadores de biblioteca de HuggingFace junto con un modelo previamente entrenado de Facebook con la arquitectura BART. Se introdujo en el documento BART: Entrenamiento previo de eliminación de ruido de secuencia a secuencia para la generación de lenguaje natural. Este modelo tiene las siguientes limitaciones que es importante tener en cuenta para su implementación:

  • Puede trabajar con secuencias de hasta 1024 tokens.
  • Está entrenado para resumir el texto en inglés.
  • Utilizaremos Torch como backend.

El ejemplo de este artículo se basa en ejemplos de código incluidos en el repositorio azureml-examples. Para ejecutar los comandos localmente sin tener que copiar o pegar YAML y otros archivos, use los siguientes comandos para clonar el repositorio e ir a la carpeta del lenguaje de codificación:

git clone https://github.com/Azure/azureml-examples --depth 1
cd azureml-examples/cli

Los archivos de este ejemplo están en:

cd endpoints/batch/deploy-models/huggingface-text-summarization

Siga estos pasos en Jupyter Notebooks

Puede seguir este ejemplo en un Jupyter Notebook. En el repositorio clonado, abra el cuaderno: text-summarization-batch.ipynb.

Requisitos previos

  • Suscripción a Azure. Si no tiene una suscripción a Azure, cree una cuenta gratuita antes de empezar.

  • Un área de trabajo de Azure Machine Learning. Para crear un área de trabajo, vea Administración de áreas de trabajo de Azure Machine Learning.

  • Los siguientes permisos en el área de trabajo de Azure Machine Learning:

    • Para crear o administrar puntos de conexión e implementaciones: Use un rol de Propietario, Colaborador o personalizado al que se le hayan asignado los permisos Microsoft.MachineLearningServices/workspaces/batchEndpoints/*.
    • Para crear implementaciones de Azure Resource Manager en el grupo de recursos del área de trabajo: Use un rol de Propietario, Colaborador o personalizado que tenga asignado el permiso Microsoft.Resources/deployments/write en el grupo de recursos donde se implementa el área de trabajo.
  • La CLI de Azure Machine Learning o el SDK de Azure Machine Learning para Python:

    Ejecute el siguiente comando para instalar la CLI de Azure y la extensión ml para Azure Machine Learning:

    az extension add -n ml
    

    Las implementaciones de componentes de canalización para puntos de conexión por lotes se introdujeron en la versión 2.7 de la extensión ml para la CLI de Azure. Use el comando az extension update --name ml para obtener la versión más reciente.


Conexión con su área de trabajo

El área de trabajo es el recurso de nivel superior de Azure Machine Learning. Proporciona un lugar centralizado para trabajar con todos los artefactos que cree al usar Azure Machine Learning. En esta sección, se conectará al área de trabajo donde realizará las tareas de implementación.

En el siguiente comando, escriba el id. de suscripción, el nombre del área de trabajo, el nombre de grupo de recursos y la ubicación:

az account set --subscription <subscription>
az configure --defaults workspace=<workspace> group=<resource-group> location=<location>

Registro del modelo

Debido al tamaño del modelo, no se ha incluido en este repositorio. En su lugar, puede descargar una copia desde el centro del modelo HuggingFace. Necesita los paquetes transformers e torch instalados en el entorno que está usando.

%pip install transformers torch

Utilice el siguiente código para descargar el modelo en una carpeta model:

from transformers import pipeline

model = pipeline("summarization", model="facebook/bart-large-cnn")
model_local_path = 'model'
summarizer.save_pretrained(model_local_path)

Ahora podemos registrar este modelo en el registro de Azure Machine Learning:

MODEL_NAME='bart-text-summarization'
az ml model create --name $MODEL_NAME --path "model"

Creación del punto de conexión

Vamos a crear un punto de conexión por lotes denominado text-summarization-batch donde implementar el modelo HuggingFace para ejecutar el resumen de texto en archivos de texto en inglés.

  1. Decida el nombre del punto de conexión. El nombre del punto de conexión termina en el URI asociado al punto de conexión. Por este motivo, los nombres de punto de conexión por lotes deben ser únicos dentro de una región de Azure. Por ejemplo, solo puede haber un punto de conexión por lotes con el nombre mybatchendpoint en westus2.

    En este caso, vamos a colocar el nombre del punto de conexión en una variable para que podamos hacer referencia a él más adelante.

    ENDPOINT_NAME="text-summarization-batch"
    
  2. Configuración del punto de conexión por lotes

    El siguiente archivo YAML define un punto de conexión por lotes:

    endpoint.yml

    $schema: https://azuremlschemas.azureedge.net/latest/batchEndpoint.schema.json
    name: text-summarization-batch
    description: A batch endpoint for summarizing text using a HuggingFace transformer model.
    auth_mode: aad_token
    
  3. Creación del punto de conexión:

    az ml batch-endpoint create --file endpoint.yml  --name $ENDPOINT_NAME
    

Creación de la implementación

Vamos a crear la implementación que hospeda el modelo:

  1. Es necesario crear un script de puntuación que pueda leer los archivos .csv proporcionados por la implementación por lotes y devolver las puntuaciones del modelo junto con el resumen. El siguiente script realiza las siguientes acciones:

    • Indica una función init que detecta la configuración de hardware (CPU vs GPU) y carga el modelo en consecuencia. Tanto el modelo como el tokenizador se cargan en variables globales. No estamos utilizando un objeto pipeline de HuggingFace para tener en cuenta la limitación en la secuencia lenghs del modelo que estamos utilizando actualmente.
    • Tenga en cuenta que estamos realizando optimizaciones del modelo para mejorar el rendimiento mediante optimum y accelerate las bibliotecas. Si el modelo o hardware no lo admite, ejecutaremos la implementación sin estas optimizaciones.
    • Indica una función run que se ejecuta para cada miniproceso que proporciona la implementación por lotes.
    • La función run lee todo el lote mediante la biblioteca datasets. El texto que necesitamos resumir está en la columna text.
    • El método run itera en cada una de las filas del texto y ejecuta la predicción. Dado que se trata de un modelo con un costo elevado, la ejecución de la predicción en archivos enteros dará lugar a una excepción de memoria insuficiente. Observe que el modelo no se ejecuta con el objeto pipeline de transformers. Esto se hace para tener en cuenta secuencias largas de texto y la limitación de 1024 tokens en el modelo subyacente que usamos.
    • Devuelve el resumen del texto proporcionado.

    code/batch_driver.py

    import os
    import time
    import torch
    import subprocess
    import mlflow
    from pprint import pprint
    from transformers import AutoTokenizer, BartForConditionalGeneration
    from optimum.bettertransformer import BetterTransformer
    from datasets import load_dataset
    
    
    def init():
        global model
        global tokenizer
        global device
    
        cuda_available = torch.cuda.is_available()
        device = "cuda" if cuda_available else "cpu"
    
        if cuda_available:
            print(f"[INFO] CUDA version: {torch.version.cuda}")
            print(f"[INFO] ID of current CUDA device: {torch.cuda.current_device()}")
            print("[INFO] nvidia-smi output:")
            pprint(
                subprocess.run(["nvidia-smi"], stdout=subprocess.PIPE).stdout.decode(
                    "utf-8"
                )
            )
        else:
            print(
                "[WARN] CUDA acceleration is not available. This model takes hours to run on medium size data."
            )
    
        # AZUREML_MODEL_DIR is an environment variable created during deployment
        model_path = os.path.join(os.environ["AZUREML_MODEL_DIR"], "model")
    
        # load the tokenizer
        tokenizer = AutoTokenizer.from_pretrained(
            model_path, truncation=True, max_length=1024
        )
    
        # Load the model
        try:
            model = BartForConditionalGeneration.from_pretrained(
                model_path, device_map="auto"
            )
        except Exception as e:
            print(
                f"[ERROR] Error happened when loading the model on GPU or the default device. Error: {e}"
            )
            print("[INFO] Trying on CPU.")
            model = BartForConditionalGeneration.from_pretrained(model_path)
            device = "cpu"
    
        # Optimize the model
        if device != "cpu":
            try:
                model = BetterTransformer.transform(model, keep_original_model=False)
                print("[INFO] BetterTransformer loaded.")
            except Exception as e:
                print(
                    f"[ERROR] Error when converting to BetterTransformer. An unoptimized version of the model will be used.\n\t> {e}"
                )
    
        mlflow.log_param("device", device)
        mlflow.log_param("model", type(model).__name__)
    
    
    def run(mini_batch):
        resultList = []
    
        print(f"[INFO] Reading new mini-batch of {len(mini_batch)} file(s).")
        ds = load_dataset("csv", data_files={"score": mini_batch})
    
        start_time = time.perf_counter()
        for idx, text in enumerate(ds["score"]["text"]):
            # perform inference
            inputs = tokenizer.batch_encode_plus(
                [text], truncation=True, padding=True, max_length=1024, return_tensors="pt"
            )
            input_ids = inputs["input_ids"].to(device)
            summary_ids = model.generate(
                input_ids, max_length=130, min_length=30, do_sample=False
            )
            summaries = tokenizer.batch_decode(
                summary_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False
            )
    
            # Get results:
            resultList.append(summaries[0])
            rps = idx / (time.perf_counter() - start_time + 00000.1)
            print("Rows per second:", rps)
    
        mlflow.log_metric("rows_per_second", rps)
        return resultList
    

    Sugerencia

    Aunque la implementación proporciona archivos en mini lotes, este script de puntuación procesa una fila cada vez. Este es un patrón común cuando se trata de modelos de costo elevado, ya que intentar cargar todo el lote y enviarlo al modelo a la vez puede dar lugar a una presión de memoria alta en el ejecutor de lotes (excepciones de OOM).

  2. Es necesario indicar en qué entorno vamos a ejecutar la implementación. En nuestro caso, nuestro modelo se ejecuta en Torch y requiere las bibliotecas transformers, accelerate, y optimum de HuggingFace. Azure Machine Learning ya tiene un entorno con compatibilidad con Torch y GPU disponible. Solo vamos a agregar un par de dependencias en un archivo de conda.yaml.

    environment/torch200-conda.yaml

    name: huggingface-env
    channels:
      - conda-forge
    dependencies:
      - python=3.8.5
      - pip
      - pip:
        - torch==2.0
        - transformers
        - accelerate
        - optimum
        - datasets
        - mlflow
        - azureml-mlflow
        - azureml-core
        - azureml-dataset-runtime[fuse]
    
  3. Podemos usar el archivo conda mencionado antes de lo siguiente:

    La definición del entorno se incluye en el archivo de implementación.

    deployment.yml

    compute: azureml:gpu-cluster
    environment:
      name: torch200-transformers-gpu
      image: mcr.microsoft.com/azureml/openmpi4.1.0-cuda11.8-cudnn8-ubuntu22.04:latest
    

    Importante

    El entorno torch200-transformers-gpu que hemos creado requiere un dispositivo de hardware compatible con CUDA 11.8 para ejecutar Torch 2.0 y Ubuntu 20.04. Si su GPU no admite esta versión de CUDA, puede consultar el entorno alternativo torch113-conda.yaml conda (también disponible en el repositorio), que ejecuta Torch 1.3 sobre Ubuntu 18.04 con CUDA 10.1. Sin embargo, la aceleración mediante las bibliotecas optimum y accelerate no se admitirá en esta configuración.

  4. Cada implementación se ejecuta en clústeres de proceso. Admiten clústeres de proceso de Azure Machine Learning (AmlCompute) o clústeres de Kubernetes. En este ejemplo, nuestro modelo puede beneficiarse de la aceleración de GPU, por lo que usaremos un clúster de GPU.

    az ml compute create -n gpu-cluster --type amlcompute --size STANDARD_NV6 --min-instances 0 --max-instances 2
    

    Nota

    En este momento, no se le cobra por el proceso, ya que el clúster permanece en 0 nodos hasta que se invoque un punto de conexión por lotes y se envíe un trabajo de puntuación por lotes. Más información sobre cómo administrar y optimizar el coste de AmlCompute.

  5. Ahora, vamos a crear la implementación.

    Para crear una nueva implementación en el punto de conexión creado, cree una configuración de YAML como la siguiente. Puede comprobar el esquema YAML del punto de conexión por lotes completo para obtener propiedades adicionales.

    deployment.yml

    $schema: https://azuremlschemas.azureedge.net/latest/modelBatchDeployment.schema.json
    endpoint_name: text-summarization-batch
    name: text-summarization-optimum
    description: A text summarization deployment implemented with HuggingFace and BART architecture with GPU optimization using Optimum.
    type: model
    model: azureml:bart-text-summarization@latest
    compute: azureml:gpu-cluster
    environment:
      name: torch200-transformers-gpu
      image: mcr.microsoft.com/azureml/openmpi4.1.0-cuda11.8-cudnn8-ubuntu22.04:latest
      conda_file: environment/torch200-conda.yaml
    code_configuration:
      code: code
      scoring_script: batch_driver.py
    resources:
      instance_count: 2
    settings:
      max_concurrency_per_instance: 1
      mini_batch_size: 1
      output_action: append_row
      output_file_name: predictions.csv
      retry_settings:
        max_retries: 1
        timeout: 3000
      error_threshold: -1
      logging_level: info
    

    Después, cree la implementación con el siguiente comando:

    az ml batch-deployment create --file deployment.yml --endpoint-name $ENDPOINT_NAME --set-default
    

    Importante

    Observará en esta implementación un valor alto en timeout en el parámetro retry_settings. Esto se debe a la naturaleza del modelo que estamos ejecutando. Este es un modelo con un costo muy elevado y la inferencia en una sola fila puede tardar hasta 60 segundos. Los parámetros timeout controlan cuánto tiempo debe esperar la implementación por lotes para que el script de puntuación termine de procesar cada mini lote. Dado que nuestro modelo ejecuta predicciones fila por fila, el procesamiento de un archivo largo puede tardar un tiempo. Observe también que el número de archivos por lote está establecido en 1 (mini_batch_size=1). Esto también está relacionado con el tipo de trabajo que estamos haciendo. El procesamiento de un archivo a la vez por lote es lo suficientemente caro como para justificarlo. Observará que se trata de un patrón en el procesamiento de NLP.

  6. Aunque puede invocar una implementación específica dentro de un punto de conexión, normalmente querrá invocar el propio punto de conexión y dejar que este decida qué implementación se va a usar. Esta se denomina implementación "predeterminada". Esto le ofrece la posibilidad de cambiar la implementación predeterminada y, por tanto, modificar el modelo que sirve a la implementación sin cambiar el contrato con el usuario que invoca el punto de conexión. Use la instrucción siguiente para actualizar la implementación predeterminada:

    DEPLOYMENT_NAME="text-summarization-hfbart"
    az ml batch-endpoint update --name $ENDPOINT_NAME --set defaults.deployment_name=$DEPLOYMENT_NAME
    
  7. En este momento, nuestro punto de conexión por lotes está listo para usarse.

Prueba de la implementación

Para probar nuestro punto de conexión, vamos a utilizar una muestra del conjunto de datos BillSum: Un corpus para el resumen automático de la legislación estadounidense. Este ejemplo se incluye en el repositorio de la carpeta data. Observe que el formato de los datos es CSV y el contenido a resumir se encuentra bajo la columna text, tal y como espera el modelo.

  1. Vamos a invocar el punto de conexión:

    JOB_NAME=$(az ml batch-endpoint invoke --name $ENDPOINT_NAME --input data --input-type uri_folder --query name -o tsv)
    

    Nota

    Es posible que la utilidad jq no esté instalada en todas las instalaciones. Puede obtener instrucciones en este vínculo.

    Sugerencia

    Tenga en cuenta que, al indicar una ruta de acceso local como entrada, los datos se cargan en la cuenta de almacenamiento predeterminada de Azure Machine Learning.

  2. Se inicia un trabajo por lotes tan pronto como el comando vuelve. Puede supervisar el estado del trabajo hasta que finalice:

    az ml job show -n $JOB_NAME --web
    
  3. Una vez finalizada la implementación, podemos descargar las predicciones:

    Use los siguientes comandos para descargar las predicciones:

    az ml job download --name $JOB_NAME --output-name score --download-path .
    

Consideraciones al implementar modelos que procesan texto

Como se mencionó en algunas de las notas de este tutorial, el texto de procesamiento puede tener algunas peculiaridades que requieren una configuración específica para las implementaciones por lotes. Tenga en cuenta lo siguiente al diseñar la implementación por lotes:

  • Algunos modelos de NLP pueden tener un costo muy elevado en términos de memoria y tiempo de proceso. Si este es el caso, considere la posibilidad de reducir el número de archivos incluidos en cada mini lote. En el ejemplo anterior, el número se tomó al mínimo, 1 archivo por lote. Aunque es posible que este no sea el caso, tenga en cuenta cuántos archivos puede puntuar el modelo en cada momento. Tenga en cuenta que es posible que la relación entre el tamaño de la entrada y la superficie de memoria del modelo no sea lineal en el caso de los modelos de aprendizaje profundo.
  • Si el modelo ni siquiera puede controlar un archivo a la vez (como en este ejemplo), considere la posibilidad de leer los datos de entrada en filas o fragmentos. Implemente el procesamiento por lotes en el nivel de fila si necesita lograr un mayor rendimiento o uso de hardware.
  • Establezca el valor timeout de la implementación de acuerdo con el costo del modelo y la cantidad de datos que espera procesar. Recuerde que el timeout indica el tiempo que la implementación por lotes esperaría para que se ejecutara el script de puntuación para un lote determinado. Si el lote tiene muchos archivos o archivos con muchas filas, esto afectará al valor correcto de este parámetro.

Consideraciones sobre el procesamiento de texto de modelos de MLflow

Las mismas consideraciones mencionadas anteriormente se aplican a los modelos de MLflow. Sin embargo, dado que no está obligado a proporcionar un script de puntuación para la implantación de su modelo MLflow, algunas de las recomendaciones mencionadas pueden requerir un enfoque diferente.

  • Los modelos MLflow en puntos de conexión por lotes admiten la lectura de datos tabulares como datos de entrada, que pueden contener largas secuencias de texto. Consulte Compatibilidad de tipos de archivo para obtener más información sobre los tipos de archivo compatibles.
  • Las implementaciones de Batch llaman a la función predict del modelo de MLflow con el contenido de un archivo completo como dataframe de Pandas. Si los datos de entrada contienen muchas filas, es probable que la ejecución de un modelo complejo (como el que se presenta en este tutorial) produzca una excepción de memoria insuficiente. Si este es su caso, tenga en cuenta las indicaciones siguientes: