@@ -159,6 +159,47 @@ def extract_output_dequant_params(
159159 raise ValueError ("Could not find dequantize_per_tensor at the output of the graph" )
160160
161161
162+ def extract_all_output_dequant_params (
163+ module : torch .fx .GraphModule ,
164+ ) -> list [QuantArgs | None ]:
165+ """
166+ Extract per-output dequantization parameters from a multi-output model.
167+
168+ Returns a QuantArgs tuple for outputs ending in dequantize_per_tensor
169+ or None for outputs that aren't dequantized.
170+ """
171+ for node in module .graph .nodes :
172+ if node .op != "output" :
173+ continue
174+ output_args = node .args [0 ]
175+ if not isinstance (output_args , (tuple , list )):
176+ output_args = (output_args ,)
177+ params : list [QuantArgs | None ] = []
178+ for out in output_args :
179+ if isinstance (out , torch .fx .Node ) and "dequantize_per_tensor" in str (
180+ out .target
181+ ):
182+ args = out .args [1 :]
183+ if len (args ) >= 5 :
184+ dtype = args [4 ]
185+ assert isinstance (dtype , torch .dtype )
186+ params .append (
187+ (
188+ float (args [0 ]),
189+ int (args [1 ]),
190+ int (args [2 ]),
191+ int (args [3 ]),
192+ dtype ,
193+ )
194+ )
195+ else :
196+ params .append (None )
197+ else :
198+ params .append (None )
199+ return params
200+ raise ValueError ("No output node in graph" )
201+
202+
162203def extract_output_dequant_params_through_permute (
163204 module : torch .fx .GraphModule ,
164205) -> QuantArgs :
@@ -400,33 +441,55 @@ def sink_dequants(program: torch.export.ExportedProgram) -> None:
400441
401442class QuantizedOutputWrapper (torch .nn .Module ):
402443 """
403- Wrapper that quantizes a model's output so it produces uint8 tensors.
444+ Wrapper that quantizes a model's output(s) so they produce quantized tensors.
404445
405446 Mirrors QuantizedInputWrapper: the wrapper adds a quantize_per_tensor after
406- the model's output. When the graph is traced, the dequant (from the model) →
447+ each output. When the graph is traced, the dequant (from the model) →
407448 quant (from the wrapper) pair with matching parameters folds away, leaving
408449 the output in its quantized form.
409450
410451 Args:
411452 module: The module to wrap (may already be a QuantizedInputWrapper).
412- output_quant_args: (scale, zero_point, qmin, qmax, dtype) for the output.
453+ output_quant_args: Quantization parameters — either a single QuantArgs
454+ tuple or a list with one entry per output.
413455 """
414456
415457 def __init__ (
416458 self ,
417459 module : torch .nn .Module ,
418- output_quant_args : QuantArgs ,
460+ output_quant_args : Union [ QuantArgs , list [ QuantArgs | None ]] ,
419461 ) -> None :
420462 super ().__init__ ()
421463 self .module : torch .nn .Module = module
422- self .output_quant_args : QuantArgs = output_quant_args
464+ if isinstance (output_quant_args , list ):
465+ self ._multi_output : bool = True
466+ self ._per_output_args : list [QuantArgs | None ] = output_quant_args
467+ else :
468+ self ._multi_output = False
469+ self ._per_output_args = [output_quant_args ]
423470
424471 def forward (self , * args : torch .Tensor ) -> Any :
425472 result = self .module (* args )
426- scale , zp , qmin , qmax , dtype = self .output_quant_args
427- return torch .ops .quantized_decomposed .quantize_per_tensor .default (
428- result , scale , zp , qmin , qmax , dtype
429- )
473+ if not self ._multi_output :
474+ qa = self ._per_output_args [0 ]
475+ assert qa is not None
476+ scale , zp , qmin , qmax , dtype = qa
477+ return torch .ops .quantized_decomposed .quantize_per_tensor .default (
478+ result , scale , zp , qmin , qmax , dtype
479+ )
480+ out : list [torch .Tensor ] = []
481+ for i , r in enumerate (result ):
482+ qa = self ._per_output_args [i ] if i < len (self ._per_output_args ) else None
483+ if qa is None :
484+ out .append (r )
485+ continue
486+ scale , zp , qmin , qmax , dtype = qa
487+ out .append (
488+ torch .ops .quantized_decomposed .quantize_per_tensor .default (
489+ r , scale , zp , qmin , qmax , dtype
490+ )
491+ )
492+ return tuple (out )
430493
431494
432495def _get_transparent_ops () -> set [Any ]:
0 commit comments