manbeast3b commited on
Commit
810e804
·
verified ·
1 Parent(s): 8f24c5c

Update src/pipeline.py

Browse files
Files changed (1) hide show
  1. src/pipeline.py +123 -162
src/pipeline.py CHANGED
@@ -159,177 +159,138 @@
159
  # are_two_tensors_similar_max_diff = TensorComparator.max_diff_comparison
160
  # empty_cache = MemoryManager.empty_cache
161
 
162
-
163
- from __future__ import annotations
164
  import os
 
165
  import torch
166
- import functools
167
- from enum import Enum, auto
168
- from contextlib import contextmanager
169
- from typing import Protocol, TypeVar, Generic, Callable, Any
170
- from dataclasses import dataclass, field
171
  from PIL.Image import Image
172
- from torch import Generator
173
  from diffusers import DiffusionPipeline, AutoencoderTiny, FluxTransformer2DModel
174
  from transformers import T5EncoderModel
175
  from huggingface_hub.constants import HF_HUB_CACHE
176
  from torchao.quantization import quantize_, int8_weight_only, float8_weight_only
177
  from first_block_cache.diffusers_adapters import apply_cache_on_pipe
178
  from pipelines.models import TextToImageRequest
 
179
 
180
- T = TypeVar('T')
181
-
182
- class ModelComponent(Protocol):
183
- def to(self, *args, **kwargs) -> Any: ...
184
-
185
- class ModelState(Enum):
186
- INITIALIZED = auto()
187
- LOADED = auto()
188
- OPTIMIZED = auto()
189
- READY = auto()
190
-
191
- class ResourceMonitor:
192
- """Monitors and manages system resources."""
193
-
194
- @contextmanager
195
- def monitor_memory(self, threshold_mb: int = 1000):
196
- initial_memory = torch.cuda.memory_allocated() / 1024**2
197
- yield
198
- final_memory = torch.cuda.memory_allocated() / 1024**2
199
- if final_memory - initial_memory > threshold_mb:
200
- torch.cuda.empty_cache()
201
-
202
  @dataclass
203
- class ModelRegistry(Generic[T]):
204
- """Type-safe registry for model components."""
205
- _components: dict[str, T] = field(default_factory=dict)
206
-
207
- def register(self, name: str, component: T) -> None:
208
- self._components[name] = component
209
-
210
- def get(self, name: str) -> T:
211
- return self._components[name]
212
-
213
- def __iter__(self):
214
- return iter(self._components.values())
215
-
216
- class PipelineBuilder:
217
- """Fluent builder for pipeline construction."""
218
-
219
- def __init__(self):
220
- self.config = {
221
- "model_id": "black-forest-labs/FLUX.1-schnell",
222
- "revision": "741f7c3ce8b383c54771c7003378a50191e9efe9",
223
- "device": "cuda",
224
- "dtype": torch.bfloat16
225
- }
226
- self.registry = ModelRegistry[ModelComponent]()
227
- self.state = ModelState.INITIALIZED
228
- self.monitor = ResourceMonitor()
229
-
230
- def with_torch_settings(self) -> PipelineBuilder:
231
- torch.backends.cuda.matmul.allow_tf32 = True
232
- torch.backends.cudnn.enabled = True
233
- torch.backends.cudnn.benchmark = True
234
- os.environ['PYTORCH_CUDA_ALLOC_CONF'] = "expandable_segments:True"
235
- return self
236
-
237
- def load_text_encoder(self) -> PipelineBuilder:
238
- with self.monitor.monitor_memory():
239
- encoder = T5EncoderModel.from_pretrained(
240
- "city96/t5-v1_1-xxl-encoder-bf16",
241
- revision="1b9c856aadb864af93c1dcdc226c2774fa67bc86",
242
- torch_dtype=self.config["dtype"]
243
- ).to(memory_format=torch.channels_last)
244
- self.registry.register("text_encoder", encoder)
245
- return self
246
-
247
- def load_vae(self) -> PipelineBuilder:
248
- with self.monitor.monitor_memory():
249
- vae = AutoencoderTiny.from_pretrained(
250
- "RobertML/FLUX.1-schnell-vae_e3m2",
251
- revision="da0d2cd7815792fb40d084dbd8ed32b63f153d8d",
252
- torch_dtype=self.config["dtype"]
253
- )
254
- self.registry.register("vae", vae)
255
- return self
256
-
257
- def load_transformer(self) -> PipelineBuilder:
258
- with self.monitor.monitor_memory():
259
- path = os.path.join(
260
- HF_HUB_CACHE,
261
- "models--RobertML--FLUX.1-schnell-int8wo/snapshots/307e0777d92df966a3c0f99f31a6ee8957a9857a"
262
- )
263
- transformer = FluxTransformer2DModel.from_pretrained(
264
- path,
265
- torch_dtype=self.config["dtype"],
266
- use_safetensors=False
267
- ).to(memory_format=torch.channels_last)
268
- self.registry.register("transformer", transformer)
269
- return self
270
-
271
- def optimize(self, pipeline: DiffusionPipeline) -> PipelineBuilder:
272
- with self.monitor.monitor_memory():
273
- pipeline.to(memory_format=torch.channels_last)
274
- pipeline.vae = torch.compile(pipeline.vae, mode="max-autotune")
275
- quantize_(pipeline.vae, int8_weight_only())
276
- quantize_(pipeline.vae, float8_weight_only())
277
- apply_cache_on_pipe(pipeline)
278
- return self
279
-
280
- def warmup(self, pipeline: DiffusionPipeline) -> PipelineBuilder:
281
- with torch.no_grad(), self.monitor.monitor_memory():
282
- for _ in range(3):
283
- pipeline(prompt=" ")
284
- return self
285
-
286
- def build(self) -> DiffusionPipeline:
287
- pipeline = DiffusionPipeline.from_pretrained(
288
- self.config["model_id"],
289
- vae=self.registry.get("vae"),
290
- revision=self.config["revision"],
291
- transformer=self.registry.get("transformer"),
292
- text_encoder_2=self.registry.get("text_encoder"),
293
- torch_dtype=self.config["dtype"],
294
- ).to(self.config["device"])
295
-
296
- self.optimize(pipeline)
297
- self.warmup(pipeline)
298
- self.state = ModelState.READY
299
- return pipeline
300
-
301
- class InferenceContext:
302
- """Context manager for inference operations."""
303
-
304
- def __init__(self, pipeline: DiffusionPipeline):
305
- self.pipeline = pipeline
306
- self.monitor = ResourceMonitor()
307
-
308
- @contextmanager
309
- def inference_mode(self):
310
- with torch.no_grad(), self.monitor.monitor_memory():
311
- yield self.pipeline
312
-
313
- def load() -> DiffusionPipeline:
314
- """Build and configure the pipeline using the fluent builder pattern."""
315
- return (PipelineBuilder()
316
- .with_torch_settings()
317
- .load_text_encoder()
318
- .load_vae()
319
- .load_transformer()
320
- .build())
321
-
322
  def infer(request: TextToImageRequest, pipeline: DiffusionPipeline, generator: Generator) -> Image:
