Preentrena BERT con Hugging Face Transformers y Habana Gaudi

Entrena BERT con Hugging Face Transformers y Habana Gaudi.

En este tutorial, aprenderás cómo pre-entrenar BERT-base desde cero utilizando una instancia DL1 basada en Habana Gaudi en AWS para aprovechar los beneficios de rendimiento y costo de Gaudi. Utilizaremos las bibliotecas Hugging Face Transformers, Optimum Habana y Datasets para pre-entrenar un modelo BERT-base utilizando el modelado de lenguaje enmascarado, una de las dos tareas originales de pre-entrenamiento de BERT. Antes de comenzar, debemos configurar el entorno de aprendizaje profundo.

Ver Código

Aprenderás cómo:

  1. Preparar el conjunto de datos
  2. Entrenar un Tokenizer
  3. Preprocesar el conjunto de datos
  4. Pre-entrenar BERT en Habana Gaudi

Nota: Los pasos 1 al 3 deben ejecutarse en un tamaño de instancia diferente, ya que estas son tareas intensivas en CPU.

Requisitos

Antes de comenzar, asegúrate de cumplir con los siguientes requisitos:

  • Cuenta de AWS con cuota para el tipo de instancia DL1
  • CLI de AWS instalado
  • Usuario IAM de AWS configurado en CLI con permisos para crear y gestionar instancias EC2

Recursos útiles

  • Configuración del entorno de aprendizaje profundo para Hugging Face Transformers con Habana Gaudi en AWS
  • Configuración de aprendizaje profundo simplificada con EC2 Remote Runner y Habana Gaudi
  • Documentación de Optimum Habana
  • Script de pre-entrenamiento
  • Código: pre-training-bert.ipynb

¿Qué es BERT?

BERT, abreviatura de Bidirectional Encoder Representations from Transformers, es un modelo de aprendizaje automático (ML) para el procesamiento del lenguaje natural. Fue desarrollado en 2018 por investigadores de Google AI Language y sirve como una solución de navaja suiza para más de 11 tareas comunes de lenguaje, como análisis de sentimientos y reconocimiento de entidades nombradas.

Lee más sobre BERT en nuestro blog BERT 101 🤗 Explicación del modelo NLP de última generación.

¿Qué es un modelo de lenguaje enmascarado (MLM)?

MLM permite/implementa el aprendizaje bidireccional a partir de texto al enmascarar (ocultar) una palabra en una oración y obligar a BERT a utilizar bidireccionalmente las palabras a cada lado de la palabra oculta para predecir la palabra enmascarada.

Ejemplo de Modelado de Lenguaje Enmascarado:

"¡Vaya! ¡Estoy pescando y un enorme trucha acaba de [MASK] mi línea!"

Lee más sobre el Modelado de Lenguaje Enmascarado aquí.


Empecemos. 🚀

Nota: Los pasos 1 al 3 se ejecutaron en una instancia AWS c6i.12xlarge.

1. Preparar el conjunto de datos

El tutorial está “dividido” en dos partes. La primera parte (paso 1-3) se trata de preparar el conjunto de datos y el tokenizador. La segunda parte (paso 4) se trata de pre-entrenar BERT en el conjunto de datos preparado. Antes de poder comenzar con la preparación del conjunto de datos, debemos configurar nuestro entorno de desarrollo. Como se mencionó en la introducción, no es necesario preparar el conjunto de datos en la instancia DL1 y se puede utilizar su cuaderno o computadora de escritorio.

Primero vamos a instalar transformers, datasets y git-lfs para enviar nuestro tokenizador y conjunto de datos al Hugging Face Hub para su uso posterior.

!pip install transformers datasets
!sudo apt-get install git-lfs

Para finalizar nuestra configuración, iniciemos sesión en el Hugging Face Hub para enviar nuestro conjunto de datos, tokenizador, artefactos del modelo, registros y métricas durante el entrenamiento y después al Hub.

Para poder enviar nuestro modelo al Hub, debes registrarte en el Hugging Face Hub.

Utilizaremos la utilidad notebook_login del paquete huggingface_hub para iniciar sesión en nuestra cuenta. Puedes obtener tu token en la configuración en Access Tokens.

from huggingface_hub import notebook_login

notebook_login()

Ya que estamos conectados, obtengamos el user_id, que se utilizará para enviar los artefactos.

from huggingface_hub import HfApi

user_id = HfApi().whoami()["name"]

print(f"se utilizará el id de usuario '{user_id}' durante el ejemplo")

