Skip to content

Commit bc6733c

Browse files
committed
[compressed-tensors] keep weights compressed unless dequantize=True
1 parent 93b1133 commit bc6733c

3 files changed

Lines changed: 28 additions & 38 deletions

File tree

‎src/transformers/quantizers/quantizer_compressed_tensors.py‎

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -111,26 +111,12 @@ def _process_model_before_weight_loading(self, model, **kwargs):
111111
ignore=ct_config.ignore,
112112
modules_to_not_convert=self.modules_to_not_convert,
113113
)
114-
apply_quantization_config(model, ct_config, run_compressed=False)
115-
# Packed formats (e.g. int4 `weight_packed`) need the compressed module layout to
116-
# receive the checkpoint tensors.
117-
if self.quantization_config.is_quantization_compressed:
118-
self.compressor.compress_model(model=model)
119-
return
120114

121-
# No kernel runs these weights compressed: the `deserialize` load operation decompresses them, and
122-
# compressed-tensors only sets up what quantizes at inference (activations, kv cache)
123-
from compressed_tensors.quantization import QuantizationStatus
124-
125-
ct_config.quantization_status = QuantizationStatus.FROZEN
126-
for group in ct_config.config_groups.values():
127-
group.weights = None
128-
ct_config.config_groups = {
129-
name: group
130-
for name, group in ct_config.config_groups.items()
131-
if group.input_activations is not None or group.output_activations is not None
132-
}
133115
apply_quantization_config(model, ct_config, run_compressed=False)
116+
# Packed formats (e.g. int4 `weight_packed`) need the compressed module layout to
117+
# receive the checkpoint tensors.
118+
if self.quantization_config.is_quantization_compressed:
119+
self.compressor.compress_model(model=model)
134120

135121
@property
136122
def is_trainable(self):
@@ -182,5 +168,5 @@ def get_weight_conversions(self):
182168
}
183169

184170
def get_load_operations(self):
185-
# Without kernels nothing runs compressed, so deserializing decompresses as well
186-
return {"deserialize": CompressedTensorsDequantize, "dequantize": CompressedTensorsDequantize}
171+
# No `deserialize`: the serialized tensors load as is into the params compressed-tensors creates
172+
return {"dequantize": CompressedTensorsDequantize}

‎tests/quantization/compressed_tensors_integration/test_compressed_models.py‎

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,9 @@ def test_no_warnings_for_all_models(self):
105105
@require_compressed_tensors
106106
@require_torch
107107
class DequantizeTest(unittest.TestCase):
108-
"""Non-FP8 schemes have no kernels, so they run dequantized: the weights are decompressed while loading.
109-
`dequantize=True` (or the deprecated `run_compressed=False`) also drops the activation quantization."""
108+
"""Non-FP8 schemes have no kernels, so they run dequantized. `dequantize=True` (or the
109+
deprecated `run_compressed=False`) does it at load time; by default the weights are left
110+
compressed and compressed-tensors decompresses them on the first forward pass."""
110111

111112
tinyllama_w4a16 = "nm-testing/tinyllama-w4a16-compressed"
112113
tinyllama_w8a8 = "nm-testing/tinyllama-w8a8-compressed"
@@ -120,19 +121,26 @@ def tearDown(self):
120121
backend_empty_cache(torch_device)
121122
gc.collect()
122123

123-
def test_default_load_decompresses_while_loading(self):
124+
def test_default_load_stays_compressed_until_first_forward(self):
124125
from compressed_tensors import QuantizationStatus
125126

126127
for stub in self.stubs:
127128
model = AutoModelForCausalLM.from_pretrained(stub)
128129