323
- """Generate image using the pipeline within a managed context."""
324
- context = InferenceContext(pipeline)
325
- with context.inference_mode() as p:
326
- return p(
327
- prompt=request.prompt,
328
- generator=generator,
329
- guidance_scale=0.0,
330
- num_inference_steps=4,
331
- max_sequence_length=256,
332
- height=request.height,
333
- width=request.width,
334
- output_type="pil"
335
- ).images[0]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  # are_two_tensors_similar_max_diff = TensorComparator.max_diff_comparison
160
  # empty_cache = MemoryManager.empty_cache
161
 
 
 
162
  import os
163
+ import gc
164
  import torch
 
 
 
 
 
165
  from PIL.Image import Image
166
+ from dataclasses import dataclass
167
  from diffusers import DiffusionPipeline, AutoencoderTiny, FluxTransformer2DModel
168
  from transformers import T5EncoderModel
169
  from huggingface_hub.constants import HF_HUB_CACHE
170
  from torchao.quantization import quantize_, int8_weight_only, float8_weight_only
171
  from first_block_cache.diffusers_adapters import apply_cache_on_pipe
172
  from pipelines.models import TextToImageRequest
173
+ from torch import Generator
174
 
175
+ # Configuration settings using a dataclass for clarity
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  @dataclass
177
+ class Config:
178
+ CKPT_ID: str = "black-forest-labs/FLUX.1-schnell"
179
+ CKPT_REVISION: str = "741f7c3ce8b383c54771c7003378a50191e9efe9"
180
+ DEVICE: str = "cuda"
181
+ DTYPE = torch.bfloat16
182
+ PYTORCH_CUDA_ALLOC_CONF: str = "expandable_segments:True"
183
+
184
+ def _initialize_environment():
185
+ """Set up PyTorch and CUDA environment variables for optimal performance."""
186
+ torch.backends.cuda.matmul.allow_tf32 = True
187
+ torch.backends.cudnn.enabled = True
188
+ torch.backends.cudnn.benchmark = True
189
+ os.environ['PYTORCH_CUDA_ALLOC_CONF'] = Config.PYTORCH_CUDA_ALLOC_CONF
190
+
191
+ def _clear_gpu_memory():
192
+ """Free up GPU memory to prevent memory-related issues."""
193
+ gc.collect()
194
+ torch.cuda.empty_cache()
195
+ torch.cuda.reset_max_memory_allocated()
196
+ torch.cuda.reset_peak_memory_stats()
197
+
198
+ def _load_text_encoder_model():
199
+ """Load the text encoder model with specified configuration."""
200
+ return T5EncoderModel.from_pretrained(
201
+ "city96/t5-v1_1-xxl-encoder-bf16",
202
+ revision="1b9c856aadb864af93c1dcdc226c2774fa67bc86",
203
+ torch_dtype=Config.DTYPE
204
+ ).to(memory_format=torch.channels_last)
205
+
206
+ def _load_vae_model():
207
+ """Load the variational autoencoder (VAE) model with specified configuration."""
208
+ return AutoencoderTiny.from_pretrained(
209
+ "RobertML/FLUX.1-schnell-vae_e3m2",
210
+ revision="da0d2cd7815792fb40d084dbd8ed32b63f153d8d",
211
+ torch_dtype=Config.DTYPE
212
+ )
213
+
214
+ def _load_transformer_model():
215
+ """Load the transformer model from a specific cached path."""
216
+ transformer_path = os.path.join(
217
+ HF_HUB_CACHE,
218
+ "models--RobertML--FLUX.1-schnell-int8wo/snapshots/307e0777d92df966a3c0f99f31a6ee8957a9857a"
219
+ )
220
+ return FluxTransformer2DModel.from_pretrained(
221
+ transformer_path,
222
+ torch_dtype=Config.DTYPE,
223
+ use_safetensors=False
224
+ ).to(memory_format=torch.channels_last)
225
+
226
+ def _warmup_pipeline(pipeline):
227
+ """Warm up the pipeline by running it with an empty prompt to initialize internal caches."""
228
+ for _ in range(3):
229
+ pipeline(prompt=" ")
230
+
231
+ def load_pipeline():
232
+ """
233
+ Load and configure the diffusion pipeline for text-to-image generation.
234
+
235
+ Returns:
236
+ DiffusionPipeline: The configured pipeline ready for inference.
237
+ """
238
+ _clear_gpu_memory()
239
+
240
+ # Load individual components
241
+ text_encoder = _load_text_encoder_model()
242
+ vae = _load_vae_model()
243
+ transformer = _load_transformer_model()
244
+
245
+ # Assemble the diffusion pipeline
246
+ pipeline = DiffusionPipeline.from_pretrained(
247
+ Config.CKPT_ID,
248
+ vae=vae,
249
+ revision=Config.CKPT_REVISION,
250
+ transformer=transformer,
251
+ text_encoder_2=text_encoder,
252
+ torch_dtype=Config.DTYPE,
253
+ ).to(Config.DEVICE)
254
+
255
+ # Apply optimizations
256
+ apply_cache_on_pipe(pipeline)
257
+ pipeline.to(memory_format=torch.channels_last)
258
+ pipeline.vae = torch.compile(pipeline.vae, mode="max-autotune")
259
+ quantize_(pipeline.vae, int8_weight_only())
260
+ quantize_(pipeline.vae, float8_weight_only())
261
+
262
+ # Warm up the pipeline to ensure readiness
263
+ _warmup_pipeline(pipeline)
264
+
265
+ return pipeline
266
+
267
+ @torch.no_grad()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  def infer(request: TextToImageRequest, pipeline: DiffusionPipeline, generator: Generator) -> Image:
269
+ """
270
+ Generate an image from a text prompt using the diffusion pipeline.
271
+
272
+ Args:
273
+ request (TextToImageRequest): The request containing the prompt and image parameters.
274
+ pipeline (DiffusionPipeline): The pre-loaded diffusion pipeline.
275
+ generator (Generator): The random seed generator for reproducibility.
276
+
277
+ Returns:
278
+ Image: The generated image in PIL format.
279
+ """
280
+ image = pipeline(
281
+ prompt=request.prompt,
282
+ generator=generator,
283
+ guidance_scale=0.0,
284
+ num_inference_steps=4,
285
+ max_sequence_length=256,
286
+ height=request.height,
287
+ width=request.width,
288
+ output_type="pil"
289
+ ).images[0]
290
+ return image
291
+
292
+ # Initialize environment settings when the module is imported
293
+ _initialize_environment()
294
+
295
+ # For compatibility with other scripts, alias load_pipeline as load
296
+ load = load_pipeline