2727)
2828from coreai_opt ._utils .spec_utils import PartialConstructor as _PartialConstructor
2929from coreai_opt ._utils .torch_utils import (
30- move_model_to_eval as _move_model_to_eval ,
3130 remove_compression_parametrizations as _remove_compression_parametrizations ,
3231)
3332from coreai_opt .common import ExportBackend
@@ -100,9 +99,6 @@ def _calculate_centroids_for_module(
10099 except Exception as e :
101100 raise RuntimeError (f"Centroid calculation failed for layer { layer_name !r} " ) from e
102101
103- if fp_module ._disabled :
104- fp_module ._disabled_reason = f"layer { layer_name !r} "
105-
106102 return fp_module
107103
108104
@@ -141,9 +137,6 @@ def __init__(self, model: torch.nn.Module, config: KMeansPalettizerConfig | None
141137 optimization_type_name = "palettize" ,
142138 )
143139
144- # Store example inputs for sensitivity-based centroid recomputation
145- self ._example_inputs = None
146-
147140 self ._num_workers = 1
148141
149142 @classmethod
@@ -198,9 +191,6 @@ def prepare(
198191 logger .info ("Preparing model for palettization" )
199192 prepared_model = self ._handler .prepare (self ._model , example_inputs = example_inputs )
200193
201- # Save example inputs for later use in calibration
202- self ._example_inputs = tuple ([ip .detach ().clone () for ip in example_inputs ])
203-
204194 # Load precomputed sensitivities if provided
205195 if sensitivity_path is not None :
206196 logger .info (
@@ -216,7 +206,7 @@ def prepare(
216206 if self ._num_workers > 1 :
217207 self ._calculate_centroids_parallel (num_workers )
218208 else :
219- self ._calculate_centroids_sequential (example_inputs )
209+ self ._calculate_centroids_sequential ()
220210
221211 # Remove FakePalettize modules that were disabled during the forward
222212 # pass due to incompatible granularity or cluster dimensions.
@@ -335,7 +325,7 @@ def step(self, output: torch.Tensor, target: torch.Tensor):
335325 if self ._num_workers > 1 :
336326 self ._calculate_centroids_parallel (self ._num_workers )
337327 else :
338- self ._calculate_centroids_sequential (self . _example_inputs )
328+ self ._calculate_centroids_sequential ()
339329
340330 # Restore normal operation
341331 self ._model .apply (_enable_fake_palett )
@@ -438,49 +428,24 @@ def _spec_to_partial(
438428 args .update (module_config ._get_compressor_specific_settings ())
439429 return _KMeansFakePalettize .with_args (** args )
440430
441- def _calculate_centroids_sequential (self , example_inputs : tuple [torch .Tensor ]) -> None :
442- """Run a forward pass to calculate centroids, with a per-layer progress bar."""
443- fp_modules : list [_KMeansFakePalettize ] = []
444- for _ , module in self ._model .named_modules (remove_duplicate = True ):
445- if not P .is_parametrized (module ):
446- continue
447- for parametrizations in module .parametrizations .values ():
448- for p in parametrizations :
449- if isinstance (p , _KMeansFakePalettize ):
450- fp_modules .append (p )
451- break
452-
453- progress = tqdm (total = len (fp_modules ), desc = "Palettizing layers (num_workers=1)" )
454- seen : set [int ] = set ()
431+ def _collect_fake_palett_info (self , * , to_cpu : bool ) -> list [_FakePalettInfo ]:
432+ """Collect one ``_FakePalettInfo`` per ``_KMeansFakePalettize`` parametrization.
455433
456- def _tick (module , _inputs , _output ):
457- if id (module ) not in seen :
458- seen .add (id (module ))
459- progress .update (1 )
460-
461- handles = [m .register_forward_hook (_tick ) for m in fp_modules ]
462- try :
463- with _move_model_to_eval (self ._model ):
464- with torch .no_grad ():
465- self ._model (* example_inputs )
466- finally :
467- for h in handles :
468- h .remove ()
469- progress .close ()
470-
471- def _calculate_centroids_parallel (self , num_workers : int ) -> None :
472- """Compute centroids for all _KMeansFakePalettize modules in parallel."""
473- # Track parametrization slot (module, attr_name, idx) so the worker's
474- # mutated module can be swapped back in. Whole-module swap means every
475- # buffer and plain attribute round-trips automatically.
434+ Records the parametrization slot (module, attr_name, idx) so a
435+ (worker-mutated) module can be swapped back in, plus the layer's dense
436+ weight. ``to_cpu`` moves each weight to CPU, required when the weight is
437+ shipped to a spawned worker process.
438+ """
476439 fp_info : list [_FakePalettInfo ] = []
477440 for module_name , module in self ._model .named_modules (remove_duplicate = True ):
478441 if not P .is_parametrized (module ):
479442 continue
480443 for attr_name , parametrizations in module .parametrizations .items ():
481444 for idx , p in enumerate (parametrizations ):
482445 if isinstance (p , _KMeansFakePalettize ):
483- weight = parametrizations .original .detach ().cpu ()
446+ weight = parametrizations .original .detach ()
447+ if to_cpu :
448+ weight = weight .cpu ()
484449 fp_info .append (
485450 _FakePalettInfo (
486451 module = module ,
@@ -492,7 +457,30 @@ def _calculate_centroids_parallel(self, num_workers: int) -> None:
492457 )
493458 )
494459 break
460+ return fp_info
495461
462+ def _calculate_centroids_sequential (self ) -> None :
463+ """Compute centroids for every ``_KMeansFakePalettize`` module in-process.
464+
465+ Mirrors the parallel path but runs in the current process (no worker
466+ pool): each layer's centroids are computed by invoking
467+ ``fp_module(weight)`` directly. Palettization centroids depend only on
468+ the layer weight, so this needs no model forward — and therefore no
469+ example inputs.
470+ """
471+ fp_info = self ._collect_fake_palett_info (to_cpu = False )
472+ if not fp_info :
473+ return
474+
475+ results = [
476+ _calculate_centroids_for_module ((info .fp_module , info .weight , info .layer_name ))
477+ for info in tqdm (fp_info , desc = "Palettizing layers (num_workers=1)" )
478+ ]
479+ self ._apply_centroid_results (fp_info , results )
480+
481+ def _calculate_centroids_parallel (self , num_workers : int ) -> None :
482+ """Compute centroids for all _KMeansFakePalettize modules in parallel."""
483+ fp_info = self ._collect_fake_palett_info (to_cpu = True )
496484 if not fp_info :
497485 return
498486
@@ -516,15 +504,24 @@ def _calculate_centroids_parallel(self, num_workers: int) -> None:
516504 )
517505 )
518506
507+ self ._apply_centroid_results (fp_info , results )
508+
509+ def _apply_centroid_results (
510+ self ,
511+ fp_info : list [_FakePalettInfo ],
512+ results : list [_KMeansFakePalettize ],
513+ ) -> None :
514+ """Swap each computed ``_KMeansFakePalettize`` back into its slot.
515+
516+ ``ParametrizationList`` supports item assignment, so this swaps the
517+ module into the live model without touching the surrounding
518+ parametrization registration. In the parallel path ``results`` holds the
519+ workers' returned copies; in-process they are the same live objects
520+ (mutated in place), so the assignment is a harmless no-op there.
521+ """
519522 for info , new_fp in zip (fp_info , results , strict = True ):
520- if getattr (new_fp , "_disabled" , False ):
521- logger .warning (
522- f"Disabling palettization for a module: "
523- f"{ getattr (new_fp , '_disabled_reason' , '' )} "
524- )
525- # ParametrizationList supports item assignment; this swaps the
526- # worker's mutated module into the live model without touching
527- # the surrounding parametrization registration.
523+ if new_fp .is_disabled ():
524+ logger .warning ("Disabling palettization for layer %r" , info .layer_name )
528525 info .module .parametrizations [info .attr_name ][info .idx ] = new_fp
529526
530527 @staticmethod
0 commit comments