Acelerando el ajuste fino distribuido de PyTorch con tecnologías de Intel

'Acelerando ajuste fino distribuido de PyTorch con tecnologías de Intel'

Para toda su asombrosa capacidad, los modelos de aprendizaje profundo de última generación a menudo tardan mucho tiempo en entrenarse. Para acelerar los trabajos de entrenamiento, los equipos de ingeniería confían en el entrenamiento distribuido, una técnica de dividir y conquistar donde los servidores agrupados mantienen una copia del modelo, lo entrenan en un subconjunto del conjunto de entrenamiento e intercambian resultados para converger en un modelo final.

Las Unidades de Procesamiento Gráfico (GPUs) han sido durante mucho tiempo la opción predeterminada para entrenar modelos de aprendizaje profundo. Sin embargo, el auge del aprendizaje por transferencia está cambiando el juego. Ahora, los modelos rara vez se entrenan desde cero en conjuntos de datos enormes. En su lugar, se ajustan con frecuencia en conjuntos de datos específicos (y más pequeños) para construir modelos especializados que son más precisos que el modelo base para tareas particulares. Como estos trabajos de entrenamiento son mucho más cortos, el uso de un clúster basado en CPU puede resultar una opción interesante que mantenga tanto el tiempo de entrenamiento como el costo bajo control.

De qué trata esta publicación

En esta publicación, aprenderás cómo acelerar los trabajos de entrenamiento de PyTorch distribuyéndolos en un clúster de servidores de CPU Intel Xeon Scalable, alimentados por la arquitectura Ice Lake y ejecutando bibliotecas de software optimizadas para el rendimiento. Construiremos el clúster desde cero utilizando máquinas virtuales y deberías poder replicar fácilmente la demostración en tu propia infraestructura, ya sea en la nube o en local.

Ejecutando un trabajo de clasificación de texto, ajustaremos un modelo BERT en el conjunto de datos MRPC (una de las tareas incluidas en la prueba GLUE). El conjunto de datos MRPC contiene 5,800 pares de frases extraídas de fuentes de noticias, con una etiqueta que nos indica si las dos frases de cada par son semánticamente equivalentes. Elegimos este conjunto de datos por su tiempo de entrenamiento razonable, y probar otras tareas de GLUE está a solo un parámetro de distancia.

Una vez que el clúster esté funcionando, ejecutaremos un trabajo de referencia en un solo servidor. Luego, lo escalaremos a 2 servidores y 4 servidores y mediremos la aceleración.

En el camino, cubriremos los siguientes temas:

  • Listado de la infraestructura requerida y los componentes de software necesarios,
  • Configuración de nuestro clúster,
  • Instalación de dependencias,
  • Ejecución de un trabajo de un solo nodo,
  • Ejecución de un trabajo distribuido.

¡Manos a la obra!

Usando servidores Intel

Para obtener el mejor rendimiento, utilizaremos servidores Intel basados en la arquitectura Ice Lake, que admiten características de hardware como Intel AVX-512 e Intel Vector Neural Network Instructions (VNNI). Estas características aceleran las operaciones que se encuentran típicamente en el entrenamiento e inferencia de aprendizaje profundo. Puedes obtener más información sobre ellas en esta presentación (PDF).

Los tres principales proveedores de servicios en la nube ofrecen máquinas virtuales con procesadores Intel Ice Lake:

  • Amazon Web Services: instancias Amazon EC2 M6i y C6i.
  • Azure: máquinas virtuales de las series Dv5/Dsv5, Ddv5/Ddsv5 y Edv5/Edsv5.
  • Google Cloud Platform: máquinas virtuales de Compute Engine N2.

Por supuesto, también puedes utilizar tus propios servidores. Si están basados en la arquitectura Cascade Lake (predecesora de Ice Lake), están listos para funcionar, ya que Cascade Lake también incluye AVX-512 y VNNI.

Usando bibliotecas de rendimiento de Intel

Para aprovechar AVX-512 y VNNI en PyTorch, Intel ha diseñado la extensión de Intel para PyTorch . Esta biblioteca de software proporciona una aceleración inmediata para el entrenamiento e inferencia, por lo que definitivamente deberíamos instalarla.

Cuando se trata de entrenamiento distribuido, el principal cuello de botella de rendimiento suele ser la comunicación en red. De hecho, los diferentes nodos del clúster necesitan intercambiar periódicamente información del estado del modelo para mantenerse sincronizados. Como los transformadores son modelos grandes con miles de millones de parámetros (a veces muchos más), el volumen de información es significativo y las cosas solo empeoran a medida que aumenta el número de nodos. Por lo tanto, es importante utilizar una biblioteca de comunicación optimizada para el aprendizaje profundo.

