PEFT documentation
Cuantización
Cuantización
La cuantización representa los datos con menos bits, lo que la convierte en una técnica útil para reducir el uso de memoria y acelerar la inferencia, especialmente cuando se trata de modelos de lenguaje grandes (LLMs). Existen varias formas de cuantizar un modelo, entre ellas:
- optimizar qué pesos del modelo se cuantizan con el algoritmo AWQ
- cuantizar de forma independiente cada fila de una matriz de pesos con el algoritmo GPTQ
- cuantización a precisión de 8 bits y 4 bits con la biblioteca bitsandbytes
- cuantizando hasta una precisión tan baja como de 2 bits con el algoritmo AQLM
Sin embargo, una vez que un modelo está cuantizado, normalmente no se entrena más para tareas posteriores, ya que el entrenamiento puede ser inestable debido a la menor precisión de los pesos y las activaciones. Pero, dado que los métodos PEFT solo añaden parámetros entrenables adicionales, esto permite entrenar un modelo cuantizado con un adaptador PEFT encima. Combinar la cuantización con PEFT puede ser una buena estrategia para entrenar incluso los modelos más grandes en una sola GPU. Por ejemplo, QLoRA es un método que cuantiza un modelo a 4 bits y luego lo entrena con LoRA. Este método permite ajustar un modelo de 65B parámetros en una sola GPU de 48GB.
En esta guía, verás cómo cuantizar un modelo a 4 bits y entrenarlo con LoRA.
Cuantizar un modelo
bitsandbytes es una biblioteca de cuantización con una integración con Transformers. Con esta integración, puedes cuantizar un modelo a 8 o 4 bits y habilitar muchas otras opciones configurando la clase BitsAndBytesConfig. Por ejemplo, puedes:
- Establezca
load_in_4bit=Truepara cuantizar el modelo a 4 bits al cargarlo - Establezca
bnb_4bit_quant_type="nf4"para usar un tipo de datos de 4 bits especial para los pesos inicializados desde una distribución normal - Establezca
bnb_4bit_use_double_quant=Truepara usar un esquema de cuantización anidado que cuantice los pesos ya cuantizados - establezca
bnb_4bit_compute_dtype=torch.bfloat16para usar bfloat16 y obtener un cálculo más rápido
import torch
from transformers import BitsAndBytesConfig
config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=torch.bfloat16,
)Pase el config al método from_pretrained.
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1", quantization_config=config)A continuación, debe llamar a la función prepare_model_for_kbit_training() para preprocesar el modelo cuantizado para el entrenamiento.
from peft import prepare_model_for_kbit_training
model = prepare_model_for_kbit_training(model)Ahora que el modelo cuantizado está listo, vamos a configurar una configuración.
LoraConfig
Crea un LoraConfig con los siguientes parámetros (o elige los tuyos):
from peft import LoraConfig
config = LoraConfig(
r=16,
lora_alpha=8,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)Luego, use la función get_peft_model() para crear un PeftModel a partir del modelo cuantizado y la configuración.
from peft import get_peft_model
model = get_peft_model(model, config)¡Todo listo para el entrenamiento con el método que prefieras!
Inicialización de LoftQ
LoftQ inicializa los pesos de LoRA de manera que se minimice el error de cuantización, y puede mejorar el rendimiento al entrenar modelos cuantizados. Para comenzar, sigue estas instrucciones.
LoftQ solo se aplica a capas lineales, incluidas aquellas dirigidas mediante target_parameters. Las capas de incrustación y convolucionales no son compatibles y utilizan la inicialización predeterminada de LoRA en su lugar. Las capas nn.MultiheadAttention no son compatibles y generan un error.
En general, para que LoftQ funcione de la mejor manera, se recomienda aplicar LoRA a la mayor cantidad de capas posible, ya que a las capas que no se seleccionen no se les puede aplicar LoftQ. Esto significa que pasar LoraConfig(..., target_modules="all-linear") probablemente dará los mejores resultados. Además, debe usar nf4 como tipo de cuantización en su configuración de cuantización al usar cuantización de 4 bits, es decir, BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4").
En general, para que LoftQ funcione de la mejor manera, se recomienda aplicar LoRA a la mayor cantidad de capas posible, ya que a las capas que no se seleccionen no se les puede aplicar LoftQ. Esto significa que pasar LoraConfig(..., target_modules="all-linear") probablemente dará los mejores resultados. Además, debe usar nf4 como tipo de cuantización en su configuración de cuantización al usar cuantización de 4 bits, es decir, BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4").
Actualmente, existen dos formas previstas de aplicar LoftQ:
- cargue el modelo base no cuantizado, aplique LoftQ con el nivel de cuantización deseado (por ejemplo,
bits=4para nf4) y guarde el adaptador resultante. Luego, cuantice el modelo base utilizando, por ejemplo,BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4")y cargue el adaptador inicializado con LoftQ encima - cargar el modelo base cuantizado, inicializar un modelo PEFT con LoRA (sin loftq) y usar
replace_lora_weights_loftqpara aplicar la inicialización de LoftQ mediante el streaming de los pesos de referencia del modelo base no cuantizado desde el archivo de pesos (se explica con más detalle en la siguiente sección)
Usar replace_lora_weights_loftq para la aplicación de LoftQ sobre la marcha
Una forma más sencilla, aunque más limitada, de aplicar la inicialización de LoftQ es utilizar la función de conveniencia replace_lora_weights_loftq. Esta toma el modelo PEFT cuantizado como entrada y reemplaza los pesos de LoRA in situ por sus equivalentes inicializados con LoftQ.
from peft import replace_lora_weights_loftq
from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(load_in_4bit=True, ...)
base_model = AutoModelForCausalLM.from_pretrained(..., quantization_config=bnb_config)
# note: don't pass init_lora_weights="loftq" or loftq_config!
lora_config = LoraConfig(task_type="CAUSAL_LM")
peft_model = get_peft_model(base_model, lora_config)
replace_lora_weights_loftq(peft_model)replace_lora_weights_loftq también permite pasar un argumento callback para tener más control sobre qué capas deben modificarse o no, lo cual empíricamente puede mejorar bastante los resultados. Para ver un ejemplo más elaborado de esto, consulta este notebook.
replace_lora_weights_loftq implementa solo un paso de iteración de LoftQ. Esto significa que solo se actualizan los pesos de LoRA, en lugar de actualizar de forma iterativa los pesos de LoRA y los pesos cuantizados del modelo base. Esto puede llevar a un rendimiento inferior, pero tiene la ventaja de que podemos usar los pesos cuantizados originales derivados del modelo base, en lugar de tener que mantener una copia adicional de pesos cuantizados modificados. Si este compromiso es rentable depende del caso de uso.
En este momento, replace_lora_weights_loftq tiene estas limitaciones adicionales:
- Los archivos de modelo deben almacenarse como un archivo
safetensors. - Solo se admite la cuantización de 4 bits de bitsandbytes.
Entrenamiento de estilo QLoRA
QLoRA añade pesos entrenables a todas las capas lineales de la arquitectura transformer. Dado que los nombres de los atributos de estas capas lineales pueden variar entre arquitecturas, establece target_modules en "all-linear" para añadir LoRA a todas las capas lineales:
config = LoraConfig(target_modules="all-linear", ...)Cuantización GPTQ
Puedes aprender más sobre la cuantización de [2, 3, 4, 8] bits basada en GPTQ en GPT-QModel y en la documentación de GPTQ de Transformers. PEFT admite el entrenamiento posterior con GPTQ a través de GPT-QModel.
# GPT-QModel install
pip install "gptqmodel>=7.0.0"from transformers import AutoModelForCausalLM, AutoTokenizer, GPTQConfig
model_id = "facebook/opt-125m"
tokenizer = AutoTokenizer.from_pretrained(model_id)
gptq_config = GPTQConfig(bits=4, group_size=128, dataset="wikitext2", tokenizer=tokenizer)
quantized_model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto", quantization_config=gptq_config)
# save quantized model
quantized_model.save_pretrained("./opt-125m-gptq")
tokenizer.save_pretrained("./opt-125m-gptq")Una vez cuantizados, puedes realizar el entrenamiento posterior de modelos GPTQ con las API de PEFT.
Cuantización AQLM
La Cuantización Aditiva de Modelos de Lenguaje (AQLM) es un método de compresión para Modelos de Lenguaje de Gran Escala. Cuantiza varios pesos de forma conjunta y aprovecha las interdependencias entre ellos. AQLM representa grupos de 8 a 16 pesos como la suma de múltiples códigos vectoriales. Esto permite comprimir los modelos hasta 2 bits con pérdidas de precisión considerablemente bajas.
Dado que el proceso de cuantización AQLM es computacionalmente costoso, se recomienda el uso de modelos precuantizados. Una lista parcial de los modelos disponibles se puede encontrar en el repositorio oficial de aqlm.
Los modelos admiten el ajuste de adaptadores LoRA. Para ajustar el modelo cuantizado, es necesario instalar la biblioteca de inferencia aqlm: pip install aqlm>=1.0.2. Los adaptadores LoRA ajustados deben guardarse por separado, ya que no es posible fusionarlos con los pesos cuantizados con AQLM.
quantized_model = AutoModelForCausalLM.from_pretrained(
"BlackSamorez/Mixtral-8x7b-AQLM-2Bit-1x16-hf-test-dispatch",
dtype="auto", device_map="auto", low_cpu_mem_usage=True,
)
peft_config = LoraConfig(...)
quantized_model = get_peft_model(quantized_model, peft_config)Puede consultar el ejemplo de Google Colab para obtener una visión general del ajuste fino de AQLM+LoRA.
Cuanticización EETQ
También puedes realizar el ajuste fino con LoRA en modelos cuantizados con EETQ. El paquete EETQ ofrece una forma simple y eficiente de realizar la cuantización de 8 bits, que se afirma que es más rápida que el algoritmo LLM.int8(). Primero, asegúrate de tener una versión de transformers compatible con EETQ (por ejemplo, instalándola desde la última versión de pypi o desde el código fuente).
import torch
from transformers import EetqConfig
config = EetqConfig("int8")Pase el config al método from_pretrained.
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1", quantization_config=config)y crea una LoraConfig y pásala a get_peft_model:
from peft import LoraConfig, get_peft_model
config = LoraConfig(
r=16,
lora_alpha=8,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, config)Cuantización HQQ
Los modelos cuantizados mediante Half-Quadratic Quantization of Large Machine Learning Models (HQQ) admiten el ajuste de adaptadores LoRA. Para ajustar el modelo cuantizado, es necesario instalar la biblioteca hqq con: pip install hqq.
from hqq.engine.hf import HQQModelForCausalLM
device = torch.accelerator.current_accelerator().type if hasattr(torch, "accelerator") else "cuda"
quantized_model = HQQModelForCausalLM.from_quantized(save_dir_or_hfhub, device=device)
peft_config = LoraConfig(...)
quantized_model = get_peft_model(quantized_model, peft_config)O usando una versión de transformers compatible con HQQ (por ejemplo, instalándola desde la última versión de pypi o desde el código fuente).
from transformers import HqqConfig, AutoModelForCausalLM
quant_config = HqqConfig(nbits=4, group_size=64)
quantized_model = AutoModelForCausalLM.from_pretrained(save_dir_or_hfhub, device_map=device_map, quantization_config=quant_config)
peft_config = LoraConfig(...)
quantized_model = get_peft_model(quantized_model, peft_config)torchao (Optimización de la arquitectura de PyTorch)
PEFT admite modelos cuantizados con torchao (“ao”) para la cuantización int8.
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM, TorchAoConfig
from torchao.quantization import Int8WeightOnlyConfig
model_id = ...
quantization_config = TorchAoConfig(quant_type=Int8WeightOnlyConfig())
base_model = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=quantization_config)
peft_config = LoraConfig(...)
model = get_peft_model(base_model, peft_config)Precauciones:
- Utilice las versiones más recientes de torchao (>= v0.4.0) y transformers (> 4.42).
- Actualmente solo se admiten capas lineales.
quant_type = "int4_weight_only"no se admite actualmente.NF4aún no está implementado en transformers y, por lo tanto, tampoco es compatible.- Por el momento, DoRA solo funciona con
quant_type = "int8_weight_only". - Existe soporte explícito para torchao cuando se usa con LoRA. Sin embargo, cuando torchao cuantiza una capa, su clase no cambia, solo cambia el tipo del tensor subyacente. Por esta razón, los métodos de PEFT distintos de LoRA generalmente también funcionarán con torchao, aunque no estén explícitamente soportados. Ten en cuenta, no obstante, que la fusión solo funciona correctamente con LoRA y con
quant_type = "int8_weight_only". Si usas un método de PEFT o un dtype diferente, la fusión probablemente resultará en un error, y aunque no lo haga, los resultados seguirán siendo incorrectos.
Cuantización INC
Intel Neural Compressor (INC) permite la cuantización de modelos para diversos dispositivos, incluidos los aceleradores Intel Gaudi (también conocidos como dispositivos HPU). Puede realizar el ajuste fino con LoRA en modelos que hayan sido cuantizados con INC. Para usar INC con modelos de PyTorch, instale la biblioteca con: pip install neural-compressor[pt].
Cuantizar un modelo a precisión FP8 para dispositivos HPU se puede hacer con el siguiente flujo de cuantización de un solo paso:
import torch
from neural_compressor.torch.quantization import FP8Config, convert, finalize_calibration, prepare
quant_configs = {
...
}
config = FP8Config(**quant_configs)Pase la configuración al método prepare, ejecute la inferencia para recopilar las estadísticas de calibración y llame a los métodos finalize_calibration y convert para cuantizar el modelo a precisión FP8:
model = prepare(model, config)
# Run inference to collect calibration statistics
...
# Finalize calibration and convert the model to FP8 precision
finalize_calibration(model)
model = convert(model)
# Load PEFT LoRA adapter as usual
...Se proporciona un ejemplo que demuestra cómo cargar un adaptador PEFT LoRA en un modelo de texto a imagen FLUX cuantizado con INC para dispositivos HPU aquí.
Precauciones:
- Los métodos
merge()yunmerge()no están actualmente soportados para modelos cuantizados con INC. - Actualmente, solo se admiten capas cuantizadas INC de Linear al cargar adaptadores de PEFT.
Otros métodos PEFT admitidos
Además de LoRA, los siguientes métodos de PEFT también admiten cuantización:
- VeRA (admite cuantización de bitsandbytes)
- AdaLoRA (admite la cuantización con bitsandbytes y GPTQ)
- (IA)³ (admite cuantización de bitsandbytes)
LoRA de Transformer Engine (TE)
PEFT admite adaptadores LoRA sobre capas de NVIDIA Transformer Engine (te.pytorch.Linear, te.pytorch.LayerNormLinear y te.pytorch.LayerNormMLP). Las capas de TE utilizan FP8 y kernels fusionados para acelerar el entrenamiento de transformers, y adjuntar adaptadores LoRA permite realizar un ajuste fino eficiente en parámetros de modelos acelerados con TE.
from peft import LoraConfig, get_peft_model
config = LoraConfig(
r=8,
lora_alpha=16,
target_modules=["q_proj", "v_proj"],
task_type="TOKEN_CLS",
)
model = get_peft_model(te_model, config)Cuando Transformer Engine está instalado, PEFT asigna automáticamente las capas coincidentes al adaptador TeLinear; no se necesita configuración adicional.
Precauciones
merge()yunmerge()aún no son compatibles con las capas de TE.- DoRA no es compatible con las capas de TE.
Un ejemplo completo de clasificación de tokens con ESM2 está disponible en examples/lora_finetuning_transformer_engine/.
Fusión de adaptadores en pesos cuantizados
Para cualquier método de cuantización que lo soporte, merge_adapter() descuantiza el peso base, suma los pesos del adaptador y vuelve a cuantizar el resultado. unmerge_adapter() realiza la misma operación en sentido inverso, por lo que no restaura los pesos cuantizados originales, y los ciclos repetidos de fusión y desfusión pueden acumular errores de redondeo. Para recuperar los pesos originales, recargue el modelo base. Para alternar entre el modelo base y el adaptador sin fusionarlos, use el gestor de contexto disable_adapter().
Próximos pasos
Si le interesa aprender más sobre cuantización, lo siguiente puede ser útil:
- Obtenga más detalles sobre QLoRA y consulte algunos benchmarks sobre su impacto en la publicación de blog Making LLMs even more accessible with bitsandbytes, 4-bit quantization and QLoRA.
- Lea más sobre los diferentes esquemas de cuantización en la guía de Cuantización de Transformers.