Transformers documentation
compressed-tensors
compressed-tensors
compressed-tensors расширяет файлы safetensors сжатыми типами данных тензоров, обеспечивая единый формат контрольных точек для сохранения и загрузки различных форматов квантования, таких как dense, int-квантование (int8), float-квантование (fp8) и pack-квантование (int4 или int8, весовое квантование, упакованное в int32).
compressed-tensors поддерживает дообучение с PEFT и включает в себя следующие возможности.
- Точности весов и активаций fp8, int4, int8.
- Стратегии масштабов квантования и нулевых точек для tensor, channel, group, block, token.
- Динамическое квантование активаций для каждого токена (или любая статическая стратегия).
- Квантование произвольных модулей, а не только модулей nn.Linear.
- Целевая поддержка конкретных модулей по имени или классу.
Установите compressed-tensors из PyPI, чтобы получить последнюю стабильную версию (рекомендуется), или установите пакет из исходного кода, чтобы получить доступ к новейшим функциям.
pip install compressed-tensors
git clone https://github.com/neuralmagic/compressed-tensors
cd compressed-tensors
pip install -e .Используйте тег compressed-tensors для поиска совместимой модели на Hugging Face Hub.
Предварительно квантованные модели можно загружать напрямую. Чтобы квантовать модель в формат compressed-tensors, см. llm-compressor. Альтернативно, модели можно создавать независимо и сериализовать с конфигурацией 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Режимы загрузки
Чекпоинт compressed-tensors хранит веса в сжатом виде (fp8 или упакованные int4/int8). Способ их выполнения определяется двумя аргументами CompressedTensorsConfig.
| Конфигурация | Веса после загрузки | Выполнение |
|---|---|---|
| default | хранятся в формате compressed-tensors: каждый сериализованный тензор (weight_packed, weight_scale, weight_zero_point, …) загружается в том виде, в котором он сохранён | compressed-tensors управляет слоями и декомпрессирует модель при первом проходе |
dequantize=True | распаковываются при загрузке в модельный dtype (например, BF16) | обычная плотная модель, в которой не осталось quantization_scheme, поэтому активации w8a8 также не квантуются; её можно дообучить или сохранить в этом типе данных |
use_optimized_inference=True | остался в квантованном виде | слои, в схеме которых используется ядро, в настоящее время W8A8 fp8; только для инференса |
По умолчанию save_pretrained() записывает сжатые тензоры без изменений, поэтому загруженная модель сохраняется в том же формате compressed-tensors.
Ускорение FP8-ядер
Передайте use_optimized_inference=True, чтобы модель compressed-tensors в формате FP8 оставалась в FP8, а её операции matmul выполнялись через аппаратно-ускоренные ядра FP8 (torch.nn.functional.scaled_mm, которое вызывает torch._scaled_mm_v2; в более старых версиях torch используется torch._scaled_mm), а не через деквантование весов обратно в BF16. Хранение весов в FP8 на протяжении всего инференса снижает потребление памяти и ускоряет вычисления. Это работает только для инференса, поэтому отключите этот параметр при дообучении.
| Устройство | Ядро | Примечания |
|---|---|---|
| Intel XPU | torch.nn.functional.scaled_mm | Все устройства XPU с поддержкой FP8 |
| NVIDIA CUDA (SM89+) | torch.nn.functional.scaled_mm | Ada Lovelace (L4, L40), Hopper (H100), Blackwell и более новые |
| Apple silicon (MPS) | torch.nn.functional.scaled_mm | Требуется torch >= 2.15 |
| CPU / CUDA SM80 (A100) / MPS с torch < 2.15 | Резервный вариант | use_optimized_inference=True игнорируется, модель работает в режиме декуантизации |
Ядро FP8 поддерживает следующие схемы квантования.
| Стратегия | Пример модели |
|---|---|
| Динамическая по каналам | RedHatAI/Meta-Llama-3.1-8B-Instruct-FP8-dynamic |
| Статическая по тензорам | RedHatAI/Meta-Llama-3.1-8B-Instruct-FP8 |
Загрузка предварительно квантованной модели FP8
Ядра FP8 включаются по запросу: укажите use_optimized_inference=True, и они будут использоваться, если конфигурация модели задаёт квантование FP8 и доступно поддерживаемое GPU.
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))Декуантизация при загрузке
Без use_optimized_inference=True модель использует стандартный путь compressed-tensors: веса остаются сжатыми, а compressed-tensors разжимает их при первом проходе. Передайте dequantize=True, чтобы разжать их при загрузке, что необходимо для дообучения модели или сохранения её в исходной точности (например, BF16). В результате получается обычная плотная модель: схема compressed-tensors не привязывается, поэтому квантование активаций (например, 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",
)Чекпоинт модели
Модели compressed-tensor определяются через соответствующий параметр в конфигурации. Приведённый ниже пример взят из файла config.json модели nm-testing/Meta-Llama-3.1-8B-Instruct-FP8-hf.
Список содержит множество записей, что позволяет гибко настраивать процесс как во время сжатия, так и после него, однако записи, отвечающие за загрузку и инференс, можно упростить, сосредоточившись лишь на нескольких ключевых параметрах.
"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"
},Файл конфигурации задаёт квантование группы конфигурации (group_0), которая включает квантование весов и активаций до fp8 со статической стратегией per-tensor. Модуль lm_head не квантуется, как показано в ключе ignore.
Для более подробного изучения весов модели используйте просмотрщик safetensors на карточке модели, чтобы увидеть квантованные веса, масштаб входных данных и масштаб весов для всех модулей nn.Linear.
| Тензоры | Форма | Точность |
|---|---|---|
| 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 |
При загрузке модели compressed-tensors с интеграцией ~quantizers.HFQuantizer целевые модули передаются в compressed-tensors: он присоединяет разрешённую quantization_scheme, устанавливает quantization_status, регистрирует параметры, которые хранятся в чекпоинте (weight в fp8, а также weight_scale и, для статической стратегии, input_scale), и устанавливает собственный forward-проход для них. Они остаются экземплярами nn.Linear, поэтому именно это показывает print — в последних версиях compressed-tensors они больше не оборачиваются в подкласс CompressedLinear. Модули, перечисленные в ignore, например lm_head, остаются без изменений.
При dequantize=False (значение по умолчанию) веса остаются сжатыми после завершения загрузки, а compressed-tensors разжимает всю модель при первом проходе. dequantize=True разжимает каждый вес в процессе его загрузки, поэтому для получения плотных весов не требуется пропускать данные через модель, а модули становятся обычными плотными слоями без 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При use_optimized_inference=True слои, охваченные группой конфигурации fp8, заменяются на CompressedTensorsFP8Linear, который хранит вес fp8 и его масштаб в том расположении, которое ожидает его ядро row-wise matmul. Эти веса остаются в fp8, включая прохождение вперед.
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