Transformers documentation

compressed-tensors

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

and get access to the augmented documentation experience

to get started

compressed-tensors

compressed-tensors estende os arquivos safetensors para tipos de dados de tensores comprimidos, fornecendo um formato de checkpoint unificado para armazenar e carregar diversos formatos de quantização, como denso, quantizado em int (int8), quantizado em float (fp8) e quantizado em pacote (int4 ou int8 com pesos quantizados empacotados em int32).

O compressed-tensors suporta fine-tuning com PEFT e inclui os seguintes recursos.

  • precisões de pesos e ativações fp8, int4 e int8.
  • Estratégias de escalas de quantização e pontos zero para tensor, canal, grupo, bloco, token.
  • Quantização dinâmica de ativação por token (ou qualquer estratégia estática).
  • Quantização de módulos arbitrários, não apenas de módulos nn.Linear.
  • Suporte direcionado a módulos específicos por nome ou classe.

Instale o compressed-tensors a partir do PyPI para obter a versão estável mais recente (recomendado) ou instale-o a partir do código-fonte para obter os recursos mais recentes.

PyPI
código-fonte
pip install compressed-tensors

Pesquise usando a tag compressed-tensors para encontrar um modelo compatível no Hugging Face Hub.

Modelos pré-quantizados podem ser carregados diretamente. Para quantizar um modelo para o formato compressed-tensors, consulte llm-compressor. Alternativamente, os modelos podem ser criados de forma independente e serializados com uma configuração compressed-tensors.

from transformers import AutoModelForCausalLM

ct_model = AutoModelForCausalLM.from_pretrained("nm-testing/Meta-Llama-3.1-8B-Instruct-FP8-hf", device_map="auto")

# measure memory usage
mem_params = sum([param.nelement()*param.element_size() for param in ct_model.parameters()])
print(f"{mem_params/2**30:.4f} GB")
# 8.4575 GB

Modos de carregamento

Um checkpoint do compressed-tensors armazena seus pesos em formato comprimido (fp8, ou int4/int8 empacotados). A forma como eles são executados depende de dois argumentos de CompressedTensorsConfig.

ConfiguraçãoPesos após o carregamentoExecução
defaultmantido no formato compressed-tensors: cada tensor serializado (weight_packed, weight_scale, weight_zero_point, …) é carregado como armazenadocompressed-tensors possui as camadas e descomprime o modelo na primeira passagem de frente
dequantize=Truedescomprimido durante o carregamento no dtype do modelo (por exemplo, BF16)um modelo denso simples sem nenhum quantization_scheme restante, de modo que as ativações w8a8 também não são quantizadas; ele pode ser ajustado ou salvo nesse dtype
use_optimized_inference=Truemantido quantizadocamadas cujo esquema possui um kernel executado através delas, atualmente W8A8 fp8; apenas para inferência

Com o padrão, save_pretrained() grava os tensores comprimidos de volta sem alterações, de modo que um modelo carregado é salvo no mesmo formato de tensores comprimidos.

Aceleração de kernels FP8

Passe use_optimized_inference=True para manter um modelo de compressed-tensors em FP8 em FP8 e executar seus matmuls por meio de kernels de FP8 acelerados por hardware (torch.nn.functional.scaled_mm, que despacha para torch._scaled_mm_v2; versões mais antigas do torch fazem fallback para torch._scaled_mm), em vez de desquantizar os pesos de volta para BF16. Manter os pesos em FP8 durante todo o inferência reduz o uso de memória e acelera o cálculo. Isso é apenas para inferência, então deixe desativado para fazer fine-tuning.

DispositivoKernelObservações
Intel XPUtorch.nn.functional.scaled_mmTodos os dispositivos XPU com suporte a FP8
NVIDIA CUDA (SM89+)torch.nn.functional.scaled_mmAda Lovelace (L4, L40), Hopper (H100), Blackwell e mais recentes
Apple silicon (MPS)torch.nn.functional.scaled_mmExige torch >= 2.15
CPU / CUDA SM80 (A100) / MPS com torch < 2.15Fallbackuse_optimized_inference=True é ignorado, o modelo é executado sem desquantização

