Aprendizaje Profundo en Internet Entrenando Modelos de Lenguaje de forma Colaborativa

Aprendizaje Profundo en Internet - Entrenamiento Colaborativo de Modelos de Lenguaje.

Con la ayuda adicional de Quentin Lhoest y Sylvain Lesage.

Los modelos de lenguaje modernos a menudo requieren una cantidad significativa de cálculo para el preentrenamiento, lo que hace imposible obtenerlos sin acceso a decenas y cientos de GPUs o TPUs. Aunque en teoría podría ser posible combinar los recursos de múltiples personas, en la práctica, estos métodos de entrenamiento distribuido han tenido éxito limitado anteriormente debido a que las velocidades de conexión a través de Internet son mucho más lentas que en las supercomputadoras de GPU de alto rendimiento.

En esta publicación de blog, describimos DeDLOC, un nuevo método para el entrenamiento distribuido colaborativo que puede adaptarse a las limitaciones de red y hardware de los participantes. Mostramos que se puede aplicar con éxito en escenarios del mundo real mediante el preentrenamiento de sahajBERT, un modelo para el idioma bengalí, con la ayuda de 40 voluntarios. En tareas posteriores en bengalí, este modelo logra una calidad casi de vanguardia con resultados comparables a modelos mucho más grandes que utilizan cientos de aceleradores de alto nivel.

Aprendizaje profundo distribuido en colaboraciones abiertas

¿Por qué deberíamos hacerlo?

Hoy en día, muchos sistemas de procesamiento de lenguaje natural (NLP) de la más alta calidad se basan en transformadores preentrenados grandes. En general, su calidad mejora con el tamaño: se pueden lograr resultados inigualables en comprensión y generación del lenguaje natural al aumentar el número de parámetros y aprovechar la abundancia de datos de texto no etiquetados.

Lamentablemente, usamos estos modelos preentrenados no solo porque es conveniente. Los recursos de hardware para entrenar transformadores en conjuntos de datos grandes a menudo superan lo que una sola persona o la mayoría de las organizaciones comerciales o de investigación pueden pagar. Tomemos, por ejemplo, BERT: se estimó que su entrenamiento costaba alrededor de $7,000, ¡y para los modelos más grandes como GPT-3, este número puede llegar a $12 millones! Esta limitación de recursos puede parecer obvia e inevitable, pero ¿realmente no hay alternativa para la comunidad de ML en general?

Sin embargo, podría haber una solución a esta situación: para encontrar una solución, solo necesitamos mirar a nuestro alrededor. Podría ser el caso de que los recursos computacionales que estamos buscando ya estén disponibles; por ejemplo, muchos de nosotros tenemos potentes computadoras con GPU para juegos o estaciones de trabajo en casa. Es posible que ya hayas adivinado que vamos a unir su potencia de manera similar a Folding@home, Rosetta@home, Leela Chess Zero u otros proyectos de BOINC que aprovechan la computación voluntaria, pero el enfoque es aún más general. Por ejemplo, varios laboratorios pueden unir sus clústeres más pequeños para utilizar todos los recursos disponibles, y algunos pueden querer unirse al experimento utilizando instancias económicas en la nube.

Para una mente escéptica, podría parecer que estamos pasando por alto un factor clave aquí: la transferencia de datos en el aprendizaje profundo distribuido a menudo es un cuello de botella, ya que necesitamos agregar los gradientes de múltiples trabajadores. De hecho, cualquier enfoque ingenuo para el entrenamiento distribuido a través de Internet está destinado al fracaso, ya que la mayoría de los participantes no tienen conexiones de gigabit y pueden desconectarse de la red en cualquier momento. Entonces, ¿cómo puedes entrenar algo con un plan de datos doméstico? 🙂

Como solución a este problema, proponemos un nuevo algoritmo de entrenamiento llamado Aprendizaje profundo distribuido en colaboraciones abiertas (o DeDLOC), que se describe en detalle en nuestro preprint recientemente publicado. ¡Ahora, descubramos cuáles son las ideas principales detrás de este algoritmo!

Entrenamiento con voluntarios

