@@ -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
@@ -314,27 +321,19 @@ def _exclude_moe_router_if_needed(self):
314321 config = self .model_config .load_config ()
315322 num_experts = (
316323 getattr (config , "num_experts" , 0 )
317- or getattr (
318- getattr (config , "text_config" , None ), "num_experts" , 0
319- ) or
320- 0
324+ or getattr (getattr (config , "text_config" , None ), "num_experts" , 0 )
325+ or 0
321326 )
322327 if num_experts == 0 :
323328 return
324329
325330 keyword = "router"
326- target_quantizers = (
327- self .quantizers
328- if self .quantizers is not None
329- else [self .quantizer ]
330- )
331+ target_quantizers = self .quantizers if self .quantizers is not None else [self .quantizer ]
331332 for q in target_quantizers :
332333 if q .exclude_layer_keywords is None :
333334 q .exclude_layer_keywords = [keyword ]
334335 elif keyword not in q .exclude_layer_keywords :
335- q .exclude_layer_keywords = list (q .exclude_layer_keywords ) + [
336- keyword
337- ]
336+ q .exclude_layer_keywords = list (q .exclude_layer_keywords ) + [keyword ]
338337
339338 self .logger .info (
340339 "MoE model (num_experts=%d): excluding '%s' layers from "
@@ -511,12 +510,9 @@ def auto_run(
511510 uniform_bit = max (valid_wbits )
512511 if save_dir == "auto" :
513512 model_name = model_id .rstrip ("/" ).split ("/" )[- 1 ]
514- save_dir = (
515- f"{ model_name } -gptq-{ uniform_bit } bit"
516- )
513+ save_dir = f"{ model_name } -gptq-{ uniform_bit } bit"
517514 logger .warning (
518- "Gemma 4 detected → falling back to uniform GPTQ %d-bit "
519- "(target wbits=%.2f)" ,
515+ "Gemma 4 detected → falling back to uniform GPTQ %d-bit " "(target wbits=%.2f)" ,
520516 uniform_bit ,
521517 wbits ,
522518 )
@@ -527,6 +523,7 @@ def auto_run(
527523 save_dir = f"{ model_name } -autobit-{ wbits } bit"
528524
529525 from .quantizer .autobit import AutoBitQuantizer
526+
530527 candidate_quantizers = [
531528 GPTQ (wbits = b , groupsize = groupsize , ** kwargs ) for b in candidate_bits
532529 ]
@@ -595,8 +592,30 @@ def quantize_with_calibration(self):
595592
596593 # Register hooks to all linear layers
597594 handles = []
595+ progress = None
596+ if self .quantization_progress :
597+ # pylint: disable-next=import-outside-toplevel
598+ from .utils .quantization_progress import QuantizationProgressTracker
599+
600+ progress = QuantizationProgressTracker (
601+ logger ,
602+ len (self .quantizer .module_to_name ),
603+ "Calibration quantization layers" ,
604+ )
605+
606+ if progress :
607+ quantize_bound = self .quantizer .quantize
608+
609+ def _quantize_hook (module , input , output ): # pylint: disable=redefined-builtin
610+ quantize_bound (module , input , output )
611+ progress .step_complete (self .quantizer .module_to_name [module ])
612+
613+ hook_fn = _quantize_hook
614+ else :
615+ hook_fn = self .quantizer .quantize
616+
598617 for module in self .quantizer .module_to_name .keys ():
599- handle = module .register_forward_hook (self . quantizer . quantize )
618+ handle = module .register_forward_hook (hook_fn )
600619 handles .append (handle )
601620
602621 logger .info ("Quantizing the model using %s" , self .quantizer .name )
@@ -636,6 +655,7 @@ def quantize_with_calibration_chunked(self):
636655 model_config = self .model_config ,
637656 quantizers = self .quantizers if self .quantizers is not None else [self .quantizer ],
638657 calibration_config = self .calibration_config ,
658+ quantization_progress = self .quantization_progress ,
639659 )
640660
641661 def quantize_with_calibration_on_multi_gpu (self ):
@@ -664,6 +684,7 @@ def quantize_with_calibration_on_multi_gpu(self):
664684 quantizer = self .quantizer ,
665685 calibration_config = self .calibration_config ,
666686 gpu_ids = self .gpu_ids ,
687+ quantization_progress = self .quantization_progress ,
667688 )
668689
669690 # Store results in quantizer.results
@@ -690,8 +711,20 @@ def quantize_without_calibration(self):
690711 "Quantizing the model without calibration using %s" ,
691712 self .quantizer .name ,
692713 )
714+ progress = None
715+ if self .quantization_progress :
716+ # pylint: disable-next=import-outside-toplevel
717+ from .utils .quantization_progress import QuantizationProgressTracker
718+
719+ progress = QuantizationProgressTracker (
720+ logger ,
721+ len (self .quantizer .module_to_name ),
722+ "Quantization without calibration (layers)" ,
723+ )
693724 for module in self .quantizer .module_to_name .keys ():
694725 self .quantizer .quantize (module , None , None )
726+ if progress :
727+ progress .step_complete (self .quantizer .module_to_name [module ])
695728
696729 self .quantizer .execute_post_processing ()
697730
@@ -712,6 +745,7 @@ def quantize_with_qep(self):
712745 quantizer = self .quantizer ,
713746 qep_config = self .qep_config ,
714747 calibration_config = self .calibration_config ,
748+ quantization_progress = self .quantization_progress ,
715749 )
716750
717751 if self .qep_config .general :
@@ -859,7 +893,7 @@ def prepare_calibration_dataset(self, device, model=None):
859893
860894 Args:
861895 device (torch.device): Device to place tensors on (CPU or GPU)
862- model: Model instance (optional). Add model-specific fields
896+ model: Model instance (optional). Add model-specific fields
863897 (e.g. mm_token_type_ids for Gemma 4).
864898
865899 Returns:
@@ -983,10 +1017,7 @@ def save_quantization_statistics(self, path: str, quantizer=None):
9831017
9841018 logger .info ("Saving the quantization statistics to %s" , path )
9851019
986- statistics = {
987- key : result .get_statistics ()
988- for key , result in quantizer .results .items ()
989- }
1020+ statistics = {key : result .get_statistics () for key , result in quantizer .results .items ()}
9901021
9911022 with open (path , "w" , encoding = "utf-8" ) as f :
9921023 json .dump (statistics , f , indent = 4 )
@@ -1665,15 +1696,10 @@ def create_quantized_model(self, pack_weights: bool = True, quantizer=None, use_
16651696 # cf) https://docs.vllm.ai/en/stable/features/quantization/#implementing-a-quantized-moe-method
16661697 num_experts = (
16671698 getattr (model .config , "num_experts" , None )
1668- or getattr (
1669- getattr (model .config , "text_config" , None ), "num_experts" , None
1670- )
1699+ or getattr (getattr (model .config , "text_config" , None ), "num_experts" , None )
16711700 or 0
16721701 )
1673- if (
1674- quant_config .get ("quant_method" ) == "gptq"
1675- and num_experts > 0
1676- ):
1702+ if quant_config .get ("quant_method" ) == "gptq" and num_experts > 0 :
16771703 quant_config ["quant_method" ] = "mixed_gptq"
16781704 self .logger .info (
16791705 "MoE model detected (num_experts=%d): "
@@ -1697,15 +1723,13 @@ def _patch_k_eq_v_for_vllm(self, model, quant_config: dict) -> None:
16971723 Gemma4 full-attention layers with attention_k_eq_v=True have no
16981724 v_proj weight — the model reuses key states as value states.
16991725 vLLM fuses q/k/v into a single qkv_proj and requires all shards
1700- to share the same quantization status.
1726+ to share the same quantization status.
17011727 """
17021728 text_cfg = getattr (model .config , "text_config" , None )
17031729 if text_cfg is None or not getattr (text_cfg , "attention_k_eq_v" , False ):
17041730 return
17051731 layer_types = getattr (text_cfg , "layer_types" , [])
1706- k_eq_v_indices = {
1707- i for i , lt in enumerate (layer_types ) if lt == "full_attention"
1708- }
1732+ k_eq_v_indices = {i for i , lt in enumerate (layer_types ) if lt == "full_attention" }
17091733 if not k_eq_v_indices :
17101734 return
17111735
@@ -1743,9 +1767,7 @@ def _patch_k_eq_v_for_vllm(self, model, quant_config: dict) -> None:
17431767 and "self_attn.k_proj" in layer_cfg
17441768 and "self_attn.v_proj" not in layer_cfg
17451769 ):
1746- layer_cfg ["self_attn.v_proj" ] = copy .deepcopy (
1747- layer_cfg ["self_attn.k_proj" ]
1748- )
1770+ layer_cfg ["self_attn.v_proj" ] = copy .deepcopy (layer_cfg ["self_attn.k_proj" ])
17491771
17501772 for key in ("modules_in_block_to_quantize" , "quantized_layer_names" ):
17511773 names = quant_config .get (key , [])
@@ -1816,6 +1838,7 @@ def save_quantized_model(self, save_directory: str, pack_weights: bool = True):
18161838 if src_dir and not os .path .isdir (src_dir ):
18171839 # when the model_id is specified, the path is modifed to the local directory
18181840 from huggingface_hub import snapshot_download
1841+
18191842 src_dir = snapshot_download (src_dir , local_files_only = True )
18201843 if src_dir and os .path .isdir (src_dir ):
18211844 for fname in ("processor_config.json" , "preprocessor_config.json" ):
0 commit comments