PEFT documentation

Quantização

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.21.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Quantização

A quantização representa dados com menos bits, tornando-se uma técnica útil para reduzir o uso de memória e acelerar a inferência, especialmente no que diz respeito a grandes modelos de linguagem (LLMs). Existem várias maneiras de quantizar um modelo, incluindo:

  • otimizando quais pesos do modelo são quantizados com o algoritmo AWQ
  • quantizando independentemente cada linha de uma matriz de pesos com o algoritmo GPTQ
  • quantizando para precisão de 8 bits e 4 bits com a biblioteca bitsandbytes
  • quantizando para uma precisão tão baixa quanto 2 bits com o algoritmo AQLM

No entanto, após um modelo ser quantizado, ele normalmente não é treinado novamente para tarefas de baixo nível, pois o treinamento pode ser instável devido à menor precisão dos pesos e das ativações. Mas, como os métodos PEFT apenas adicionam parâmetros treináveis extras, isso permite treinar um modelo quantizado com um adaptador PEFT por cima! Combinar quantização com PEFT pode ser uma boa estratégia para treinar até os maiores modelos em uma única GPU. Por exemplo, QLoRA é um método que quantiza um modelo para 4 bits e, em seguida, o treina com LoRA. Esse método permite ajustar um modelo com 65 bilhões de parâmetros em uma única GPU de 48 GB!

Neste guia, você verá como quantizar um modelo para 4 bits e treiná-lo com LoRA.

Quantizar um modelo

bitsandbytes é uma biblioteca de quantização com integração com o Transformers. Com essa integração, você pode quantizar um modelo para 8 ou 4 bits e habilitar muitas outras opções configurando a classe BitsAndBytesConfig. Por exemplo, você pode:

  • Defina load_in_4bit=True para quantizar o modelo para 4 bits ao carregá-lo
  • Defina bnb_4bit_quant_type="nf4" para usar um tipo de dados de 4 bits especial para pesos inicializados a partir de uma distribuição normal
  • Defina bnb_4bit_use_double_quant=True para usar um esquema de quantização aninhado para quantizar os pesos já quantizados
  • Defina bnb_4bit_compute_dtype=torch.bfloat16 para usar bfloat16 em cálculos mais rápidos
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,
)

Passe o config para o método from_pretrained.

from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1", quantization_config=config)

Em seguida, você deve chamar a função prepare_model_for_kbit_training() para pré-processar o modelo quantizado para o treinamento.

from peft import prepare_model_for_kbit_training

model = prepare_model_for_kbit_training(model)

Agora que o modelo quantizado está pronto, vamos configurar uma configuração.

LoraConfig

Crie um LoraConfig com os seguintes parâmetros (ou escolha os seus):

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"
)

Em seguida, use a função get_peft_model() para criar um PeftModel a partir do modelo quantizado e da configuração.

from peft import get_peft_model

model = get_peft_model(model, config)

Você está pronto para treinar com o método de treinamento que preferir!

Inicialização do LoftQ

LoftQ inicializa os pesos do LoRA de modo a minimizar o erro de quantização e pode melhorar o desempenho ao treinar modelos quantizados. Para começar, siga estas instruções.

LoftQ é aplicado apenas a camadas lineares, incluindo aquelas especificadas via target_parameters. Camadas de incorporação e convolucionais não são suportadas e utilizam a inicialização padrão do LoRA no lugar. Camadas nn.MultiheadAttention não são suportadas e geram um erro.

Em geral, para que o LoftQ funcione da melhor forma, recomenda-se direcionar o LoRA para o maior número possível de camadas, pois as camadas não direcionadas não podem ter o LoftQ aplicado. Isso significa que passar LoraConfig(..., target_modules="all-linear") provavelmente dará os melhores resultados. Além disso, você deve usar nf4 como tipo de quantização na sua configuração de quantização ao usar quantização de 4 bits, ou seja, BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4").

Em geral, para que o LoftQ funcione da melhor forma, recomenda-se direcionar o LoRA para o maior número possível de camadas, pois as camadas não direcionadas não podem ter o LoftQ aplicado. Isso significa que passar LoraConfig(..., target_modules="all-linear") provavelmente dará os melhores resultados. Além disso, você deve usar nf4 como tipo de quantização na sua configuração de quantização ao usar quantização de 4 bits, ou seja, BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4").

