El manual definitivo para la cuantificación de LLM | por Ashish Abraham | Jul, 2024

Implica convertir los pesos de FP16 a INT8, lo que reduce a la mitad el tamaño del LLM. El método pretende reducir de manera eficiente el tamaño de los LLM hasta 175 000 000 de parámetros sin degradar el rendimiento.

Antes de entrar en los detalles de la papel [1]Es importante entender que los LLM tienen Características emergentes — patrones que surgen de los datos de entrenamiento y que son cruciales para el rendimiento del modelo. Algunas de estas características pueden tener grandes magnitudes y pueden ejercer una fuerte influencia sobre el rendimiento general del modelo.

Fuente: Papel

Pasos involucrados:

  1. El LLM.int8() El método comienza con la cuantificación vectorial. Esto significa que cada vector (una fila de la matriz) se cuantifica por separado, utilizando su propia constante de normalización. De esta manera, se conserva la importancia relativa de cada característica.
  2. Para cada vector, se calcula una constante de normalización que se utiliza para escalar los vectores de modo que puedan representarse como números enteros de 8 bits. Al utilizar las constantes de normalización, se cuantifican la mayoría de las características del LLM.
  3. En el caso de valores atípicos emergentes (características con magnitudes inusualmente grandes), se utiliza un esquema de descomposición de precisión mixta. Esto aísla estas características atípicas en una multiplicación de matriz de 16 bits independiente, lo que garantiza que se gestionen con precisión y, al mismo tiempo, permite que más del 99,9 % de los valores se multipliquen en 8 bits.

Ventajas
Los LLM se pueden cuantificar y utilizar inmediatamente para inferencia sin degradación del rendimiento.

Contras
El método se centra únicamente en el tipo de datos INT8 y en modelos de hasta 175B parámetros (especialmente OPT-175B / BLOOM).

Implementación de código

from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

model_id = "meta-llama/Llama-2-7b-chat-hf"

tokenizer = AutoTokenizer.from_pretrained(model_id)
model_8bit = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto", load_in_8bit=True)

GPTQ (Octubre de 2022)

GPTQ Fue una de las primeras técnicas de PTQ de un solo uso que permitía la implementación eficiente de grandes modelos de lenguaje. Se logró principalmente a través de las dos características propuestas en el papel [4],

  1. Cuantificación por capas
    La cuantificación se realiza capa por capa en el LLM. El objetivo es encontrar una versión más simple de los pesos que nos dé un buen resultado cuando la usemos para hacer predicciones. Esto se hace de manera que la diferencia entre los pesos originales y los simplificados sea lo más pequeña posible, es decir, el error cuadrático medio más bajo.
  2. Cuantificación cerebral óptima
    Es un algoritmo que tiene como objetivo reducir los errores introducidos en el modelo debido a la cuantificación. Al cuantificar un peso, se ajustan los pesos restantes.
Fuente: Papel

Ventajas
GPTQ permite la cuantificación de hasta 2 bits, lo que proporciona una variedad de compensaciones entre el tamaño del modelo y el rendimiento.

Contras
La cuantificación mediante este método introduce una degradación considerable del rendimiento.

Implementación de código

Instalar las bibliotecas necesarias.

pip install auto-gptq transformers accelerate

Cargue el modelo y cuantícelo con el autogptq biblioteca.

from transformers import AutoModelForCausalLM, AutoTokenizer, GPTQConfig
model_id = "meta-llama/Llama-2-7b-hf"
tokenizer = AutoTokenizer.from_pretrained(model_id)
quant_config = GPTQConfig(bits=4, dataset = "c4", tokenizer=tokenizer)
model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto", quantization_config=quant_config)

QLoRA (Mayo de 2023)

Antes de sumergirnos en QLoRA, aquí hay una breve introducción a LoRA. LoRA (adaptación de bajo rango de modelos de lenguaje grandes) es un método de ajuste fino que utiliza parámetros de manera eficiente para especializar los LLM para tareas específicas. Esto se logra mediante la integración de matrices entrenables basadas en la descomposición de rangos en cada capa del transformador. Además, minimiza la cantidad de parámetros que se deben entrenar para la tarea en cuestión, al mismo tiempo que mantiene sin cambios los pesos originales del modelo preentrenado. Lea más sobre esto aquí.

Fuente: Papel

QLoRA es una versión mejorada de LoRA. A continuación se muestran los aspectos más destacados de este método, tal como se describe en la papel [2]:

1. Cuantificación de punto flotante normal de 4 bits:
El flotante normal de 4 bits funciona calculando el 2ᵏ+1 cuantiles (donde k es el número de bits) dentro de una distribución que va de 0 a 1, normalizando posteriormente estos valores para que se ajusten a la [-1, 1] intervalo. Con esta normalización, podemos ajustar de manera similar los pesos de nuestra red neuronal a la [-1, 1] rango y proceder con la cuantificación.