En su versión más utilizada, el entrenamiento distribuido con múltiples GPUs es bastante sencillo. Recordemos que cuando hacemos aprendizaje profundo, generalmente calculamos los gradientes de nuestra función de pérdida promediados en muchos ejemplos en un lote de datos de entrenamiento. En el caso del aprendizaje profundo distribuido a nivel de datos, simplemente dividimos los datos entre varios trabajadores, calculamos los gradientes por separado y luego los promediamos una vez que se procesan los lotes locales. Una vez que se calcula el gradiente promedio en todos los trabajadores, ajustamos los pesos del modelo con el optimizador y continuamos entrenando nuestro modelo. A continuación, puedes ver una ilustración de las diferentes tareas que se ejecutan.

A menudo, para reducir la cantidad de sincronización y estabilizar el proceso de aprendizaje, podemos acumular los gradientes durante N lotes antes de promediarlos, lo que equivale a aumentar el tamaño del lote real N veces. Este enfoque, combinado con la observación de que la mayoría de los modelos de lenguaje de vanguardia utilizan lotes grandes, nos llevó a una idea simple: ¡acumulemos un lote muy grande en todos los dispositivos de voluntarios antes de cada paso del optimizador! Junto con la equivalencia completa al entrenamiento distribuido regular y la escalabilidad fácil, este método también tiene la ventaja de tener tolerancia a fallos incorporada, como ilustramos a continuación.

Consideremos un par de casos de fallas potenciales que podríamos encontrar durante un experimento colaborativo. Por mucho, el escenario más frecuente es que uno o varios participantes se desconecten del procedimiento de entrenamiento: pueden tener una conexión inestable o simplemente querer utilizar sus GPUs para otra cosa. En este caso, solo sufrimos un pequeño contratiempo en el entrenamiento: la contribución de estos participantes se resta del tamaño del lote acumulado en ese momento, pero otros participantes compensarán eso con sus gradientes. Además, si más participantes se unen, el tamaño objetivo del lote se alcanzará más rápido y nuestro procedimiento de entrenamiento acelerará naturalmente. Puedes ver una demostración de esto en el video:

Promediado adaptativo

Ahora que hemos discutido el procedimiento general de entrenamiento, queda una pregunta más: ¿cómo agregamos realmente los gradientes de los participantes? La mayoría de las computadoras domésticas no pueden aceptar fácilmente conexiones entrantes, y la velocidad de descarga también puede convertirse en una limitación.

Dado que confiamos en hardware voluntario para experimentos, un servidor central no es realmente una opción viable, ya que rápidamente se enfrentaría a sobrecarga al escalar a decenas de clientes y cientos de millones de parámetros. La mayoría de las ejecuciones de entrenamiento de datos paralelos hoy en día no utilizan esta estrategia de todos modos; en cambio, confían en All-Reduce, una primitiva de comunicación eficiente de todos a todos. Gracias a las optimizaciones algorítmicas inteligentes, cada nodo puede calcular el promedio global sin enviar todo el gradiente local a cada par.

Debido a que All-Reduce es descentralizado, parece ser una buena elección; sin embargo, aún debemos tener en cuenta la diversidad de hardware y configuraciones de red. Por ejemplo, algunos voluntarios pueden unirse desde computadoras que tienen una red lenta pero potentes GPU, otros pueden tener una mejor conectividad solo con un subconjunto de otros pares, y algunos pueden tener un cortafuegos que bloquea las conexiones entrantes.

Resulta que en realidad podemos idear una estrategia óptima de transferencia de datos sobre la marcha aprovechando esta información sobre el rendimiento. En un nivel alto, dividimos el vector de gradiente completo en partes dependiendo de la velocidad de Internet de cada par: aquellos con la conexión más rápida agregan las partes más grandes. Además, si algunos nodos no aceptan conexiones entrantes, simplemente envían sus datos para la agregación pero no calculan el promedio ellos mismos. Dependiendo de las condiciones, este algoritmo adaptativo puede recuperar algoritmos de DL distribuidos conocidos y mejorarlos con una estrategia híbrida, como se muestra a continuación.

sahajBERT

Como siempre, tener un marco algorítmico bien diseñado no significa que funcionará según lo previsto en la práctica, ya que algunas suposiciones pueden no ser ciertas en las ejecuciones de entrenamiento reales. Para verificar el rendimiento competitivo de esta tecnología y mostrar su potencial, organizamos un evento colaborativo especial para preentrenar un modelo de lenguaje enmascarado para el idioma bengalí. Aunque es el quinto idioma nativo más hablado en el mundo, tiene muy pocos modelos de lenguaje enmascarados disponibles abiertamente, lo que enfatiza la importancia de las herramientas que pueden capacitar a la comunidad, desbloqueando un sinfín de oportunidades en el campo.