130+
compressed_count = sum(
131+
1 for m in model.modules() if getattr(m, "quantization_status", None) == QuantizationStatus.COMPRESSED
132+
)
133+
self.assertGreater(compressed_count, 0, "weights must not be decompressed at load time")
134+
135+
model(input_ids=torch.tensor([[0, 1, 2]]))
136+
137+
# The hook compressed-tensors registered decompressed the model on that forward pass.
129138
compressed_count = sum(
130139
1 for m in model.modules() if getattr(m, "quantization_status", None) == QuantizationStatus.COMPRESSED
131140
)
132141
self.assertEqual(compressed_count, 0, "no modules should be left in COMPRESSED state")
133142
self.assertFalse(any("weight_packed" in name for name, _ in model.named_parameters()))
134143
self.assertTrue(model.model.layers[0].self_attn.q_proj.weight.is_floating_point())
135-
model(input_ids=torch.tensor([[0, 1, 2]]))
136144

137145
def test_deprecated_run_compressed_maps_to_dequantize(self):
138146
# `run_compressed=False` is deprecated and must behave as `dequantize=True`.
@@ -166,17 +174,10 @@ def test_dequantize_outputs_match_default(self):
166174
self.assertEqual(tokenizer.decode(output_default[0]), tokenizer.decode(output_dequantized[0]))
167175

168176
def test_dequantize_drops_activation_quantization(self):
169-
"""`dequantize=True` loads a dense model: w8a8 also stops quantizing its activations, so its outputs differ
170-
from the default (quantized activations) load."""
171-
from transformers import AutoTokenizer
172-
173-
tokenizer = AutoTokenizer.from_pretrained(self.tinyllama_w8a8)
174-
input_ids = tokenizer(self.prompt, return_tensors="pt").input_ids
177+
"""`dequantize=True` loads a plain dense model: no compressed-tensors scheme is left, so w8a8 also stops
178+
quantizing its activations."""
175179
model = AutoModelForCausalLM.from_pretrained(
176180
self.tinyllama_w8a8, quantization_config=CompressedTensorsConfig(dequantize=True)
177181
)
178-
output = model.generate(input_ids, max_new_tokens=20, do_sample=False)
179-
self.assertEqual(
180-
tokenizer.decode(output[0]),
181-
"<s> Paris is the capital of which country?\n\n * a) France\n * b) Germany\n * c) Italy\n",
182-
)
182+
self.assertFalse(any(hasattr(module, "quantization_scheme") for module in model.modules()))
183+
self.assertTrue(model.model.layers[0].self_attn.q_proj.weight.is_floating_point())

‎tests/quantization/compressed_tensors_integration/test_compressed_tensors.py‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,20 +244,23 @@ def test_tinyllama_fp8_per_tensor_save_reload(self):
244244
self.assertLessEqual(torch.exp(outputs.loss), 20.0)
245245

246246
def test_use_optimized_inference_is_opt_in(self):
247-
"""Without `use_optimized_inference=True`, an FP8 checkpoint runs no kernels: its weights are
248-
decompressed while loading."""
247+
"""Without `use_optimized_inference=True`, an FP8 checkpoint takes the regular compressed-tensors
248+
route: no kernels, and with `dequantize=False` the weights are left compressed until the
249+
first forward pass decompresses them."""
249250
from transformers.integrations.compressed_tensors import CompressedTensorsFP8Linear
250251

251252
model = AutoModelForCausalLM.from_pretrained(self.tinyllama_fp8, device_map=torch_device)
252253
module = model.model.layers[0].self_attn.q_proj
253254

254255
self.assertNotIsInstance(module, CompressedTensorsFP8Linear)
255-
self.assertEqual(module.weight.dtype, model.dtype, "weights are decompressed while loading")
256+
self.assertEqual(module.weight.dtype, torch.float8_e4m3fn, "weights must not be decompressed at load time")
256257

257258
tokenizer = AutoTokenizer.from_pretrained(self.tinyllama_fp8)
258259
inputs = tokenizer(self.prompt, return_tensors="pt").to(model.device)
259260
with torch.no_grad():
260261
outputs = model(**inputs, labels=inputs["input_ids"])
262+
# compressed-tensors decompressed the model on that first forward pass
263+
self.assertEqual(module.weight.dtype, model.dtype)
261264
self.assertLessEqual(torch.exp(outputs.loss), 20.0)
262265

263266
def test_non_fp8_model_unaffected(self):

0 commit comments

Comments
 (0)