@@ -107,6 +107,63 @@ def __init__(self, config: GPTQConfig):
107107 # Reference to original model for use_orig_model_inference and GPTQv2
108108 self .orig_model : Optional [torch .nn .Module ] = None
109109
110+ @staticmethod
111+ def _cast_to_double (obj ):
112+ """Recursively cast all tensors in a list/dict/tensor to float64."""
113+ if isinstance (obj , torch .Tensor ):
114+ return obj .double ()
115+ if isinstance (obj , (list , tuple )):
116+ return type (obj )(GPTQQuantizer ._cast_to_double (o ) for o in obj )
117+ if isinstance (obj , dict ):
118+ return {k : GPTQQuantizer ._cast_to_double (v ) for k , v in obj .items ()}
119+ return obj
120+
121+ def _run_layer_forward_double_precision (
122+ self ,
123+ layer : torch .nn .Module ,
124+ args : List [Any ],
125+ kwargs : Dict [str , Any ],
126+ double_precision : bool ,
127+ ):
128+ """Run a layer forward, optionally in float64 for batch-size-independence.
129+
130+ When double_precision=True, temporarily casts the layer and its inputs to float64
131+ so that GPU matmul tiling differences don't produce different results
132+ for different batch sizes. Outputs are kept in float64 to avoid losing
133+ precision when they become the next layer's input.
134+ """
135+ if not double_precision :
136+ return layer (* args , ** kwargs )
137+
138+ # Save original dtypes and cast layer to double
139+ orig_dtypes : Dict [str , torch .dtype ] = {}
140+ for name , param in layer .named_parameters ():
141+ orig_dtypes [name ] = param .dtype
142+ param .data = param .data .double ()
143+ for name , buf in layer .named_buffers ():
144+ orig_dtypes [f"buf:{ name } " ] = buf .dtype
145+ buf .data = buf .data .double ()
146+
147+ # Cast inputs to double
148+ args_d = self ._cast_to_double (args )
149+ kwargs_d = self ._cast_to_double (kwargs )
150+
151+ outs = layer (* args_d , ** kwargs_d )
152+
153+ # Keep outputs in float64 — casting to float32 would introduce
154+ # batch-size-dependent rounding when these outputs become the
155+ # next layer's input, defeating the purpose of double precision.
156+ if isinstance (outs , tuple ):
157+ outs = outs [0 ]
158+
159+ # Restore original dtypes
160+ for name , param in layer .named_parameters ():
161+ param .data = param .data .to (orig_dtypes [name ])
162+ for name , buf in layer .named_buffers ():
163+ buf .data = buf .data .to (orig_dtypes [f"buf:{ name } " ])
164+
165+ return outs
166+
110167 def _resolve_weight_bits (
111168 self ,
112169 gptq_conf : GPTQConfig ,
@@ -335,7 +392,12 @@ def convert(self, model):
335392 cache_kwargs_batch = move_to_device (cache_kwargs_batch , device )
336393
337394 orig_layer = orig_layers [l_idx ].to (device )
338- orig_layer (* cache_args_batch , ** cache_kwargs_batch )
395+ if gptq_conf .double_precision :
396+ self ._run_layer_forward_double_precision (
397+ orig_layer , cache_args_batch , cache_kwargs_batch , True
398+ )
399+ else :
400+ orig_layer (* cache_args_batch , ** cache_kwargs_batch )
339401 orig_layer .cpu ()
340402
341403 fp_inputs_cache .clear_hook ()
@@ -346,10 +408,10 @@ def convert(self, model):
346408
347409 gptq : Dict [str , GPTQ ] = {}
348410 for name in subset :
349- gptq [name ] = GPTQ (subset [name ])
411+ full_module_name = module_name [subset [name ]]
412+ gptq [name ] = GPTQ (subset [name ], double_precision = gptq_conf .double_precision , layer_name = full_module_name )
350413 gptq [name ].saturation_threshold = gptq_conf .saturation_threshold
351414 gptq [name ].saturation_min_batches = gptq_conf .saturation_min_batches
352- full_module_name = module_name [subset [name ]]
353415 weight_bits = self ._resolve_weight_bits (
354416 gptq_conf ,
355417 full_module_name = full_module_name ,
@@ -369,6 +431,7 @@ def convert(self, model):
369431 sym = gptq_conf .symmetric ,
370432 mse = gptq_conf .mse ,
371433 sensitivity = cur_sensitivity ,
434+ mse_tolerance = gptq_conf .mse_tolerance ,
372435 )
373436
374437 # GPTQv2: Assign native_inp from FPInputsCache
@@ -413,7 +476,12 @@ def _hook(_, inp, out):
413476 )
414477 cache_kwargs_batch = move_to_device (cache_kwargs_batch , device )
415478
416- layer (* cache_args_batch , ** cache_kwargs_batch )
479+ if gptq_conf .double_precision :
480+ self ._run_layer_forward_double_precision (
481+ layer , cache_args_batch , cache_kwargs_batch , True
482+ )
483+ else :
484+ layer (* cache_args_batch , ** cache_kwargs_batch )
417485
418486 # Remove handles
419487 for h in handles :
@@ -435,6 +503,7 @@ def _hook(_, inp, out):
435503 adaptive_percdamp = gptq_conf .adaptive_percdamp ,
436504 cond_threshold_good = gptq_conf .cond_threshold_good ,
437505 use_iterate = gptq_conf .use_iterate ,
506+ actorder_precision = gptq_conf .actorder_precision ,
438507 )
439508 quantizers [full_module_name ] = gptq [name ].quantizer
440509 gptq [name ].free ()
@@ -463,9 +532,14 @@ def _hook(_, inp, out):
463532 fp_cache_args_batch = gather_single_batch_from_list (fp_inps , batch_idx )
464533 fp_cache_args_batch = move_to_device (fp_cache_args_batch , device )
465534 orig_layer = orig_layers [l_idx ].to (device )
466- fp_outs = orig_layer (* fp_cache_args_batch , ** cache_kwargs_batch )
535+ if gptq_conf .double_precision :
536+ fp_outs = self ._run_layer_forward_double_precision (
537+ orig_layer , fp_cache_args_batch , cache_kwargs_batch , True
538+ )
539+ else :
540+ fp_outs = orig_layer (* fp_cache_args_batch , ** cache_kwargs_batch )
541+ fp_outs = fp_outs [0 ] if isinstance (fp_outs , tuple ) else fp_outs
467542 orig_layer .cpu ()
468- fp_outs = fp_outs [0 ] if isinstance (fp_outs , tuple ) else fp_outs
469543 # Update inputs for next iteration.
470544 if len (fp_inps ) > 0 :
471545 if hasattr (fp_outs , "to" ) and hasattr (
@@ -477,16 +551,28 @@ def _hook(_, inp, out):
477551 else :
478552 fp_inps [0 ][batch_idx ] = fp_outs
479553
480- if orig_layers is None or gptq_conf .gptq_v2 is True :
481- outs = layer (* cache_args_batch , ** cache_kwargs_batch )
554+ if gptq_conf .double_precision :
555+ if orig_layers is None or gptq_conf .gptq_v2 is True :
556+ outs = self ._run_layer_forward_double_precision (
557+ layer , cache_args_batch , cache_kwargs_batch , True
558+ )
559+ else :
560+ orig_layer = orig_layers [l_idx ].to (device )
561+ outs = self ._run_layer_forward_double_precision (
562+ orig_layer , cache_args_batch , cache_kwargs_batch , True
563+ )
564+ orig_layer .cpu ()
482565 else :
483- orig_layer = orig_layers [l_idx ].to (device )
484- outs = orig_layer (* cache_args_batch , ** cache_kwargs_batch )
485- orig_layer .cpu ()
486- # LLaMA's decoder layer return type differs across Transformers versions:
487- # some return a tuple (hidden_states, ...), others return just a tensor.
488- # This line ensures we always take the first element when it's a tuple.
489- outs = outs [0 ] if isinstance (outs , tuple ) else outs
566+ if orig_layers is None or gptq_conf .gptq_v2 is True :
567+ outs = layer (* cache_args_batch , ** cache_kwargs_batch )
568+ else :
569+ orig_layer = orig_layers [l_idx ].to (device )
570+ outs = orig_layer (* cache_args_batch , ** cache_kwargs_batch )
571+ orig_layer .cpu ()
572+ # LLaMA's decoder layer return type differs across Transformers versions:
573+ # some return a tuple (hidden_states, ...), others return just a tensor.
574+ # This line ensures we always take the first element when it's a tuple.
575+ outs = outs [0 ] if isinstance (outs , tuple ) else outs
490576 # Update inputs for next iteration.
491577 if len (self .cache_args ) > 0 :
492578 if hasattr (outs , "to" ) and hasattr (
@@ -557,8 +643,8 @@ def _quantize_lm_head(self, model, quantizers):
557643 self .cache_args [0 ][batch_idx ] = move_to_cpu (hidden_states )
558644
559645 layer = model .lm_head
560- gptq = GPTQ (layer )
561646 full_module_name = "lm_head"
647+ gptq = GPTQ (layer , double_precision = gptq_conf .double_precision , layer_name = full_module_name )
562648 weight_bits = self ._resolve_weight_bits (
563649 gptq_conf ,
564650 full_module_name = full_module_name ,
@@ -578,6 +664,7 @@ def _quantize_lm_head(self, model, quantizers):
578664 sym = gptq_conf .symmetric ,
579665 mse = gptq_conf .mse ,
580666 sensitivity = cur_sensitivity ,
667+ mse_tolerance = gptq_conf .mse_tolerance ,
581668 )
582669
583670 # Hook to collect (inp, out) for GPTQ with optional weights
@@ -628,6 +715,7 @@ def _hook(_, inp, out):
628715 adaptive_percdamp = gptq_conf .adaptive_percdamp ,
629716 cond_threshold_good = gptq_conf .cond_threshold_good ,
630717 use_iterate = gptq_conf .use_iterate ,
718+ actorder_precision = gptq_conf .actorder_precision ,
631719 )
632720 quantizers [f"lm_head" ] = gptq .quantizer
633721 gptq .free ()
0 commit comments