De hecho, PyTorch incluye el paquete torch.distributed, que admite diferentes backends de comunicación. Aquí, utilizaremos la biblioteca de comunicaciones colectivas Intel oneAPI (oneCCL) , una implementación eficiente de los patrones de comunicación utilizados en el aprendizaje profundo ( all-reduce , etc.). Puedes obtener información sobre el rendimiento de oneCCL en comparación con otros backends en esta publicación del blog de PyTorch .

Ahora que tenemos claros los componentes de construcción, hablemos sobre la configuración general de nuestro clúster de entrenamiento.

Configuración de nuestro clúster

En esta demostración, estoy utilizando instancias de Amazon EC2 que ejecutan Amazon Linux 2 (c6i.16xlarge, 64 vCPUs, 128GB de RAM, 25Gbit/s de conexión en red). La configuración será diferente en otros entornos, pero los pasos deberían ser muy similares.

Ten en cuenta que necesitarás 4 instancias idénticas, por lo que es posible que desees planificar alguna forma de automatización para evitar ejecutar la misma configuración 4 veces. Aquí, configuraré una instancia manualmente, crearé una nueva Imagen de Máquina de Amazon (AMI) a partir de esta instancia y usaré esta AMI para lanzar tres instancias idénticas.

Desde una perspectiva de redes, necesitaremos la siguiente configuración:

  • Abrir el puerto 22 para el acceso ssh en todas las instancias para la configuración y la depuración.
  • Configurar una conexión ssh sin contraseña entre la instancia maestra (desde la cual lanzarás el entrenamiento) y todas las demás instancias (incluida la maestra).
  • Abrir todos los puertos TCP en todas las instancias para la comunicación de oneCCL dentro del clúster. Asegúrate de NO abrir estos puertos al mundo exterior. AWS proporciona una forma conveniente de hacer esto al permitir solo conexiones desde instancias que ejecutan un grupo de seguridad específico. Así es como se ve mi configuración.

Ahora, vamos a aprovisionar la primera instancia manualmente. Primero creo la instancia en sí, adjunto el grupo de seguridad anterior y agrego 128GB de almacenamiento. Para optimizar los costos, la he lanzado como una instancia spot.

Una vez que la instancia esté lista, me conectaré a ella con ssh para instalar las dependencias.

Instalación de dependencias

Aquí están los pasos que seguiremos:

  • Instalar las herramientas de Intel,
  • Instalar la distribución de Anaconda,
  • Crear un nuevo entorno conda,
  • Instalar PyTorch y la extensión de Intel para PyTorch,
  • Compilar e instalar oneCCL,
  • Instalar la biblioteca transformers.

Parece mucho, pero no hay nada complicado. ¡Aquí vamos!

Instalando las herramientas de Intel

Primero, descargamos e instalamos el paquete de herramientas base de Intel OneAPI y el paquete de herramientas de IA. Puedes obtener más información sobre ellos en el sitio web de Intel.

wget https://registrationcenter-download.intel.com/akdlm/irc_nas/18236/l_BaseKit_p_2021.4.0.3422_offline.sh
sudo bash l_BaseKit_p_2021.4.0.3422_offline.sh

wget https://registrationcenter-download.intel.com/akdlm/irc_nas/18235/l_AIKit_p_2021.4.0.1460_offline.sh
sudo bash l_AIKit_p_2021.4.0.1460_offline.sh 

Instalando Anaconda

Luego, descargamos e instalamos la distribución de Anaconda.

wget https://repo.anaconda.com/archive/Anaconda3-2021.05-Linux-x86_64.sh
sh Anaconda3-2021.05-Linux-x86_64.sh

Creando un nuevo entorno conda

Cerramos sesión y volvemos a iniciar sesión para actualizar las rutas. Luego, creamos un nuevo entorno conda para mantener las cosas ordenadas.

yes | conda create -n transformer python=3.7.9 -c anaconda
eval "$(conda shell.bash hook)"
conda activate transformer
yes | conda install pip cmake

Instalando PyTorch y la extensión de Intel para PyTorch

A continuación, instalamos PyTorch 1.9 y la extensión de Intel. Las versiones deben coincidir.

yes | conda install pytorch==1.9.0 cpuonly -c pytorch
pip install torch_ipex==1.9.0 -f https://software.intel.com/ipex-whl-stable

Compilando e instalando oneCCL

Luego, instalamos algunas dependencias nativas necesarias para compilar oneCCL.

sudo yum -y update
sudo yum install -y git cmake3 gcc gcc-c++

A continuación, clonamos el repositorio de oneCCL, construimos la biblioteca e instalamos. Nuevamente, las versiones deben coincidir.