El BERT original fue pre-entrenado en los conjuntos de datos de Wikipedia y BookCorpus. Ambos conjuntos de datos están disponibles en Hugging Face Hub y se pueden cargar con datasets.

Nota: Para Wikipedia utilizaremos el 20220301, que es diferente a la división original.

Como primer paso, estamos cargando los conjuntos de datos y fusionándolos para crear un gran conjunto de datos.

from datasets import concatenate_datasets, load_dataset

bookcorpus = load_dataset("bookcorpus", split="train")
wiki = load_dataset("wikipedia", "20220301.en", split="train")
wiki = wiki.remove_columns([col for col in wiki.column_names if col != "text"])  # solo mantenemos la columna 'text'

assert bookcorpus.features.type == wiki.features.type
raw_datasets = concatenate_datasets([bookcorpus, wiki])

No vamos a realizar una preparación avanzada del conjunto de datos, como la eliminación de duplicados, filtrado u otro preprocesamiento. Si planeas aplicar este cuaderno para entrenar tu propio modelo BERT desde cero, te recomiendo encarecidamente incluir esos pasos de preparación de datos en tu flujo de trabajo. Esto te ayudará a mejorar tu modelo de lenguaje.

2. Entrenar un Tokenizador

Para poder entrenar nuestro modelo, necesitamos convertir nuestro texto en un formato tokenizado. La mayoría de los modelos Transformer vienen con un tokenizador pre-entrenado, pero como estamos pre-entrenando nuestro modelo desde cero, también necesitamos entrenar un Tokenizador en nuestros datos. Podemos entrenar un tokenizador en nuestros datos con transformers y la clase BertTokenizerFast.

Más información sobre cómo entrenar un nuevo tokenizador se puede encontrar en nuestro Curso de Hugging Face.

from tqdm import tqdm
from transformers import BertTokenizerFast

# id del repositorio para guardar el tokenizador
tokenizer_id="bert-base-uncased-2022-habana"

# crear un generador de Python para cargar dinámicamente los datos
def batch_iterator(batch_size=10000):
    for i in tqdm(range(0, len(raw_datasets), batch_size)):
        yield raw_datasets[i : i + batch_size]["text"]

# crear un tokenizador a partir de uno existente para reutilizar los tokens especiales
tokenizer = BertTokenizerFast.from_pretrained("bert-base-uncased")

Podemos comenzar a entrenar el tokenizador con train_new_from_iterator().

bert_tokenizer = tokenizer.train_new_from_iterator(text_iterator=batch_iterator(), vocab_size=32_000)
bert_tokenizer.save_pretrained("tokenizer")

Ponemos el tokenizador en Hugging Face Hub para entrenar nuestro modelo más adelante.

# debes iniciar sesión para subir el tokenizador
bert_tokenizer.push_to_hub(tokenizer_id)

3. Preprocesar el conjunto de datos

Antes de empezar a entrenar nuestro modelo, el último paso es preprocesar/tokenizar nuestro conjunto de datos. Utilizaremos nuestro tokenizador entrenado para tokenizar nuestro conjunto de datos y luego subirlo al hub para cargarlo fácilmente más adelante en nuestro entrenamiento. El proceso de tokenización también es bastante simple, si los documentos tienen más de 512 tokens, se truncarán y no se dividirán en varios documentos.

from transformers import AutoTokenizer
import multiprocessing

# cargar el tokenizador
# tokenizer = AutoTokenizer.from_pretrained(f"{user_id}/{tokenizer_id}")
tokenizer = AutoTokenizer.from_pretrained("tokenizer")
num_proc = multiprocessing.cpu_count()
print(f"La longitud máxima para el tokenizador es: {tokenizer.model_max_length}")

def group_texts(examples):
    tokenized_inputs = tokenizer(
       examples["text"], return_special_tokens_mask=True, truncation=True, max_length=tokenizer.model_max_length
    )
    return tokenized_inputs

# preprocesar el conjunto de datos
tokenized_datasets = raw_datasets.map(group_texts, batched=True, remove_columns=["text"], num_proc=num_proc)
tokenized_datasets.features

Como función de procesamiento de datos, concatenaremos todos los textos de nuestro conjunto de datos y generaremos fragmentos de tokenizer.model_max_length (512).

from itertools import chain