Realizamos este experimento con voluntarios reales de la comunidad Neuropark y utilizamos conjuntos de datos disponibles abiertamente (OSCAR y Wikipedia), porque queríamos tener un ejemplo completamente reproducible que pudiera servir de inspiración para otros grupos. A continuación, describimos la configuración detallada de nuestra ejecución de entrenamiento y demostramos sus resultados.

Arquitectura

Para nuestro experimento, elegimos ALBERT (A Lite BERT), un modelo de representaciones de lenguaje que se preentrena con Modelado de Lenguaje Enmascarado (MLM) y Predicción del Orden de las Oraciones (SOP) como objetivos. Utilizamos esta arquitectura porque el compartir pesos la hace muy eficiente en cuanto a parámetros: por ejemplo, ALBERT-large tiene ~18M de parámetros entrenables y tiene un rendimiento comparable a BERT-base con ~108M de pesos en el benchmark GLUE. Esto significa que hay menos datos para intercambiar entre los pares, lo cual es crucial en nuestra configuración, ya que acelera significativamente cada iteración de entrenamiento.

Tokenizador

El primer componente de nuestro modelo se llama tokenizador y se encarga de transformar el texto sin procesar en índices de vocabulario. Dado que estamos entrenando un modelo para bengalí, que no es muy similar al inglés, necesitamos implementar un preprocesamiento específico del idioma como parte de nuestro tokenizador. Podemos verlo como una secuencia de operaciones:

  1. Normalización: incluye todas las operaciones de preprocesamiento en los datos de texto sin procesar. Este fue el paso en el que realizamos la mayoría de los cambios, porque eliminar ciertos detalles puede cambiar el significado del texto o dejarlo igual, dependiendo del idioma. Por ejemplo, el normalizador estándar de ALBERT elimina los acentos, mientras que para el idioma bengalí, necesitamos conservarlos, ya que contienen información sobre las vocales. Como resultado, utilizamos las siguientes operaciones: normalización NMT, normalización NFKC, eliminación de múltiples espacios, homogeneización de caracteres Unicode recurrentes en el idioma bengalí y conversión a minúsculas.
  2. Pretokenización: describe las reglas para dividir la entrada (por ejemplo, por espacios en blanco) para imponer límites de token específicos. Como en el trabajo original, hemos elegido mantener el espacio en blanco fuera de los tokens. Por lo tanto, para distinguir las palabras entre sí y no tener múltiples tokens de un solo espacio, cada token correspondiente al inicio de una palabra comienza con un carácter especial “_”(U+2581). Además, hemos aislado toda la puntuación y los dígitos de otros caracteres para condensar nuestro vocabulario.
  3. Modelado del tokenizador: Es en este nivel donde el texto se asigna a una secuencia de elementos de un vocabulario. Hay varios algoritmos para esto, como Codificación de Parejas de Bytes (BPE) o Unigram, y la mayoría de ellos necesitan construir el vocabulario a partir de un corpus de texto. Siguiendo la configuración de ALBERT, utilizamos el enfoque del Modelo de Lenguaje Unigram, entrenando un vocabulario de 32k tokens en la parte bengalí deduplicada del conjunto de datos OSCAR.
  4. Post-procesamiento: Después de la tokenización, es posible que deseemos agregar varios tokens especiales requeridos por la arquitectura, como comenzar la secuencia con un token especial [CLS] o separar dos segmentos con un token especial [SEP]. Dado que nuestra arquitectura principal es la misma que la de ALBERT original, mantenemos el mismo post-procesamiento: específicamente, agregamos un token [CLS] al principio de cada ejemplo y un token [SEP] tanto entre dos segmentos como al final.

Puedes reutilizar nuestro tokenizer ejecutando el siguiente código:

from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("neuropark/sahajBERT")

Conjunto de datos

Lo último que necesitamos cubrir es el conjunto de datos de entrenamiento. Como probablemente sabes, la gran fortaleza de los modelos preentrenados como BERT o ALBERT es que no necesitas un conjunto de datos anotados, sino simplemente una gran cantidad de textos. Para entrenar sahajBERT, utilizamos el volcado de la Wikipedia en bengalí del 20/03/2021 y el subconjunto en bengalí de OSCAR (600MB + 6GB de texto). Estos dos conjuntos de datos se pueden descargar fácilmente desde el HF Hub.