source /opt/intel/oneapi/mkl/latest/env/vars.sh
git clone https://github.com/intel/torch-ccl.git
cd torch-ccl
git checkout ccl_torch1.9
git submodule sync
git submodule update --init --recursive
python setup.py install
cd ..

Instalando la biblioteca transformers

A continuación, instalamos la biblioteca transformers y las dependencias necesarias para ejecutar tareas GLUE.

pip install transformers datasets
yes | conda install scipy scikit-learn

Finalmente, clonamos un fork del repositorio transformers que contiene el ejemplo que vamos a ejecutar.

git clone https://github.com/kding1/transformers.git
cd transformers
git checkout dist-sigopt

¡Hemos terminado! Vamos a ejecutar un trabajo de un solo nodo.

Lanzando un trabajo de un solo nodo

Para tener una base, vamos a lanzar un trabajo de un solo nodo ejecutando el script run_glue.py en transformers/examples/pytorch/text-classification. Esto debería funcionar en cualquiera de las instancias, y es una buena verificación antes de pasar al entrenamiento distribuido.

python run_glue.py \
--model_name_or_path bert-base-cased --task_name mrpc \
--do_train --do_eval --max_seq_length 128 \
--per_device_train_batch_size 32 --learning_rate 2e-5 --num_train_epochs 3 \
--output_dir /tmp/mrpc/ --overwrite_output_dir True

Este trabajo tarda 7 minutos y 46 segundos. ¡Ahora, vamos a configurar trabajos distribuidos con oneCCL y acelerar las cosas!

Configurando un trabajo distribuido con oneCCL

Se requieren tres pasos para ejecutar un trabajo de entrenamiento distribuido:

  • Enumerar los nodos del clúster de entrenamiento,
  • Definir variables de entorno,
  • Modificar el script de entrenamiento.

Enumerando los nodos del clúster de entrenamiento

En la instancia maestra, en transformers/examples/pytorch/text-classification, creamos un archivo de texto llamado hostfile. Este archivo almacena los nombres de los nodos en el clúster (las direcciones IP también funcionarían). La primera línea debe apuntar a la instancia maestra.

Este es mi archivo:

ip-172-31-28-17.ec2.internal
ip-172-31-30-87.ec2.internal
ip-172-31-29-11.ec2.internal
ip-172-31-20-77.ec2.internal

Definiendo variables de entorno

A continuación, debemos establecer algunas variables de entorno en el nodo maestro, especialmente su dirección IP. Puede encontrar más información sobre las variables de oneCCL en la documentación.

for nic in eth0 eib0 hib0 enp94s0f0; do
  master_addr=$(ifconfig $nic 2>/dev/null | grep netmask | awk '{print $2}'| cut -f2 -d:)
  if [ "$master_addr" ]; then
    break
  fi
done
export MASTER_ADDR=$master_addr

source /home/ec2-user/anaconda3/envs/transformer/lib/python3.7/site-packages/torch_ccl-1.3.0+43f48a1-py3.7-linux-x86_64.egg/torch_ccl/env/setvars.sh

export LD_LIBRARY_PATH=/home/ec2-user/anaconda3/envs/transformer/lib/python3.7/site-packages/torch_ccl-1.3.0+43f48a1-py3.7-linux-x86_64.egg/:$LD_LIBRARY_PATH
export LD_PRELOAD="${CONDA_PREFIX}/lib/libtcmalloc.so:${CONDA_PREFIX}/lib/libiomp5.so"

export CCL_WORKER_COUNT=4
export CCL_WORKER_AFFINITY="0,1,2,3,32,33,34,35"
export CCL_ATL_TRANSPORT=ofi
export ATL_PROGRESS_MODE=0

Modificando el script de entrenamiento

Ya se han aplicado los siguientes cambios a nuestro script de entrenamiento (run_glue.py) para habilitar el entrenamiento distribuido. Deberás aplicar cambios similares al usar tu propio código de entrenamiento.

  • Importa el paquete torch_ccl.
  • Recibe la dirección del nodo maestro y el rango local del nodo en el clúster.
+import torch_ccl
+
 import datasets
 import numpy as np
 from datasets import load_dataset, load_metric
@@ -47,7 +49,7 @@ from transformers.utils.versions import require_version


 # Dará error si la versión mínima de Transformers no está instalada. Quitar bajo su propio riesgo.
-check_min_version("4.13.0.dev0")
+# check_min_version("4.13.0.dev0")

 require_version("datasets>=1.8.0", "Para solucionar: pip install -r examples/pytorch/text-classification/requirements.txt")

@@ -191,6 +193,17 @@ def main():
     # o pasando el indicador --help a este script.
     # Ahora mantenemos conjuntos distintos de argumentos para una separación más limpia de las preocupaciones.

