@@ -43,99 +43,43 @@ def __init__(self, pipeline: Any = None, model_args: Optional[Dict[str, Any]] =
4343 self .pipeline = pipeline
4444 self .tokenizer = getattr (pipeline , "tokenizer" , None ) if pipeline else None
4545
46- # Patch the pipeline's model generate method if it exists to handle generation_config properly
47- if pipeline and hasattr (pipeline , "model" ) and hasattr (pipeline .model , "generate" ):
48- self ._patch_generate_method (pipeline .model )
49-
50- # Also patch the pipeline's __call__ method to handle evaluation contexts
51- if pipeline :
46+ # Patch the pipeline's __call__ method to return logits for evaluation contexts
47+ if pipeline is not None :
5248 self ._patch_pipeline_call (pipeline )
5349
54- def _patch_generate_method (self , model : Any ) -> None :
55- """
56- Patch the model's generate method to extract parameters from generation_config.
57-
58- This handles the case where CausalLMGenerator expects max_new_tokens as a direct
59- parameter but the pipeline passes it inside generation_config.
60-
61- Parameters
62- ----------
63- model : Any
64- The model whose generate method to patch.
65- """
66- # Store the original generate method
67- original_generate = model .generate
68-
69- def patched_generate (* args , ** kwargs ):
70- # Extract generation_config if present
71- generation_config = kwargs .get ("generation_config" )
72-
73- if generation_config :
74- # Extract max_new_tokens from generation_config if not directly provided
75- if "max_new_tokens" not in kwargs and hasattr (generation_config , "max_new_tokens" ):
76- kwargs ["max_new_tokens" ] = generation_config .max_new_tokens
77-
78- # Extract other parameters that CausalLMGenerator might need
79- if "temperature" not in kwargs and hasattr (generation_config , "temperature" ):
80- kwargs ["temperature" ] = generation_config .temperature
81-
82- if "top_k" not in kwargs and hasattr (generation_config , "top_k" ):
83- kwargs ["top_k" ] = generation_config .top_k
84-
85- # Call the original generate method with the extracted parameters
86- return original_generate (* args , ** kwargs )
87-
88- # Replace the generate method with our patched version
89- model .generate = patched_generate
90-
9150 def _patch_pipeline_call (self , pipeline : Any ) -> None :
9251 """
9352 Patch the pipeline's __call__ method to return logits for evaluation contexts.
9453
95- When the input is tokenized tensors (evaluation context), we bypass the pipeline's
54+ When the input is tokenized tensors (evaluation context), bypass the pipeline's
9655 text generation and return raw logits needed for perplexity calculation.
9756
9857 Parameters
9958 ----------
10059 pipeline : Any
10160 The pipeline whose __call__ method to patch.
10261 """
103- # Store the original __call__ method
10462 original_call = pipeline .__call__
10563
10664 def patched_call (* args , ** kwargs ):
107- # Check if we're being called with string inputs (normal generation) or tensor inputs (evaluation context)
10865 inputs = args [0 ] if len (args ) > 0 else kwargs .get ("inputs" , kwargs .get ("text_inputs" ))
10966
110- # If input is tensor or we detect evaluation context, return logits
67+ # If input is tensor, return logits from the underlying model forward
11168 if hasattr (inputs , "shape" ) and hasattr (inputs , "dtype" ):
112- # This is a tensor input - likely from evaluation pipeline
113- # Use the model's forward pass to get logits instead of text generation
11469 try :
115- # Prepare inputs for the model
11670 if hasattr (pipeline , "model" ) and hasattr (inputs , "to" ):
11771 device = next (pipeline .model .parameters ()).device
11872 inputs = inputs .to (device )
119-
120- # Call the model's forward method directly to get logits
12173 with torch .no_grad ():
12274 outputs = pipeline .model (input_ids = inputs )
123-
124- # Return logits in the format expected by perplexity metric
125- if hasattr (outputs , "logits" ):
126- return outputs .logits
127- else :
128- return outputs
129-
75+ return outputs .logits if hasattr (outputs , "logits" ) else outputs
13076 except Exception as e :
13177 pruna_logger .warning (f"Failed to get logits from model forward pass: { e } " )
13278 # Fallback to original pipeline behavior
13379 pass
13480
135- # For string inputs or fallback, use the original pipeline behavior
13681 return original_call (* args , ** kwargs )
13782
138- # Replace the pipeline's __call__ method with our patched version
13983 pipeline .__call__ = patched_call
14084
14185 def prepare_inputs (
@@ -144,9 +88,9 @@ def prepare_inputs(
14488 """
14589 Prepare the inputs for the pipeline.
14690
147- For text generation pipelines, this normally converts tokenized tensors back to strings.
148- However, for evaluation contexts (like perplexity ), we keep tensors as-is since
149- the patched pipeline will handle them directly .
91+ For text generation pipelines, string inputs are passed through. For evaluation
92+ contexts (tensor token ids ), tensors are passed through so the patched pipeline
93+ can return logits for metrics like perplexity .
15094
15195 Parameters
15296 ----------
@@ -160,27 +104,13 @@ def prepare_inputs(
160104 """
161105 x , _ = batch
162106
163- # If x is already a string or list of strings, return as is
164- if isinstance (x , (str , list )) and all (isinstance (item , str ) for item in (x if isinstance (x , list ) else [x ])):
165- return x
166-
167- # If x is a tensor, we need to decide whether to convert to strings or keep as tensor
168- if isinstance (x , torch .Tensor ):
169- # For evaluation contexts, we keep tensors as-is so the patched __call__
170- # can return logits instead of generated text
171- # The patched __call__ method will detect tensor inputs and handle accordingly
172- return x
173-
174- # If no tokenizer available or x is not a tensor, return as-is
175107 return x
176108
177109 def process_output (self , output : Any ) -> Any :
178110 """
179111 Handle the output of the pipeline.
180112
181- With our patched pipeline, the output is either:
182- - Logits tensor (for evaluation with tensor inputs)
183- - Generated text (for normal generation with string inputs)
113+ Normalize common pipeline outputs. Returns generated text when available.
184114
185115 Parameters
186116 ----------
@@ -192,14 +122,20 @@ def process_output(self, output: Any) -> Any:
192122 Any
193123 The processed output - pass through since patched __call__ handles the logic.
194124 """
195- # The patched pipeline __call__ method already handles the tensor vs string logic
196- # So we just pass through the output as-is
125+ # HuggingFace text-generation pipeline returns list[dict]
126+ if (
127+ isinstance (output , list )
128+ and len (output ) > 0
129+ and isinstance (output [0 ], dict )
130+ and "generated_text" in output [0 ]
131+ ):
132+ return [o ["generated_text" ] for o in output ]
197133 return output
198134
199135 def log_model_info (self ) -> None :
200136 """Log information about the inference handler."""
201137 pruna_logger .info (
202138 "Detected transformers pipeline. Using PipelineHandler.\n "
203- "- Tensor inputs will be converted to strings for pipeline processing.\n "
204- "- Pipeline outputs will be processed to extract generated text."
139+ "- Token ids will be decoded to strings for pipeline processing.\n "
140+ "- Pipeline outputs will be normalized to generated text when applicable ."
205141 )
0 commit comments