Atualmente, existem duas formas previstas de aplicar o LoftQ:

  1. carregue o modelo base não quantizado, aplique o LoftQ com o nível de quantização pretendido (por exemplo, bits=4 para nf4) e salve o adaptador resultante. Em seguida, quantize o modelo base usando, por exemplo, BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4") e carregue o adaptador inicializado com LoftQ por cima
  2. carregar o modelo base quantizado, inicializar um modelo PEFT com LoRA (sem loftq) e usar replace_lora_weights_loftq para aplicar a inicialização LoftQ, transmitindo os pesos de referência do modelo base não quantizado a partir do arquivo de pesos (explicado com mais detalhes na próxima seção)

Usando replace_lora_weights_loftq para aplicação de LoftQ em tempo real

Uma maneira mais simples, porém mais limitada, de aplicar a inicialização LoftQ é usar a função de conveniência replace_lora_weights_loftq. Essa função recebe o modelo PEFT quantizado como entrada e substitui os pesos LoRA no local pelos seus equivalentes inicializados com 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 também permite passar um argumento callback para dar mais controle sobre quais camadas devem ou não ser modificadas, o que empiricamente pode melhorar bastante os resultados. Para ver um exemplo mais elaborado disso, confira este notebook.

replace_lora_weights_loftq implementa apenas uma etapa de iteração do LoftQ. Isso significa que apenas os pesos do LoRA são atualizados, em vez de atualizar iterativamente os pesos do LoRA e os pesos quantizados do modelo base. Isso pode levar a um desempenho inferior, mas tem a vantagem de que podemos usar os pesos quantizados originais derivados do modelo base, em vez de ter que manter uma cópia extra de pesos quantizados modificados. Se esse compromisso é vantajoso depende do caso de uso.

No momento, replace_lora_weights_loftq possui as seguintes limitações adicionais:

  • Os arquivos de modelo devem ser armazenados como um arquivo safetensors.
  • Apenas a quantização 4bit do bitsandbytes é suportada.

Treinamento no estilo QLoRA

QLoRA adiciona pesos treináveis a todas as camadas lineares na arquitetura do transformer. Como os nomes dos atributos para essas camadas lineares podem variar entre as arquiteturas, defina target_modules como "all-linear" para adicionar LoRA a todas as camadas lineares:

config = LoraConfig(target_modules="all-linear", ...)

Quantização GPTQ

Você pode saber mais sobre a quantização de [2, 3, 4, 8] bits baseada em GPTQ em GPT-QModel e na documentação de GPTQ do Transformers. O PEFT suporta o pós-treino com GPTQ por meio do 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")

Uma vez quantizado, você pode fazer pós-treino de modelos GPTQ com as APIs do PEFT.

Quantização AQLM

A Quantização Aditiva de Modelos de Linguagem (AQLM) é um método de compressão de Large Language Models. Ele quantiza vários pesos juntos e aproveita as interdependências entre eles. O AQLM representa grupos de 8 a 16 pesos como uma soma de vários códigos vetoriais. Isso permite comprimir modelos até 2 bits com perdas de precisão consideravelmente baixas.

Como o processo de quantização AQLM é computacionalmente caro, recomenda-se o uso de modelos pré-quantizados. Uma lista parcial dos modelos disponíveis pode ser encontrada no repositório oficial do aqlm repository.

Os modelos suportam ajuste de adaptadores LoRA. Para ajustar o modelo quantizado, será necessário instalar a biblioteca de inferência aqlm: pip install aqlm>=1.0.2. Os adaptadores LoRA ajustados devem ser salvos separadamente, pois não é possível mesclá-los com os pesos quantizados 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)

Você pode consultar o exemplo do Google Colab para uma visão geral do ajuste fino com AQLM+LoRA.

Quantização EETQ

Você também pode realizar o fine-tuning com LoRA em modelos quantizados com EETQ. O pacote EETQ oferece uma maneira simples e eficiente de realizar a quantização de 8 bits, que é afirmada ser mais rápida que o algoritmo LLM.int8(). Primeiro, verifique se você tem uma versão do transformers compatível com o EETQ (por exemplo, instalando-o do pypi mais recente ou do código-fonte).

import torch
from transformers import EetqConfig

config = EetqConfig("int8")

Passe o config para o método from_pretrained.

from transformers import AutoModelForCausalLM

model = AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.1", quantization_config=config)

e crie um LoraConfig e passe-o para 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)

Quantização HQQ

Os modelos quantizados usando Half-Quadratic Quantization of Large Machine Learning Models (HQQ) suportam o ajuste de adaptadores LoRA. Para ajustar o modelo quantizado, você precisará instalar a biblioteca hqq com: 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)

