Transformers documentation
compressed-tensors
compressed-tensors
compressed-tensors extiende los archivos safetensors a tipos de datos de tensores comprimidos para ofrecer un formato de checkpoint unificado que permite guardar y cargar diversos formatos de cuantización, como denso, cuantizado en enteros (int8), cuantizado en coma flotante (fp8) y cuantizado empaquetado (int4 o int8, con pesos cuantizados empaquetados en int32).
compressed-tensors permite el ajuste fino con PEFT e incluye las siguientes características.
- precisones de pesos y activaciones fp8, int4 e int8.
- Estrategias de escalas de cuantización y puntos cero para tensor, canal, grupo, bloque, token.
- Cuantización dinámica de activaciones por token (o cualquier estrategia estática).
- Cuantización de módulos arbitrarios, no solo de módulos nn.Linear.
- Soporte específico para módulos concretos por nombre o clase.
Instale compressed-tensors desde PyPI para obtener la última versión estable (recomendado) o instálelo desde el código fuente para acceder a las funciones más recientes.
pip install compressed-tensors
git clone https://github.com/neuralmagic/compressed-tensors
cd compressed-tensors
pip install -e .Busca usando la etiqueta compressed-tensors para encontrar un modelo compatible en el Hugging Face Hub.
Los modelos pre-cuantizados se pueden cargar directamente. Para cuantizar un modelo al formato compressed-tensors, consulte llm-compressor. Alternativamente, los modelos se pueden crear de forma independiente y serializar con una configuración de 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 GBModos de carga
Un checkpoint de compressed-tensors almacena sus pesos comprimidos (fp8, o int4/int8 empaquetados). La forma en que se ejecutan depende de dos argumentos de CompressedTensorsConfig.
| Configuración | Pesos después de la carga | Ejecución |
|---|---|---|
| default | se mantiene en el formato compressed-tensors: cada tensor serializado (weight_packed, weight_scale, weight_zero_point, …) se carga tal como se almacena | compressed-tensors posee las capas y descomprime el modelo en la primera pasada hacia adelante |
dequantize=True | descomprimido al cargarlo en el dtype del modelo (p. ej., BF16) | un modelo denso simple sin ningún quantization_scheme restante, por lo que las activaciones w8a8 tampoco se cuantizan; se puede ajustar o guardar en ese dtype |
use_optimized_inference=True | mantenido cuantizado | capas cuyo esquema tiene un kernel que se ejecuta a través de ellas, actualmente W8A8 fp8; solo para inferencia |
Con el valor predeterminado, save_pretrained() escribe los tensores comprimidos sin cambios, por lo que un modelo cargado se guarda en el mismo formato de tensores comprimidos.
Aceleración de kernels FP8
Pase use_optimized_inference=True para mantener un modelo de compressed-tensors en FP8 y ejecutar sus matmuls a través de kernels de FP8 acelerados por hardware (torch.nn.functional.scaled_mm, que despacha a torch._scaled_mm_v2; las versiones anteriores de torch recurren a torch._scaled_mm), en lugar de descuantizar los pesos de vuelta a BF16. Mantener los pesos en FP8 durante toda la inferencia reduce el uso de memoria y acelera el cálculo. Esto es solo para inferencia, así que desactívelo para hacer fine-tuning.
| Dispositivo | Kernel | Notas |
|---|---|---|
| Intel XPU | torch.nn.functional.scaled_mm | Todos los dispositivos XPU con soporte para FP8 |
| NVIDIA CUDA (SM89+) | torch.nn.functional.scaled_mm | Ada Lovelace (L4, L40), Hopper (H100), Blackwell y más recientes |
| Apple silicon (MPS) | torch.nn.functional.scaled_mm | Requiere torch >= 2.15 |
| CPU / CUDA SM80 (A100) / MPS con torch < 2.15 | Alternativa | use_optimized_inference=True se ignora, el modelo se ejecuta descuantizado |
La ruta del kernel FP8 admite estos diseños de cuantización.
| Estrategia | Modelo de ejemplo |
|---|---|
| Dinámico por canal | RedHatAI/Meta-Llama-3.1-8B-Instruct-FP8-dynamic |
| Estático por tensor | RedHatAI/Meta-Llama-3.1-8B-Instruct-FP8 |
Carga de un modelo FP8 pre-cuantizado
Los núcleos FP8 son opcionales: solicítalos con use_optimized_inference=True, y se utilizan cuando la configuración del modelo especifica cuantización FP8 y hay una GPU compatible disponible.
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))Descomprimir en el momento de la carga
Sin use_optimized_inference=True, el modelo sigue la ruta regular de compressed-tensors: los pesos se dejan comprimidos y compressed-tensors los descomprime en la primera pasada hacia adelante. Pasa dequantize=True para descomprimirlos durante la carga, que es lo que necesitas para afinar el modelo o guardarlo en su precisión original (p. ej. BF16). El resultado es un modelo denso simple: no se adjunta ningún esquema de compressed-tensors, por lo que también se descarta la cuantización de activaciones (p. ej. w8a8).
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",
)Punto de control del modelo
Los modelos compressed-tensor se definen a través de su entrada de configuración. El siguiente ejemplo se toma del archivo config.json de nm-testing/Meta-Llama-3.1-8B-Instruct-FP8-hf.
Hay muchas entradas para permitir una expresión flexible tanto durante como después de la compresión, pero las entradas para la carga y la inferencia se pueden simplificar para centrarse solo en algunas entradas clave.
"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"
},El archivo de configuración especifica la cuantización de un grupo de configuración (group_0), que incluye la cuantización de pesos y activaciones a fp8 con una estrategia estática por tensor. El módulo lm_head no está cuantizado, como se muestra en la clave ignore.
Para una vista más detallada de los pesos del modelo, use el visor de safetensors en la tarjeta del modelo para ver los pesos cuantizados, la escala de entrada y la escala de pesos de todos los módulos nn.Linear.
| Tensores | Forma | Precisión |
|---|---|---|
| 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 |
Al cargar un modelo de compressed-tensors con la integración ~quantizers.HFQuantizer, los módulos objetivo se transfieren a compressed-tensors: este adjunta el quantization_scheme resuelto, establece quantization_status, registra los parámetros que almacena el checkpoint (weight en fp8, además de weight_scale y, para una estrategia estática, input_scale) e instala su propio paso de propagación hacia adelante sobre ellos. Siguen siendo instancias de nn.Linear, por lo que es lo que muestra print: las versiones recientes de compressed-tensors ya no los envuelven en una subclase CompressedLinear. Los módulos enumerados bajo ignore, como lm_head, se dejan sin modificar.
Con dequantize=False (el valor predeterminado), los pesos siguen comprimidos una vez finalizada la carga, y compressed-tensors descomprime todo el modelo en la primera pasada hacia adelante. dequantize=True descomprime cada peso mientras se carga, por lo que no se necesita una pasada hacia adelante para obtener pesos densos, y los módulos son capas densas simples sin 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: BF16Con use_optimized_inference=True, las capas cubiertas por un grupo de configuración fp8 se reemplazan por CompressedTensorsFP8Linear, que almacena el peso fp8 y su escala en el formato que espera su kernel de matmul por filas. Esos pesos permanecen en fp8, incluidas las pasadas hacia adelante.
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