# Función principal de procesamiento de datos que concatenará todos los textos de nuestro conjunto de datos y generará fragmentos de
# max_seq_length.
def group_texts(examples):
    # Concatenar todos los textos.
    concatenated_examples = {k: list(chain(*examples[k])) for k in examples.keys()}
    total_length = len(concatenated_examples[list(examples.keys())[0]])
    # Descartamos el resto pequeño, podríamos agregar relleno si el modelo lo admitiera en lugar de descartarlo, puedes
    # personalizar esta parte según tus necesidades.
    if total_length >= tokenizer.model_max_length:
        total_length = (total_length // tokenizer.model_max_length) * tokenizer.model_max_length
    # Dividir en fragmentos de max_len.
    result = {
        k: [t[i : i + tokenizer.model_max_length] for i in range(0, total_length, tokenizer.model_max_length)]
        for k, t in concatenated_examples.items()
    }
    return result

tokenized_datasets = tokenized_datasets.map(group_texts, batched=True, num_proc=num_proc)
# mezclar el conjunto de datos
tokenized_datasets = tokenized_datasets.shuffle(seed=34)

print(f"el conjunto de datos contiene en total {len(tokenized_datasets)*tokenizer.model_max_length} tokens")
# el conjunto de datos contiene en total 3417216000 tokens

El último paso antes de comenzar nuestro entrenamiento es enviar nuestro conjunto de datos preparado al hub.

# enviar conjunto de datos a Hugging Face
dataset_id=f"{user_id}/conjunto_datos_bert_procesado"
tokenized_datasets.push_to_hub(f"{user_id}/conjunto_datos_bert_procesado")

4. Pre-entrenar BERT en Habana Gaudi

En este ejemplo, vamos a utilizar Habana Gaudi en AWS utilizando la instancia DL1 para ejecutar el pre-entrenamiento. Utilizaremos la herramienta Remote Runner para lanzar fácilmente nuestro pre-entrenamiento en una instancia remota DL1 desde nuestra configuración local. Si deseas saber más sobre cómo funciona esto, puedes consultar Configuración de aprendizaje profundo simplificada con EC2 Remote Runner y Habana Gaudi.

!pip install rm-runner

Cuando se utilizan GPUs, se utilizaría el Trainer y TrainingArguments. Dado que vamos a ejecutar nuestro entrenamiento en Habana Gaudi, estamos aprovechando la biblioteca optimum-habana, por lo que en su lugar podemos utilizar GaudiTrainer y GaudiTrainingArguments. El GaudiTrainer es un envoltorio alrededor del Trainer que te permite pre-entrenar o ajustar un modelo de transformador en instancias Habana Gaudi.

-from transformers import Trainer, TrainingArguments
+from optimum.habana import GaudiTrainer, GaudiTrainingArguments

# definir los argumentos de entrenamiento
-training_args = TrainingArguments(
+training_args = GaudiTrainingArguments(
+  use_habana=True,
+  use_lazy_mode=True,
+  gaudi_config_name=ruta_a_gaudi_config,
  ...
)

# Inicializar nuestro Trainer
-trainer = Trainer(
+trainer = GaudiTrainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset
    ... # otros argumentos
)

La instancia DL1 que utilizamos tiene 8 núcleos HPU disponibles, lo que significa que podemos aprovechar el entrenamiento de datos paralelos distribuidos para nuestro modelo. Para ejecutar nuestro entrenamiento como entrenamiento distribuido, necesitamos crear un script de entrenamiento que se pueda utilizar con multiprocessing para ejecutarlo en todos los HPU. Hemos creado un script run_mlm.py que implementa el modelado de lenguaje enmascarado utilizando el GaudiTrainer. Para ejecutar nuestro entrenamiento distribuido, utilizamos el runner DistributedRunner de optimum-habana y pasamos nuestros argumentos. Alternativamente, podrías consultar el archivo gaudi_spawn.py en el repositorio de optimum-habana.

Antes de comenzar nuestro entrenamiento, necesitamos definir los hiperparámetros que queremos utilizar para nuestro entrenamiento. Estamos aprovechando la integración de Hugging Face Hub del GaudiTrainer para enviar automáticamente nuestros puntos de control, registros y métricas durante el entrenamiento a un repositorio.

from huggingface_hub import HfFolder

# hiperparámetros
hiperparametros = {
    "model_config_id": "bert-base-uncased",
    "dataset_id": "philschmid/conjunto_datos_bert_procesado",
    "tokenizer_id": "philschmid/bert-base-uncased-2022-habana",
    "gaudi_config_id": "philschmid/bert-base-uncased-2022-habana",
    "repository_id": "bert-base-uncased-2022",
    "hf_hub_token": HfFolder.get_token(),  # es necesario iniciar sesión con `huggingface-cli login`
    "max_steps": 100_000,
    "per_device_train_batch_size": 32,
    "learning_rate": 5e-5,
}
cadena_hiperparametros = " ".join(f"--{key} {value}" for key, value in hiperparametros.items())

Podemos comenzar nuestro entrenamiento creando un EC2RemoteRunner y luego lanzándolo. Esto iniciará nuestra instancia AWS EC2 DL1 y ejecutará nuestro script run_mlm.py en ella utilizando el contenedor huggingface/optimum-habana:latest.

from rm_runner import EC2RemoteRunner
# crear el runner remoto de EC2
runner = EC2RemoteRunner(
  instance_type="dl1.24xlarge",
  profile="hf-sm",  # ajustar según tu perfil
  region="us-east-1",
  container="huggingface/optimum-habana:4.21.1-pt1.11.0-synapse1.5.0"
  )

# lanzar mi script con gaudi_spawn para entrenamiento distribuido
runner.launch(
    command=f"python3 gaudi_spawn.py --use_mpi --world_size=8 run_mlm.py {cadena_hiperparametros}",
    source_dir="scripts",
)

Este experimento se ejecutó durante 60k pasos.

En nuestros hiperparámetros definimos una propiedad max_steps, que limitó el pre-entrenamiento a solo 100_000 pasos. Los 100_000 pasos con un tamaño de lote global de 256 tomaron alrededor de 12,5 horas.

BERT fue originalmente pre-entrenado en 1 millón de pasos con un tamaño de lote global de 256:

Entrenamos con un tamaño de lote de 256 secuencias (256 secuencias * 512 tokens = 128,000 tokens/lote) durante 1,000,000 pasos, lo cual equivale aproximadamente a 40 épocas en el corpus de 3.3 mil millones de palabras.

Esto significa que si queremos hacer un pre-entrenamiento completo, tomaría alrededor de 125 horas (12,5 horas * 10) y nos costaría alrededor de ~$1,650 utilizando Habana Gaudi en AWS, lo cual es extremadamente barato.

Para comparación, el equipo de DeepSpeed, que tiene el récord del pre-entrenamiento más rápido de BERT, informó que el pre-entrenamiento de BERT en 1 DGX-2 (con 16 GPU NVIDIA V100 con 32GB de memoria cada una) tarda alrededor de 33,25 horas.

Para comparar el costo, podemos usar la instancia p3dn.24xlarge como referencia, que viene con 8 GPU NVIDIA V100 32GB y cuesta ~31,22$/h. Necesitaríamos dos de estas instancias para tener la misma “configuración” que la que informó DeepSpeed, por ahora estamos ignorando cualquier sobrecarga creada por la configuración multinodo (E/S, red, etc.). Esto llevaría el costo del entrenamiento basado en GPU de DeepSpeed en AWS a alrededor de ~$2,075, lo cual es un 25% más que lo que ofrece Habana Gaudi en la actualidad.

Algo a tener en cuenta aquí es que el uso de DeepSpeed en general mejora el rendimiento en un factor de ~1.5 – 2. Un factor de ~1.5 – 2x significa que el mismo trabajo de pre-entrenamiento sin DeepSpeed probablemente tomaría el doble de tiempo y costaría el doble o ~$3-4k.

Esperamos volver a realizar el experimento una vez que la integración de Gaudi DeepSpeed esté más ampliamente disponible.

Conclusión

Eso es todo para este tutorial. Ahora conoces los conceptos básicos de cómo pre-entrenar BERT desde cero utilizando Hugging Face Transformers y Habana Gaudi. También viste lo fácil que es migrar del Trainer al GaudiTrainer.

Comparamos nuestra implementación con los resultados de pre-entrenamiento más rápidos y vimos que Habana Gaudi aún ofrece una reducción de costos del 25% y nos permite pre-entrenar BERT por ~$1,650.

Estos resultados son increíbles, ya que permitirán a las empresas adaptar sus modelos pre-entrenados a su idioma y dominio para mejorar la precisión hasta en un 10% en comparación con los modelos BERT generales.

Si estás interesado en entrenar tu propio BERT u otros modelos Transformers desde cero para reducir costos y mejorar la precisión, contacta a nuestros expertos para obtener información sobre nuestro Programa de Aceleración de Expertos . Para obtener más información sobre las soluciones de Habana, lee sobre nuestra asociación y cómo ponerte en contacto con ellos .

Código: pre-training-bert.ipynb


¡Gracias por leer! Si tienes alguna pregunta, no dudes en contactarme a través de Github , o en el foro . También puedes conectarte conmigo en Twitter o LinkedIn .