Sin embargo, cargar un conjunto de datos completo requiere tiempo y almacenamiento, dos cosas que nuestros compañeros no necesariamente tienen. Para aprovechar al máximo los recursos proporcionados por los participantes, hemos implementado la transmisión de conjuntos de datos, lo que les permite entrenar el modelo casi tan pronto como se unen a la red. Específicamente, los ejemplos en el conjunto de datos se descargan y transforman en paralelo durante el entrenamiento. También podemos mezclar el conjunto de datos para que nuestros compañeros tengan pocas posibilidades de procesar los mismos ejemplos al mismo tiempo. Como el conjunto de datos no se descarga y preprocesa de antemano, las transformaciones necesarias para pasar de texto plano a un ejemplo de entrenamiento (que se muestra en la figura a continuación) se realizan sobre la marcha.

El modo de transmisión de conjuntos de datos está disponible a partir de la versión v1.9 de la biblioteca 🤗 datasets, por lo que puedes usarlo ahora mismo de la siguiente manera:

from datasets import load_dataset

oscar_dataset = load_dataset("oscar", name="unshuffled_deduplicated_bn", streaming=True)

Evento de colaboración

El evento de entrenamiento colaborativo de sahajBERT tuvo lugar del 12 al 21 de mayo. El evento reunió a 40 participantes, 30 de los cuales eran voluntarios que hablaban bengalí, y 10 eran voluntarios de una de las organizaciones de los autores. Estos 40 voluntarios se unieron al canal de Discord de Neuropark para recibir toda la información relacionada con el evento y participar en las discusiones. Para unirse al experimento, se les pidió a los voluntarios que:

  1. Envíen su nombre de usuario a los moderadores para ser incluidos en la lista de permitidos;
  2. Abren el cuaderno proporcionado localmente, en Google Colaboratory o en Kaggle;
  3. Ejecuten una celda de código y completen sus credenciales de Hugging Face cuando se les solicite;
  4. ¡Observen cómo disminuye la pérdida de entrenamiento en los paneles compartidos!

Por motivos de seguridad, establecimos un sistema de autorización para que solo los miembros de la comunidad de Neuropark pudieran entrenar el modelo. Sin entrar en detalles técnicos, nuestro protocolo de autorización nos permite garantizar que cada participante esté en la lista de permitidos y reconocer la contribución individual de cada compañero.

En la siguiente figura, puedes ver la actividad de cada voluntario. Durante el experimento, los voluntarios iniciaron sesión en 600 sesiones diferentes. Los participantes lanzaron regularmente múltiples ejecuciones en paralelo, y muchos de ellos distribuyeron las ejecuciones que lanzaron a lo largo del tiempo. Las ejecuciones de los participantes individuales duraron en promedio 4 horas, y la duración máxima fue de 21 horas. Puedes obtener más información sobre las estadísticas de participación en el artículo.

Junto con los recursos proporcionados por los participantes, también utilizamos 16 instancias en la nube de GPU individuales T4 preemptibles (económicas pero interrumpidas con frecuencia) para garantizar la estabilidad de la ejecución. El tiempo total de ejecución del experimento fue de 234 días, ¡y en la figura a continuación puedes ver las partes de la curva de pérdida a las que cada compañero contribuyó!

El modelo final se cargó en el Model Hub, por lo que puedes descargarlo y jugar con él si quieres: https://hf.co/neuropark/sahajBERT

Evaluación

Para evaluar el rendimiento de sahajBERT, lo afinamos en dos tareas secundarias en bengalí:

  • Reconocimiento de entidades nombradas (NER) en la división bengalí de WikiANN. El objetivo de esta tarea es clasificar cada token del texto de entrada en una de las siguientes categorías: persona, organización, ubicación o ninguna de ellas.
  • Clasificación de categoría de noticias (NCC) en el conjunto de datos de artículos Soham de IndicGLUE. El objetivo de esta tarea es predecir la categoría a la que pertenece el texto de entrada.

Lo evaluamos durante el entrenamiento en la tarea de NER para verificar que todo iba bien; como puedes ver en la siguiente gráfica, ¡así fue!