2. Doble descuantificación:
Esto implica cuantificar las constantes de cuantificación empleadas en el proceso de cuantificación NF de 4 bits. Puede conservar un promedio de 0,5 bits por parámetro. Esto es beneficioso porque QLoRA utiliza cuantificación de k bits por bloque.

3. Optimizaciones paginadas:
QLoRA implica transferencias de páginas eficientes de la GPU a la CPU mediante la función de memoria unificada de Nvidia. Esto evita sobrecargas en la GPU y hace que el entrenamiento sea eficiente sin interrupciones.

Ventajas
QLoRA, debido al menor uso de memoria de la GPU, puede admitir longitudes de secuencia máximas más altas y una mayor cantidad de lotes.

Contras
Puede ser más lento en términos de velocidad de ajuste. También se encuentra en el extremo inferior en cuanto a eficiencia de costos, pero eso no es un problema.

Implementación de código

Instalar las bibliotecas necesarias

pip install -q -U trl transformers accelerate git+https://github.com/huggingface/peft.git
pip install -q datasets bitsandbytes

Cargue el modelo y el tokenizador. Configure los parámetros de LoRA.

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig, AutoTokenizer

model_id = "meta-llama/Llama-2-7b-chat-hf"

bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.float16,
)

model = AutoModelForCausalLM.from_pretrained(
model_id,
quantization_config=bnb_config,
trust_remote_code=True
)
model.config.use_cache = False

from peft import LoraConfig, get_peft_model

lora_alpha = 16
lora_dropout = 0.1
lora_r = 64

peft_config = LoraConfig(
lora_alpha=lora_alpha,
lora_dropout=lora_dropout,
r=lora_r,
task_type="CAUSAL_LM"
)

Configurar el entrenador usando SFTTrainer de la biblioteca TRL que proporciona un envoltorio para los transformadores Trainer para ajustar fácilmente los modelos en conjuntos de datos basados ​​en instrucciones mediante adaptadores PEFT. Por supuesto, necesitará un conjunto de datos para entrenar.

from transformers import TrainingArguments

output_dir = "./models"
per_device_train_batch_size = 4
gradient_accumulation_steps = 4
optim = "paged_adamw_32bit"
save_steps = 100
logging_steps = 10
learning_rate = 2e-4
max_grad_norm = 0.3
max_steps = 100
warmup_ratio = 0.03
lr_scheduler_type = "constant"

training_arguments = TrainingArguments(
output_dir=output_dir,
per_device_train_batch_size=per_device_train_batch_size,
gradient_accumulation_steps=gradient_accumulation_steps,
optim=optim,
save_steps=save_steps,
logging_steps=logging_steps,
learning_rate=learning_rate,
fp16=True,
max_grad_norm=max_grad_norm,
max_steps=max_steps,
warmup_ratio=warmup_ratio,
group_by_length=True,
lr_scheduler_type=lr_scheduler_type,
)

from trl import SFTTrainer

max_seq_length = 512

trainer = SFTTrainer(
model=model,
train_dataset=dataset,
peft_config=peft_config,
dataset_text_field="text",
max_seq_length=max_seq_length,
tokenizer=tokenizer,
args=training_arguments,
)

trainer.train()

Cuartel General Adjunto (Junio ​​de 2023)

AWQ (cuantificación de peso según activación) es un método de cuantificación posterior al entrenamiento. En este método, se consideran las activaciones del modelo en lugar de los pesos. Permítanme citarlo directamente del papel [3],

Nuestro método se basa en la observación de que los pesos no son igualmente importantes: proteger solo el 1 % de los pesos salientes puede reducir en gran medida el error de cuantificación. Luego, proponemos buscar el escalamiento óptimo por canal que proteja los pesos salientes observando la activación, no los pesos.

Fuente: Papel

Ventajas
AWQ ofrece más precisión que otros métodos, ya que se conservan los pesos críticos para el rendimiento de LLM. También es eficiente y más rápido, ya que no implica retropropagación ni reconstrucción. Funciona bien en dispositivos periféricos.

Contras
Si bien mantener el 0,1 % de pesos en FP16 puede mejorar el rendimiento de la cuantificación sin aumentar significativamente el tamaño del modelo, este tipo de datos de precisión mixta complica la implementación del sistema.

Implementación de código

Instalar las bibliotecas necesarias.

!pip install autoawq transformers accelerate

Cargue el modelo y cuantícelo con el autoawq biblioteca.

from awq import AutoAWQForCausalLM
from transformers import AutoTokenizer
model_id = 'meta-llama/Llama-2-7b-hf'
quant_path = 'Llama2-7b-awq-4bit'
quant_config = { "zero_point": True, "q_group_size": 128, "w_bit": 4 }

