Modelos y plataformas de IA
OptimizaciÃģn de la memoria para inferencia y ajuste fino de modelos de lenguaje grande
Los modelos de lenguaje grande (LLM) como GPT-4, Bloom y LLaMA han logrado capacidades notables al escalar hasta miles de millones de parÃĄmetros. Sin embargo, desplegar estos modelos masivos para inferencia o ajuste fino es un desafÃo debido a sus enormes requisitos de memoria. En este blog tÃĐcnico, exploraremos tÃĐcnicas para estimar y optimizar el consumo de memoria durante la inferencia y el ajuste fino de LLM en varios entornos de hardware.
ComprensiÃģn de los requisitos de memoria
La memoria necesaria para cargar un LLM se determina principalmente por la cantidad de parÃĄmetros y la precisiÃģn numÃĐrica utilizada para almacenar los parÃĄmetros. Una regla general es:
- Cargar un modelo con X mil millones de parÃĄmetros requiere aproximadamente 4X GB de VRAM en precisiÃģn de 32 bits float
- Cargar un modelo con X mil millones de parÃĄmetros requiere aproximadamente 2X GB de VRAM en precisiÃģn de 16 bits bfloat16/float16
Por ejemplo, cargar el modelo GPT-3 de 175 mil millones de parÃĄmetros requerirÃa aproximadamente 350 GB de VRAM en precisiÃģn bfloat16. Actualmente, las GPU mÃĄs grandes disponibles comercialmente, como la NVIDIA A100 y H100, ofrecen solo 80 GB de VRAM, lo que hace necesarias tÃĐcnicas de paralelismo de tensor y paralelismo de modelo.
Durante la inferencia, la huella de memoria estÃĄ dominada por los parÃĄmetros del modelo y los tensores de activaciÃģn temporales producidos. Una estimaciÃģn de alto nivel para el uso mÃĄximo de memoria durante la inferencia es la suma de la memoria necesaria para cargar los parÃĄmetros del modelo y la memoria para las activaciones.
CuantificaciÃģn de la memoria de inferencia
Veamos los requisitos de memoria para la inferencia utilizando el modelo OctoCode, que tiene alrededor de 15 mil millones de parÃĄmetros en formato bfloat16 (~ 31 GB). Utilizaremos la biblioteca Transformers para cargar el modelo y generar texto:
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline import torch <p>model = AutoModelForCausalLM.from_pretrained("bigcode/octocoder", torch_dtype=torch.bfloat16, device_map="auto", pad_token_id=0) tokenizer = AutoTokenizer.from_pretrained("bigcode/octocoder") pipe = pipeline("text-generation", model=model, tokenizer=tokenizer)</p> <p>prompt = "Pregunta: Por favor, escriba una funciÃģn de Python para convertir bytes a gigabytes.\n\nRespuesta:" result = pipe(prompt, max_new_tokens=60)[0]["generated_text"][len(prompt):]</p> <p>def bytes_to_gigabytes(bytes): return bytes / 1024 / 1024 / 1024</p> <p>bytes_to_gigabytes(torch.cuda.max_memory_allocated())
Salida:
29.0260648727417El uso mÃĄximo de memoria de la GPU es de aproximadamente 29 GB, lo que coincide con nuestra estimaciÃģn de 31 GB para cargar los parÃĄmetros del modelo en formato bfloat16.
OptimizaciÃģn de la memoria de inferencia con cuantizaciÃģn
Si bien bfloat16 es la precisiÃģn comÚn utilizada para entrenar LLM, los investigadores han encontrado que cuantizar los pesos del modelo a tipos de datos de precisiÃģn mÃĄs baja como enteros de 8 bits (int8) o enteros de 4 bits puede reducir significativamente el uso de memoria con una pÃĐrdida mÃnima de precisiÃģn para tareas de inferencia como la generaciÃģn de texto.
Veamos los ahorros de memoria de la cuantizaciÃģn de 8 bits y 4 bits del modelo OctoCode:
&lt;/div&gt; # CuantizaciÃģn de 8 bits model = AutoModelForCausalLM.from_pretrained("bigcode/octocoder", load_in_8bit=True, pad_token_id=0) pipe = pipeline("text-generation", model=model, tokenizer=tokenizer) result = pipe(prompt, max_new_tokens=60)[0]["generated_text"][len(prompt):] bytes_to_gigabytes(torch.cuda.max_memory_allocated())</pre>
Salida:
15.219234466552734# CuantizaciÃģn de 4 bits model = AutoModelForCausalLM.from_pretrained("bigcode/octocoder", load_in_4bit=True, low_cpu_mem_usage=True, pad_token_id=0) pipe = pipeline("text-generation", model=model, tokenizer=tokenizer) result = pipe(prompt, max_new_tokens=60)[0]["generated_text"][len(prompt):] bytes_to_gigabytes(torch.cuda.max_memory_allocated())
Salida:
9.543574333190918Con la cuantizaciÃģn de 8 bits, el requisito de memoria disminuye de 31 GB a 15 GB, mientras que la cuantizaciÃģn de 4 bits lo reduce aÚn mÃĄs a solo 9,5 GB. Esto permite ejecutar el modelo OctoCode de 15 mil millones de parÃĄmetros en GPUs de consumo como la RTX 3090 (24 GB de VRAM).
Sin embargo, tenga en cuenta que la cuantizaciÃģn mÃĄs agresiva como la de 4 bits puede llevar a una degradaciÃģn de la precisiÃģn en comparaciÃģn con la precisiÃģn de 8 bits o bfloat16. Hay un equilibrio entre ahorro de memoria y precisiÃģn que los usuarios deben evaluar para su caso de uso.
La cuantizaciÃģn es una tÃĐcnica poderosa que puede permitir el despliegue de LLM en entornos con recursos limitados como instancias en la nube, dispositivos de borde o incluso telÃĐfonos mÃģviles al reducir drÃĄsticamente la huella de memoria.
EstimaciÃģn de la memoria para el ajuste fino
Mientras que la cuantizaciÃģn se utiliza principalmente para la inferencia eficiente, tÃĐcnicas como el paralelismo de tensor y el paralelismo de modelo son cruciales para gestionar los requisitos de memoria durante el entrenamiento o ajuste fino de modelos de lenguaje grande.
El consumo mÃĄximo de memoria durante el ajuste fino es generalmente 3-4 veces mayor que el de la inferencia debido a los requisitos de memoria adicionales para:
- Gradientes
- Estados del optimizador
- Activaciones de la pasada hacia adelante almacenadas para la retropropagaciÃģn
Una estimaciÃģn conservadora es que el ajuste fino de un LLM con X mil millones de parÃĄmetros requiere alrededor de 4 * (2X) = 8X GB de VRAM en precisiÃģn bfloat16.
Por ejemplo, el ajuste fino del modelo LLaMA de 7 mil millones de parÃĄmetros requerirÃa aproximadamente 7 * 8 = 56 GB de VRAM por GPU en precisiÃģn bfloat16. Esto supera la capacidad de memoria de las GPUs actuales, lo que hace necesarias tÃĐcnicas de ajuste fino distribuido.
TÃĐcnicas de ajuste fino distribuido
Se han propuesto varios mÃĐtodos de ajuste fino distribuido para superar las limitaciones de memoria de la GPU para modelos grandes:
- Paralelismo de datos: El enfoque clÃĄsico de paralelismo de datos replica el modelo completo en varias GPUs mientras divide y distribuye los lotes de datos de entrenamiento. Esto reduce el tiempo de entrenamiento linealmente con la cantidad de GPUs, pero no reduce el requisito de memoria mÃĄximo en cada GPU.
- ZeRO Stage 3: Una forma avanzada de paralelismo de datos que divide los parÃĄmetros del modelo, los gradientes y los estados del optimizador en las GPUs. Reduce la memoria en comparaciÃģn con el paralelismo de datos clÃĄsico al mantener solo los datos particionados necesarios en cada GPU durante las diferentes fases del entrenamiento.
- Paralelismo de tensor: En lugar de replicar el modelo, el paralelismo de tensor divide los parÃĄmetros del modelo en filas o columnas y los distribuye en las GPUs. Cada GPU opera en un conjunto particionado de parÃĄmetros, gradientes y estados del optimizador, lo que lleva a un ahorro de memoria sustancial.
- Paralelismo de tuberÃa: Esta tÃĐcnica divide las capas del modelo en diferentes GPUs/trabajadores, con cada dispositivo ejecutando un subconjunto de las capas. Las activaciones se pasan entre los trabajadores, lo que reduce la memoria mÃĄxima pero aumenta la sobrecarga de comunicaciÃģn.
Estimar el uso de memoria para estos mÃĐtodos distribuidos es no trivial, ya que la distribuciÃģn de parÃĄmetros, gradientes, activaciones y estados del optimizador varÃa entre tÃĐcnicas. AdemÃĄs, diferentes componentes como el cuerpo del transformador y la cabeza de modelado de lenguaje pueden exhibir diferentes comportamientos de asignaciÃģn de memoria.
La soluciÃģn LLMem
Los investigadores han propuesto recientemente LLMem, una soluciÃģn que estima con precisiÃģn el consumo de memoria de la GPU cuando se aplican mÃĐtodos de ajuste fino distribuido a LLM en varias GPUs.
LLMem considera factores como la recombinaciÃģn de parÃĄmetros antes del cÃĄlculo (ZeRO Stage 3), la recopilaciÃģn de salidas en la pasada hacia atrÃĄs (paralelismo de tensor) y las diferentes estrategias de asignaciÃģn de memoria para el cuerpo del transformador y la cabeza de modelado de lenguaje.
Los resultados experimentales muestran que LLMem puede estimar el uso mÃĄximo de memoria de la GPU para el ajuste fino de LLM en una sola GPU con tasas de error de hasta 1,6%, superando la tasa de error promedio de 42,6% de DNNMem. Cuando se aplican mÃĐtodos de ajuste fino distribuido a LLM con mÃĄs de mil millones de parÃĄmetros en varias GPUs, LLMem logra una tasa de error promedio de 3,0%.