Ou usando uma versão do transformers compatível com HQQ (por exemplo, instalando-a do pypi mais recente ou do código-fonte).

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 (Otimização de Arquitetura do PyTorch)

O PEFT suporta modelos quantizados com torchao (“ao”) para quantização 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)

Observações:

  • Use as versões mais recentes de torchao (>= v0.4.0) e transformers (> 4.42).
  • Atualmente, apenas camadas lineares são suportadas.
  • quant_type = "int4_weight_only" não é suportado no momento.
  • NF4 ainda não foi implementado no transformers e, portanto, também não é suportado.
  • No momento, o DoRA só funciona com quant_type = "int8_weight_only".
  • Há suporte explícito para torchao quando usado com LoRA. No entanto, quando o torchao quantiza uma camada, sua classe não muda, apenas o tipo do tensor subjacente. Por esse motivo, os métodos PEFT diferentes de LoRA geralmente também funcionarão com torchao, mesmo que não sejam explicitamente suportados. Esteja ciente, porém, de que a fusão funciona corretamente apenas com LoRA e com quant_type = "int8_weight_only". Se você usar um método PEFT diferente ou um dtype diferente, a fusão provavelmente resultará em um erro e, mesmo que não resulte, os resultados ainda assim serão incorretos.

Quantização INC

O Intel Neural Compressor (INC) permite a quantização de modelos para diversos dispositivos, incluindo os aceleradores Intel Gaudi (também conhecidos como dispositivos HPU). É possível realizar o ajuste fino com LoRA em modelos que foram quantizados usando o INC. Para usar o INC com modelos PyTorch, instale a biblioteca com: pip install neural-compressor[pt]. A quantização de um modelo para a precisão FP8 em dispositivos HPU pode ser feita com o seguinte fluxo de quantização em uma única etapa:

import torch
from neural_compressor.torch.quantization import FP8Config, convert, finalize_calibration, prepare
quant_configs = {
    ...
}
config = FP8Config(**quant_configs)

Passe a configuração para o método prepare, execute a inferência para coletar estatísticas de calibração e chame os métodos finalize_calibration e convert para quantizar o modelo para a precisão 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
...

Um exemplo demonstrando como carregar um adaptador PEFT LoRA em um modelo de texto para imagem FLUX quantizado com INC para dispositivos HPU está disponível aqui.

Observações:

  • Os métodos merge() e unmerge() não são suportados atualmente para modelos quantizados com INC.
  • Atualmente, apenas camadas quantizadas INC de Linear são suportadas ao carregar adaptadores PEFT.

Outros Métodos PEFT Suportados

Além do LoRA, os seguintes métodos PEFT também suportam quantização:

  • VeRA (suporta quantização bitsandbytes)
  • AdaLoRA (suporta quantização bitsandbytes e GPTQ)
  • (IA)³ (suporta quantização bitsandbytes)

LoRA do Transformer Engine (TE)

O PEFT suporta adaptadores LoRA em camadas do NVIDIA Transformer Engine (te.pytorch.Linear, te.pytorch.LayerNormLinear e te.pytorch.LayerNormMLP). As camadas do TE usam FP8 e kernels fundidos para acelerar o treinamento de transformers, e a adição de adaptadores LoRA permite o ajuste fino eficiente em parâmetros de modelos acelerados pelo 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)

Quando o Transformer Engine está instalado, o PEFT despacha automaticamente as camadas correspondentes para o adaptador TeLinear — nenhuma configuração extra é necessária.

Observações

  • merge() e unmerge() ainda não são suportados para camadas de TE.
  • DoRA não é suportado com camadas TE.

Um exemplo completo de classificação de tokens do ESM2 está disponível em examples/lora_finetuning_transformer_engine/.

Mesclando adaptadores em pesos quantizados

Para qualquer método de quantização que o suporte, merge_adapter() desquantiza o peso base, adiciona os pesos do adaptador e quantiza o resultado novamente. unmerge_adapter() faz o mesmo em ordem inversa, portanto não restaura os pesos quantizados originais, e ciclos repetidos de mesclagem e desmesclagem podem acumular erro de arredondamento. Para recuperar os pesos originais, recarregue o modelo base. Para alternar entre o modelo base e o adaptador sem mesclar, use o gerenciador de contexto disable_adapter().

Próximas etapas

Se você tiver interesse em aprender mais sobre quantização, o seguinte pode ser útil:

Update on GitHub