# Load model and tokenizer
model = AutoAWQForCausalLM.from_pretrained(model_id)
tokenizer = AutoTokenizer.from_pretrained(model_id, use_fast=True)

# Quantize
model.quantize(tokenizer, quant_config=quant_config)

Sofismo# (julio de 2023)

En lenguaje sencillo, QuIP (cuantificación con procesamiento de incoherencia) Se basa en la idea de que el proceso de cuantificación se puede mejorar si los pesos del modelo se distribuyen uniformemente (de forma incoherente) y las direcciones importantes para redondearlos no están alineadas con los ejes de coordenadas. Consta de dos pasos:

  1. Procedimiento de redondeo adaptativo LDLQ:Ajustar los pesos del modelo de manera que se minimice una cierta medida de error (el ‘objetivo proxy cuadrático’) [8].
  2. Pre y posprocesamiento:Multiplica las matrices de pesos y hessianas por matrices ortogonales aleatorias. Esto garantiza que los pesos y las hessianas sean incoherentes, lo que resulta beneficioso para el proceso de cuantificación.
Fuente: Papel

Sofismo# [5] avances en QuIP utilizando algunas mejoras en el procesamiento.

  1. Procesamiento mejorado de incoherencias:Utiliza un método más rápido y mejor llamado transformada de Hadamard aleatoria.
  2. Cuantización vectorial:QuIP# utiliza la cuantificación vectorial para aprovechar la distribución subgaussiana con forma de bola que poseen los pesos incoherentes. En concreto, introduce un conjunto de libros de códigos eficientes en términos de hardware basados ​​en la red E8 altamente simétrica. La red E8 logra el empaquetamiento de bolas unitarias de ocho dimensiones óptimo, lo que significa que puede representar los pesos de manera más eficiente.

Ventajas
En comparación con otros métodos, QuIP# ofrece un rendimiento significativamente mayor (>40 %) con la misma o mejor calidad de cuantificación. Esto no está nada mal para una cuantificación de 2 bits.

Contras
Aunque no se mencionan muchas limitaciones, se puede considerar la complejidad y la compatibilidad del hardware.

Implementación de código

Clonar el repositorio oficial e instalar las bibliotecas necesarias.

git clone https://github.com/Cornell-RelaxML/quip-sharp.git
pip install -r requirements.txt
cd quiptools && python setup.py install && cd ../

Busque los scripts para varios modelos. Ejecute el script quantize_finetune_llama.py utilizar modelos de llama.

Además, echa un vistazo a la Repositorio Para cuantificación de quip. El código para cuantificar modelos es el siguiente.

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from quantizer import QuipQuantizer

model_name = "meta-llama/Llama-2-70b-hf"
quant_dir = "llama-70b_2bit_quip"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.float16)

quant = QuipQuantizer(codebook="E8P12", dataset="redpajama")
quant.quantize_model(model, tokenizer, quant_dir)

GGUF (agosto de 2023)

GGUF (Formato unificado generado por GPT) Fue un lanzamiento muy esperado por Georgi Gerganov y el llama.cpp El punto más destacado fue que ahora los LLM se podían ejecutar fácilmente en CPUs de consumo. Antes se llamaba GGML y luego se actualizó a GGUF.
Un logro notable de GGML fue la capacidad de descargar ciertas capas del LLM a la GPU si fuera necesario, incluso mientras el LLM funciona en la CPU. Esto aborda de manera eficaz el desafío global que enfrentan los desarrolladores debido a la VRAM inadecuada.

Ventajas
Si planea ejecutar LLM en CPU o dispositivos Apple (los chips de la serie M), es el método al que recurren muchos LLM como Llama y Mistral. El formato de archivo GGUF ahora es compatible con llama.cpp y HuggingFace. Los modelos GGUF también muestran puntuaciones de perplejidad más bajas en comparación con otros formatos.

Contras
GGUF está enfocado en CPU y dispositivos de la serie M de Apple. Esto podría ser una limitación si trabajas con diferentes configuraciones de hardware.

Implementación de código

Instala el ctransformers biblioteca.

pip install ctransformers[cuda]

Los modelos están disponibles en los repositorios de Bloke en HuggingFace.

from ctransformers import AutoModelForCausalLM
from transformers import AutoTokenizer, pipeline

# Load LLM and Tokenizer
# Use `gpu_layers` to specify how many layers will be offloaded to the GPU.
model = AutoModelForCausalLM.from_pretrained(
"TheBloke/zephyr-7B-beta-GGUF",
model_file="zephyr-7b-beta.Q4_K_M.gguf",
model_type="mistral", gpu_layers=50, hf=True
)
tokenizer = AutoTokenizer.from_pretrained(
"HuggingFaceH4/zephyr-7b-beta", use_fast=True
)

