@@ -86,6 +86,7 @@ def __init__(
8686 multi_gpu = False ,
8787 gpu_ids = None ,
8888 post_processes = None ,
89+ quantization_progress = True ,
8990 ):
9091 """__init__ method
9192
@@ -130,6 +131,11 @@ def __init__(
130131 a quantized model on CPU (built via
131132 ``create_quantized_model``) and may modify it in-place.
132133 Processes are executed in order. Default is None.
134+ quantization_progress (bool):
135+ When ``True`` (default), emit ``[progress]`` log lines with
136+ completed steps, elapsed time, and a linear ETA estimate
137+ during long quantization (calibration, chunked, multi-GPU,
138+ QEP). Set to ``False`` for quiet runs (e.g. CI).
133139
134140 Note:
135141 For zero-config quantization (VRAM auto-estimation +
@@ -215,6 +221,7 @@ def __init__(
215221 self .lpcd_config = None
216222 if lpcd :
217223 self .lpcd_config = lpcd_config if lpcd_config is not None else LPCDConfig ()
224+ self .quantization_progress = quantization_progress
218225
219226 def check (self ):
220227 """Check the settings
@@ -323,27 +330,19 @@ def _exclude_moe_router_if_needed(self):
323330 config = self .model_config .load_config ()
324331 num_experts = (
325332 getattr (config , "num_experts" , 0 )
326- or getattr (
327- getattr (config , "text_config" , None ), "num_experts" , 0
328- ) or
329- 0
333+ or getattr (getattr (config , "text_config" , None ), "num_experts" , 0 )
334+ or 0
330335 )
331336 if num_experts == 0 :
332337 return
333338
334339 keyword = "router"
335- target_quantizers = (
336- self .quantizers
337- if self .quantizers is not None
338- else [self .quantizer ]
339- )
340+ target_quantizers = self .quantizers if self .quantizers is not None else [self .quantizer ]
340341 for q in target_quantizers :
341342 if q .exclude_layer_keywords is None :
342343 q .exclude_layer_keywords = [keyword ]
343344 elif keyword not in q .exclude_layer_keywords :
344- q .exclude_layer_keywords = list (q .exclude_layer_keywords ) + [
345- keyword
346- ]
345+ q .exclude_layer_keywords = list (q .exclude_layer_keywords ) + [keyword ]
347346
348347 self .logger .info (
349348 "MoE model (num_experts=%d): excluding '%s' layers from "
@@ -520,12 +519,9 @@ def auto_run(
520519 uniform_bit = max (valid_wbits )
521520 if save_dir == "auto" :
522521 model_name = model_id .rstrip ("/" ).split ("/" )[- 1 ]
523- save_dir = (
524- f"{ model_name } -gptq-{ uniform_bit } bit"
525- )
522+ save_dir = f"{ model_name } -gptq-{ uniform_bit } bit"
526523 logger .warning (
527- "Gemma 4 detected → falling back to uniform GPTQ %d-bit "
528- "(target wbits=%.2f)" ,
524+ "Gemma 4 detected → falling back to uniform GPTQ %d-bit " "(target wbits=%.2f)" ,
529525 uniform_bit ,
530526 wbits ,
531527 )
@@ -536,6 +532,7 @@ def auto_run(
536532 save_dir = f"{ model_name } -autobit-{ wbits } bit"
537533
538534 from .quantizer .autobit import AutoBitQuantizer
535+
539536 candidate_quantizers = [
540537 GPTQ (wbits = b , groupsize = groupsize , ** kwargs ) for b in candidate_bits
541538 ]
@@ -604,8 +601,30 @@ def quantize_with_calibration(self):
604601
605602 # Register hooks to all linear layers
606603 handles = []
604+ progress = None
605+ if self .quantization_progress :
606+ # pylint: disable-next=import-outside-toplevel
607+ from .utils .quantization_progress import QuantizationProgressTracker
608+
609+ progress = QuantizationProgressTracker (
610+ logger ,
611+ len (self .quantizer .module_to_name ),
612+ "Calibration quantization layers" ,
613+ )
614+
615+ if progress :
616+ quantize_bound = self .quantizer .quantize
617+
618+ def _quantize_hook (module , input , output ): # pylint: disable=redefined-builtin
619+ quantize_bound (module , input , output )
620+ progress .step_complete (self .quantizer .module_to_name [module ])
621+
622+ hook_fn = _quantize_hook
623+ else :
624+ hook_fn = self .quantizer .quantize
625+
607626 for module in self .quantizer .module_to_name .keys ():
608- handle = module .register_forward_hook (self . quantizer . quantize )
627+ handle = module .register_forward_hook (hook_fn )
609628 handles .append (handle )
610629
611630 logger .info ("Quantizing the model using %s" , self .quantizer .name )
@@ -645,6 +664,7 @@ def quantize_with_calibration_chunked(self):
645664 model_config = self .model_config ,
646665 quantizers = self .quantizers if self .quantizers is not None else [self .quantizer ],
647666 calibration_config = self .calibration_config ,
667+ quantization_progress = self .quantization_progress ,
648668 )
649669
650670 def quantize_with_calibration_on_multi_gpu (self ):
@@ -673,6 +693,7 @@ def quantize_with_calibration_on_multi_gpu(self):
673693 quantizer = self .quantizer ,
674694 calibration_config = self .calibration_config ,
675695 gpu_ids = self .gpu_ids ,
696+ quantization_progress = self .quantization_progress ,
676697 )
677698
678699 # Store results in quantizer.results
@@ -699,8 +720,20 @@ def quantize_without_calibration(self):
699720 "Quantizing the model without calibration using %s" ,
700721 self .quantizer .name ,
701722 )
723+ progress = None
724+ if self .quantization_progress :
725+ # pylint: disable-next=import-outside-toplevel
726+ from .utils .quantization_progress import QuantizationProgressTracker
727+
728+ progress = QuantizationProgressTracker (
729+ logger ,
730+ len (self .quantizer .module_to_name ),
731+ "Quantization without calibration (layers)" ,
732+ )
702733 for module in self .quantizer .module_to_name .keys ():
703734 self .quantizer .quantize (module , None , None )
735+ if progress :
736+ progress .step_complete (self .quantizer .module_to_name [module ])
704737
705738 self .quantizer .execute_post_processing ()
706739
@@ -721,6 +754,7 @@ def quantize_with_qep(self):
721754 quantizer = self .quantizer ,
722755 qep_config = self .qep_config ,
723756 calibration_config = self .calibration_config ,
757+ quantization_progress = self .quantization_progress ,
724758 )
725759
726760 if self .qep_config .general :
@@ -868,7 +902,7 @@ def prepare_calibration_dataset(self, device, model=None):
868902
869903 Args:
870904 device (torch.device): Device to place tensors on (CPU or GPU)
871- model: Model instance (optional). Add model-specific fields
905+ model: Model instance (optional). Add model-specific fields
872906 (e.g. mm_token_type_ids for Gemma 4).
873907
874908 Returns:
@@ -992,10 +1026,7 @@ def save_quantization_statistics(self, path: str, quantizer=None):
9921026
9931027 logger .info ("Saving the quantization statistics to %s" , path )
9941028
995- statistics = {
996- key : result .get_statistics ()
997- for key , result in quantizer .results .items ()
998- }
1029+ statistics = {key : result .get_statistics () for key , result in quantizer .results .items ()}
9991030
10001031 with open (path , "w" , encoding = "utf-8" ) as f :
10011032 json .dump (statistics , f , indent = 4 )
@@ -1674,15 +1705,10 @@ def create_quantized_model(self, pack_weights: bool = True, quantizer=None, use_
16741705 # cf) https://docs.vllm.ai/en/stable/features/quantization/#implementing-a-quantized-moe-method
16751706 num_experts = (
16761707 getattr (model .config , "num_experts" , None )
1677- or getattr (
1678- getattr (model .config , "text_config" , None ), "num_experts" , None
1679- )
1708+ or getattr (getattr (model .config , "text_config" , None ), "num_experts" , None )
16801709 or 0
16811710 )
1682- if (
1683- quant_config .get ("quant_method" ) == "gptq"
1684- and num_experts > 0
1685- ):
1711+ if quant_config .get ("quant_method" ) == "gptq" and num_experts > 0 :
16861712 quant_config ["quant_method" ] = "mixed_gptq"
16871713 self .logger .info (
16881714 "MoE model detected (num_experts=%d): "
@@ -1706,15 +1732,13 @@ def _patch_k_eq_v_for_vllm(self, model, quant_config: dict) -> None:
17061732 Gemma4 full-attention layers with attention_k_eq_v=True have no
17071733 v_proj weight — the model reuses key states as value states.
17081734 vLLM fuses q/k/v into a single qkv_proj and requires all shards
1709- to share the same quantization status.
1735+ to share the same quantization status.
17101736 """
17111737 text_cfg = getattr (model .config , "text_config" , None )
17121738 if text_cfg is None or not getattr (text_cfg , "attention_k_eq_v" , False ):
17131739 return
17141740 layer_types = getattr (text_cfg , "layer_types" , [])
1715- k_eq_v_indices = {
1716- i for i , lt in enumerate (layer_types ) if lt == "full_attention"
1717- }
1741+ k_eq_v_indices = {i for i , lt in enumerate (layer_types ) if lt == "full_attention" }
17181742 if not k_eq_v_indices :
17191743 return
17201744
@@ -1752,9 +1776,7 @@ def _patch_k_eq_v_for_vllm(self, model, quant_config: dict) -> None:
17521776 and "self_attn.k_proj" in layer_cfg
17531777 and "self_attn.v_proj" not in layer_cfg
17541778 ):
1755- layer_cfg ["self_attn.v_proj" ] = copy .deepcopy (
1756- layer_cfg ["self_attn.k_proj" ]
1757- )
1779+ layer_cfg ["self_attn.v_proj" ] = copy .deepcopy (layer_cfg ["self_attn.k_proj" ])
17581780
17591781 for key in ("modules_in_block_to_quantize" , "quantized_layer_names" ):
17601782 names = quant_config .get (key , [])
@@ -1825,6 +1847,7 @@ def save_quantized_model(self, save_directory: str, pack_weights: bool = True):
18251847 if src_dir and not os .path .isdir (src_dir ):
18261848 # when the model_id is specified, the path is modifed to the local directory
18271849 from huggingface_hub import snapshot_download
1850+
18281851 src_dir = snapshot_download (src_dir , local_files_only = True )
18291852 if src_dir and os .path .isdir (src_dir ):
18301853 for fname in ("processor_config.json" , "preprocessor_config.json" ):
0 commit comments