Skip to content

Commit 6da9038

Browse files
committed
feat: remove fullgraph and get generate patch pipe back
1 parent 6ef48fc commit 6da9038

4 files changed

Lines changed: 39 additions & 90 deletions

File tree

src/pruna/algorithms/compilation/utils.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,17 @@ def generate(self, *args, **kwargs) -> torch.Tensor:
556556
self.top_k = kwargs.pop("top_k", self.top_k)
557557
self.use_cache = kwargs.pop("use_cache", self.use_cache)
558558

559+
# Handle generation when the user does not provide max_new_tokens, but a generation_config.
560+
# This also fixes the evaluation test.
561+
generation_config = kwargs.pop("generation_config", None)
562+
if (
563+
generation_config is not None
564+
and "max_new_tokens" not in kwargs
565+
and hasattr(generation_config, "max_new_tokens")
566+
and getattr(generation_config, "max_new_tokens") is not None
567+
):
568+
kwargs["max_new_tokens"] = int(generation_config.max_new_tokens)
569+
559570
# Log any kwargs that are not explicitly handled
560571
unhandled_kwargs = {
561572
k: v

src/pruna/engine/handler/handler_pipeline.py

Lines changed: 19 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -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
)

src/pruna/engine/handler/handler_utils.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,17 @@ def register_inference_handler(model: Any) -> InferenceHandler:
5050

5151
model_module = model._orig_mod.__module__ if hasattr(model, "_orig_mod") else model.__module__
5252

53-
# Check if it's a transformers pipeline
54-
if hasattr(model, "__class__") and "Pipeline" in model.__class__.__name__:
53+
# Prefer diffusers handler first to avoid routing diffusers pipelines to generic pipeline handler
54+
if "diffusers" in model_module:
55+
return DiffuserHandler(call_signature=inspect.signature(model.__call__))
56+
57+
# Transformers models and pipelines
58+
if "transformers" in model_module and "Pipeline" in type(model).__name__:
5559
# Specific check for text generation pipelines
56-
if "TextGeneration" in model.__class__.__name__:
60+
if "TextGeneration" in type(model).__name__:
5761
return PipelineHandler(pipeline=model)
58-
# For other pipelines, fallback to standard pipeline handler
62+
# For other transformers pipelines, use PipelineHandler
5963
return PipelineHandler(pipeline=model)
60-
elif "diffusers" in model_module:
61-
return DiffuserHandler(call_signature=inspect.signature(model.__call__))
6264
elif "transformers" in model_module:
6365
return TransformerHandler()
6466
else:

tests/algorithms/test_combinations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def prepare_smash_config(self, smash_config: SmashConfig, device: str) -> None:
4848
("sd_tiny_random", dict(quantizer="hqq_diffusers", compiler="torch_compile"), False, 'cmmd'),
4949
("flux_tiny_random", dict(quantizer="hqq_diffusers", compiler="torch_compile"), False, 'cmmd'),
5050
("sd_tiny_random", dict(quantizer="diffusers_int8", compiler="torch_compile"), False, 'cmmd'),
51-
("tiny_llama", dict(quantizer="gptq", compiler="torch_compile", torch_compile_fullgraph=False), True, 'perplexity'),
51+
("tiny_llama", dict(quantizer="gptq", compiler="torch_compile"), True, 'perplexity'),
5252
("llama_3_tiny_random_as_pipeline", dict(quantizer="llm_int8", compiler="torch_compile"), True, 'perplexity'),
5353
("flux_tiny_random", dict(cacher="pab", quantizer="hqq_diffusers"), False, 'cmmd'),
5454
("flux_tiny_random", dict(cacher="pab", quantizer="diffusers_int8"), False, 'cmmd'),

0 commit comments

Comments
 (0)