# Create a pipeline
pipe = pipeline(model=model, tokenizer=tokenizer, task='text-generation')

from ctransformers import AutoModelForCausalLM
from transformers import AutoTokenizer, pipeline

# Load LLM and Tokenizer
# Use `gpu_layers` to specify how many layers will be offloaded to the GPU.
model = AutoModelForCausalLM.from_pretrained(
"TheBloke/zephyr-7B-beta-GGUF",
model_file="zephyr-7b-beta.Q4_K_M.gguf",
model_type="mistral", gpu_layers=50, hf=True
)
tokenizer = AutoTokenizer.from_pretrained(
"HuggingFaceH4/zephyr-7b-beta", use_fast=True
)

# Create a pipeline
pipe = pipeline(model=model, tokenizer=tokenizer, task='text-generation')

Cuartel General (Noviembre de 2023)

Según el artículo, la calibración de pesos se puede lograr mediante técnicas de calibración sin datos (BitsAndBytes) y técnicas basadas en calibración (GPTQ y AWQ). Si bien los métodos sin calibración son más rápidos, los métodos basados ​​en calibración sufren sesgos en los datos y tiempo de cuantificación.

HQQ (cuantificación semicuadrática) Realiza la cuantificación en tiempo real mediante una optimización rápida y robusta. Elimina la necesidad de datos de calibración y es lo suficientemente versátil como para cuantificar cualquier modelo dado, logrando así la velocidad de los métodos sin calibración sin problemas de sesgo de datos. Reduce drásticamente el tiempo de cuantificación a casi unos minutos gracias a técnicas de optimización como la división semicuadrática. Para obtener más detalles sobre las matemáticas y el funcionamiento del método, consulte página web oficial.

Ventajas
Se logró un tiempo de cuantificación sorprendentemente bajo en comparación con otros métodos (¡50 veces más rápido en comparación con GPTQ!). La eliminación de los requisitos de datos de calibración lo hace más fácil.

Contras
No se mencionan muchas limitaciones en otros lugares. Aún así, puede mostrar una degradación de la calidad como otros métodos.

Implementación de código

¡Instala la biblioteca de transformadores y utiliza la implementación de HQQ inmediatamente!

from transformers import AutoModelForCausalLM, HqqConfig

# All linear layers will use the same quantization config
quant_config = HqqConfig(nbits=4, group_size=64, quant_zero=False, quant_scale=False, axis=1)

model_id = "meta-llama/Llama-2-7b-hf"

# Load and quantize
model = AutoModelForCausalLM.from_pretrained(
model_id,
torch_dtype=torch.float16,
device_map="cuda",
quantization_config=quant_config
)

AQLM (Febrero de 2024)

AQLM (Cuantización aditiva de modelos lingüísticos) es un método PTQ que solo tiene en cuenta el peso y que establece un nuevo punto de referencia en el rango de 2 bits por parámetro. Supera a algoritmos populares como GPTQ, así como a QuIP y QuIP#.

Se aplica un nuevo método llamado Cuantificación de libros de códigos múltiples (MCQ) que divide cada vector en subvectores y los aproxima utilizando un conjunto finito de palabras claveLas palabras clave son vectores ya aprendidos definidos en un libro de códigos [7]AQLM funciona tomando las filas de las matrices de peso en un modelo y cuantizándolas.

Fuente: Papel

Ventajas
AQLM ofrece implementaciones rápidas para la generación de tokens tanto en GPU como en CPU, lo que le permite superar la velocidad de las implementaciones optimizadas de FP16, todo ello mientras opera dentro de un espacio de memoria significativamente reducido.

Contras
En otras partes solo se mencionan algunas limitaciones. Aún así, puede mostrar una degradación de la calidad como otros métodos.

Implementación de código

Las instrucciones sobre cómo cuantificar modelos usted mismo y el código correspondiente se pueden encontrar en repositorio oficialPara ejecutar modelos AQLM, cargue un modelo que haya sido cuantificado con AQLM:

from transformers import AutoTokenizer, AutoModelForCausalLM

quantized_model = AutoModelForCausalLM.from_pretrained(
"ISTA-DASLab/Mixtral-8x7b-AQLM-2Bit-1x16-hf",
torch_dtype="auto",
device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained("ISTA-DASLab/Mixtral-8x7b-AQLM-2Bit-1x16-hf")

Los métodos de cuantificación han abierto un mundo de posibilidades, permitiendo capacidades avanzadas de procesamiento del lenguaje incluso en nuestros bolsillos. En este artículo, analizamos todo sobre la cuantificación LLM y exploramos en detalle varios métodos para cuantificar LLM. También analizamos los pros y los contras de cada enfoque y aprendimos a usarlos. Además, obtuvimos información sobre cómo seleccionar el enfoque más adecuado en función de los requisitos específicos y de si se utiliza una CPU o una GPU.