+    # añadir rango local para cpu-dist
+    sys.argv.append("--local_rank")
+    sys.argv.append(str(os.environ.get("PMI_RANK", -1)))
+
+    # variables de entorno específicas de ccl
+    if "ccl" in sys.argv:
+        os.environ["MASTER_ADDR"] = os.environ.get("MASTER_ADDR", "127.0.0.1")
+        os.environ["MASTER_PORT"] = "29500"
+        os.environ["RANK"] = str(os.environ.get("PMI_RANK", -1))
+        os.environ["WORLD_SIZE"] = str(os.environ.get("PMI_SIZE", -1))
+
     parser = HfArgumentParser((ModelArguments, DataTrainingArguments, TrainingArguments))
     if len(sys.argv) == 2 and sys.argv[1].endswith(".json"):

La configuración está ahora completa. Escalaremos nuestro trabajo de entrenamiento a 2 nodos y 4 nodos.

Ejecutando un trabajo distribuido con oneCCL

En el nodo maestro, uso mpirun para lanzar un trabajo de 2 nodos: -np (número de procesos) se establece en 2 y -ppn (procesos por nodo) se establece en 1. Por lo tanto, se seleccionarán los dos primeros nodos en hostfile.

mpirun -f hostfile -np 2 -ppn 1 -genv I_MPI_PIN_DOMAIN=[0xfffffff0] \
-genv OMP_NUM_THREADS=28 python run_glue.py \
--model_name_or_path distilbert-base-uncased --task_name mrpc \
--do_train --do_eval --max_seq_length 128 --per_device_train_batch_size 32 \
--learning_rate 2e-5 --num_train_epochs 3 --output_dir /tmp/mrpc/ \
--overwrite_output_dir True --xpu_backend ccl --no_cuda True

En cuestión de segundos, se inicia un trabajo en los dos primeros nodos. El trabajo se completa en 4 minutos y 39 segundos, una aceleración de 1,7x.

Al configurar -np en 4 y lanzar un nuevo trabajo, ahora veo un proceso en ejecución en cada nodo del clúster.

El entrenamiento se completa en 2 minutos y 36 segundos, una aceleración de 3x.

Una última cosa. Cambiando --task_name a qqp, también ejecuté la tarea GLUE de Quora Question Pairs, que se basa en un conjunto de datos mucho más grande (más de 400,000 muestras de entrenamiento). Los tiempos de ajuste fino fueron:

  • Un solo nodo: 11 horas 22 minutos,
  • 2 nodos: 6 horas y 38 minutos (1,71x),
  • 4 nodos: 3 horas y 51 minutos (2,95x).

Parece que la aceleración es bastante consistente. ¡Siéntete libre de seguir experimentando con diferentes tasas de aprendizaje, tamaños de lote y configuraciones de oneCCL. Estoy seguro de que puedes ir aún más rápido!

Conclusión

En esta publicación, has aprendido cómo construir un clúster de entrenamiento distribuido basado en CPUs Intel y bibliotecas de rendimiento, y cómo utilizar este clúster para acelerar trabajos de ajuste fino. De hecho, el aprendizaje por transferencia está volviendo a poner el entrenamiento en CPU en juego, y definitivamente deberías considerarlo al diseñar y construir tus próximos flujos de trabajo de aprendizaje profundo.

Gracias por leer esta larga publicación. Espero que te haya resultado informativa. Se aceptan comentarios y preguntas en [email protected]. ¡Hasta la próxima, sigue aprendiendo!

Julien

We will continue to update Zepes; if you have any questions or suggestions, please contact us!

Share:

Was this article helpful?

93 out of 132 found this helpful

Discover more

Inteligencia Artificial

AI Equipaje para Personas con Discapacidad Visual Recibe Excelentes Críticas

La maleta de IA ofrece características que superan a las de los sistemas de guía tradicionales de teléfonos inteligen...

Noticias de Inteligencia Artificial

Google respalda la creación de clínicas de ciberseguridad con una donación de $20 millones.

El CEO de Google, Sundar Pichai, se comprometió a donar $20 millones para apoyar y expandir el Consorcio de Clínicas ...

Inteligencia Artificial

¿Podemos generar imágenes humanas hiperrealistas? Este artículo de IA presenta HyperHuman un avance en modelos de texto a imagen

La computación cuántica se elogia a menudo por su potencial para revolucionar la resolución de problemas, especialmen...

Inteligencia Artificial

Desenmascarando Deepfakes Aprovechando los patrones de estimación de la posición de la cabeza para mejorar la precisión de detección

La aparición de la capacidad de producir videos “falsos” ha generado preocupaciones significativas con re...