O caminho do kernel FP8 suporta esses layouts de quantização.

EstratégiaModelo de exemplo
Dinâmica por canalRedHatAI/Meta-Llama-3.1-8B-Instruct-FP8-dynamic
Estático por tensorRedHatAI/Meta-Llama-3.1-8B-Instruct-FP8

Carregando um modelo FP8 pré-quantizado

Os kernels FP8 são opcionais: solicite-os com use_optimized_inference=True, e eles serão utilizados quando a config do modelo especificar quantização FP8 e uma GPU compatível estiver disponível.

from transformers import AutoModelForCausalLM, AutoTokenizer, CompressedTensorsConfig

model = AutoModelForCausalLM.from_pretrained(
    "RedHatAI/Meta-Llama-3.1-8B-Instruct-FP8-dynamic",
    quantization_config=CompressedTensorsConfig(use_optimized_inference=True),
    device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained("RedHatAI/Meta-Llama-3.1-8B-Instruct-FP8-dynamic")
inputs = tokenizer("Hello, how are you?", return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens=20)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

Desquantização no momento do carregamento

Sem use_optimized_inference=True, o modelo segue a rota regular do compressed-tensors: os pesos permanecem comprimidos e o compressed-tensors os descomprime na primeira passada de forward. Passe dequantize=True para descomprimi-los durante o carregamento, o que é o que você deseja ao fazer fine-tuning do modelo ou salvá-lo em sua precisão original (por exemplo, BF16). O resultado é um modelo denso simples: nenhum esquema do compressed-tensors é anexado, portanto, a quantização de ativação (por exemplo, w8a8) também é descartada.

from transformers import AutoModelForCausalLM, CompressedTensorsConfig

model = AutoModelForCausalLM.from_pretrained(
    "RedHatAI/Meta-Llama-3.1-8B-Instruct-FP8-dynamic",
    quantization_config=CompressedTensorsConfig(dequantize=True),
    device_map="auto",
)

Checkpoint do modelo

Modelos compressed-tensor são definidos por meio de sua entrada de configuração. O exemplo a seguir foi extraído do arquivo config.json do nm-testing/Meta-Llama-3.1-8B-Instruct-FP8-hf.

Há muitas entradas para permitir uma expressão flexível tanto durante quanto após a compressão, mas as entradas para carregamento e inferência podem ser simplificadas para focar apenas em algumas entradas-chave.

"quantization_config": {
  "config_groups": {
    "group_0": {
      "input_activations": {
        "num_bits": 8,
        "strategy": "tensor",
        "type": "float"
      },
      "targets": ["Linear"],
      "weights": {
        "num_bits": 8,
        "strategy": "tensor",
        "type": "float"
      }
    }
  },
  "format": "naive-quantized",
  "ignore": ["lm_head"],
  "quant_method": "compressed-tensors",
  "quantization_status": "frozen"
},

O arquivo de configuração especifica a quantização de um grupo de configuração (group_0), que inclui a quantização de pesos e ativações para fp8 com uma estratégia estática por tensor. O módulo lm_head não é quantizado, conforme indicado na chave ignore.

Para uma análise mais detalhada dos pesos do modelo, use o safetensors viewer no cartão do modelo para ver os pesos quantizados, a escala de entrada e a escala de peso de todos os módulos nn.Linear.

TensoresFormatoPrecisão
model.layers.0.input_layernorm.weight[4 096]BF16
model.layers.0.mlp.down_proj.input_scale[1]BF16
model.layers.0.mlp.down_proj.weight[4 096, 14 336]F8_E4M3
model.layers.0.mlp.down_proj.weight_scale[1]BF16
model.layers.0.mlp.gate_proj.input_scale[1]BF16
model.layers.0.mlp.gate_proj.weight[14 336, 4 096]F8_E4M3
model.layers.0.mlp.gate_proj.weight_scale[1]BF16
model.layers.0.mlp.up_proj.input_scale[1]BF16
model.layers.0.mlp.up_proj.weight[14 336, 4 096]F8_E4M3
model.layers.0.mlp.up_proj.weight_scale[1]BF16
model.layers.0.post_attention_layernorm.weight[4 096]BF16
model.layers.0.self_attn.k_proj.input_scale[1]BF16
model.layers.0.self_attn.k_proj.weight[1 024, 4 096]F8_E4M3
model.layers.0.self_attn.k_proj.weight_scale[1]BF16
model.layers.0.self_attn.o_proj.input_scale[1]BF16
model.layers.0.self_attn.o_proj.weight[4 096, 4 096]F8_E4M3
model.layers.0.self_attn.o_proj.weight_scale[1]BF16
model.layers.0.self_attn.q_proj.input_scale[1]BF16
model.layers.0.self_attn.q_proj.weight[4 096, 4 096]F8_E4M3
model.layers.0.self_attn.q_proj.weight_scale[1]BF16
model.layers.0.self_attn.v_proj.input_scale[1]BF16
model.layers.0.self_attn.v_proj.weight[1 024, 4 096]F8_E4M3
model.layers.0.self_attn.v_proj.weight_scale[1]BF16

Ao carregar um modelo do compressed-tensors com a integração ~quantizers.HFQuantizer, os módulos-alvo são transferidos para o compressed-tensors: ele anexa o quantization_scheme resolvido, define quantization_status, registra os parâmetros armazenados no checkpoint (weight em fp8, além de weight_scale e, para uma estratégia estática, input_scale) e instala seu próprio forward pass sobre eles. Eles permanecem como instâncias de nn.Linear, e é isso que o print exibe — as versões recentes do compressed-tensors não os envolvem mais em uma subclasse CompressedLinear. Os módulos listados em ignore, como lm_head, são deixados intactos.

Com dequantize=False (o padrão), os pesos ainda ficam comprimidos após o carregamento, e o compressed-tensors descomprime o modelo inteiro na primeira passada de forward. dequantize=True descomprime cada peso durante o carregamento, de modo que nenhuma passada de forward é necessária para obter pesos densos, e os módulos são camadas densas simples, sem quantization_scheme.

import torch
from transformers import AutoModelForCausalLM, CompressedTensorsConfig

model_id = "nm-testing/Meta-Llama-3.1-8B-Instruct-FP8-hf"

ct_model = AutoModelForCausalLM.from_pretrained(
    model_id,
    quantization_config=CompressedTensorsConfig(dequantize=False),
    device_map="auto",
)
q_proj = ct_model.model.layers[0].self_attn.q_proj
print(q_proj, q_proj.quantization_status)
# Linear(in_features=4096, out_features=4096, bias=False) QuantizationStatus.COMPRESSED
# ^ compressed-tensors module: fp8 weight, weight_scale, and its own forward

ct_model(input_ids=torch.tensor([[0, 1, 2]], device=ct_model.device))
print(q_proj, q_proj.quantization_status)
# Linear(in_features=4096, out_features=4096, bias=False) QuantizationStatus.DECOMPRESSED
# ^ weight is BF16 now, decompressed by that forward pass

ct_model = AutoModelForCausalLM.from_pretrained(
    model_id,
    quantization_config=CompressedTensorsConfig(dequantize=True),
    device_map="auto",
)
print(ct_model.model.layers[0].self_attn.q_proj)
# Linear(in_features=4096, out_features=4096, bias=False)      weight: BF16

Com use_optimized_inference=True, as camadas cobertas por um grupo de configuração fp8 são substituídas por CompressedTensorsFP8Linear, que armazena o peso fp8 e sua escala no layout esperado pelo kernel de matmul por linha. Esses pesos permanecem em fp8, inclusive nos passes de forward.

from transformers import AutoModelForCausalLM, CompressedTensorsConfig

ct_model = AutoModelForCausalLM.from_pretrained(
    "nm-testing/Meta-Llama-3.1-8B-Instruct-FP8-hf",
    quantization_config=CompressedTensorsConfig(use_optimized_inference=True),
    device_map="auto",
)
print(ct_model.model.layers[0].self_attn.q_proj)
# CompressedTensorsFP8Linear(in_features=4096, out_features=4096, bias=False)      weight: F8_E4M3
Update on GitHub