Al final del entrenamiento, comparamos sahajBERT con otros tres modelos de lenguaje preentrenados: XLM-R Large, IndicBert y bnRoBERTa. En la tabla a continuación, puedes ver que nuestro modelo tiene resultados comparables a los mejores modelos de lenguaje bengalí disponibles en HF Hub, a pesar de que nuestro modelo tiene solo ~18M de parámetros entrenados, mientras que, por ejemplo, XLM-R (una línea de base multilingüe sólida) tiene ~559M de parámetros y fue entrenado en varias cientos de GPUs V100.

Estos modelos también están disponibles en el Hub. Puedes probarlos directamente jugando con el widget de la API de Inferencia Alojada en sus Tarjetas de Modelo o cargándolos directamente en tu código Python.

sahajBERT-NER

Tarjeta de modelo: https://hf.co/neuropark/sahajBERT-NER

from transformers import (
    AlbertForTokenClassification,
    TokenClassificationPipeline,
    PreTrainedTokenizerFast,
)

# Inicializar tokenizer
tokenizer = PreTrainedTokenizerFast.from_pretrained("neuropark/sahajBERT-NER")

# Inicializar modelo
model = AlbertForTokenClassification.from_pretrained("neuropark/sahajBERT-NER")

# Inicializar pipeline
pipeline = TokenClassificationPipeline(tokenizer=tokenizer, model=model)

raw_text = "এই ইউনিয়নে ৩ টি মৌজা ও ১০ টি গ্রাম আছে ।" # Cambia esto
output = pipeline(raw_text)

sahajBERT-NCC

Tarjeta de modelo: https://hf.co/neuropark/sahajBERT-NER

from transformers import (
    AlbertForSequenceClassification,
    TextClassificationPipeline,
    PreTrainedTokenizerFast,
)

# Inicializar tokenizer
tokenizer = PreTrainedTokenizerFast.from_pretrained("neuropark/sahajBERT-NCC")

# Inicializar modelo
model = AlbertForSequenceClassification.from_pretrained("neuropark/sahajBERT-NCC")

# Inicializar pipeline
pipeline = TextClassificationPipeline(tokenizer=tokenizer, model=model)

raw_text = "এই ইউনিয়নে ৩ টি মৌজা ও ১০ টি গ্রাম আছে ।" # Cambia esto
output = pipeline(raw_text)

Conclusión

En esta publicación del blog, hemos discutido el método que permite el preentrenamiento colaborativo de redes neuronales con sahajBERT como el primer ejemplo verdaderamente exitoso de su aplicación a un problema del mundo real.

¿Qué significa todo esto para la comunidad de ML en general? En primer lugar, ahora es posible ejecutar preentrenamientos distribuidos a gran escala con tus amigos, y esperamos ver muchos modelos nuevos geniales que antes eran menos factibles de obtener. Además, nuestros resultados podrían ser importantes para el procesamiento de lenguaje natural multilingüe, ya que ahora la comunidad de cualquier idioma puede entrenar sus propios modelos sin la necesidad de recursos computacionales significativos concentrados en un solo lugar.

Agradecimientos

El artículo DeDLOC y el experimento de entrenamiento de sahajBERT fueron creados por Michael Diskin, Alexey Bukhtiyarov, Max Ryabinin, Lucile Saulnier, Quentin Lhoest, Anton Sinitsin, Dmitry Popov, Dmitry Pyrkin, Maxim Kashirin, Alexander Borzunov, Albert Villanova del Moral, Denis Mazur, Ilia Kobelev, Yacine Jernite, Thomas Wolf y Gennady Pekhimenko. Este proyecto es el resultado de una colaboración entre Hugging Face, Yandex Research, HSE University, MIPT, University of Toronto y Vector Institute.

Además, nos gustaría agradecer a Stas Bekman, Dmitry Abulkhanov, Roman Zhytar, Alexander Ploshkin, Vsevolod Plokhotnyuk y Roman Kail por su valiosa ayuda en la construcción de la infraestructura de entrenamiento. También agradecemos a Abhishek Thakur por ayudar con la evaluación posterior y a Tanmoy Sarkar con Omar Sanseviero, quienes nos ayudaron a organizar el experimento colaborativo y brindaron actualizaciones regulares de estado a los participantes durante el transcurso de la ejecución del entrenamiento.

A continuación, puedes ver a todos los participantes del experimento colaborativo:

Referencias

“Distributed Deep Learning in Open Collaborations”, ArXiv

Código para los experimentos de sahajBERT en el repositorio DeDLOC.