Update src/pipeline.py
Browse files- 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
|
| 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 |
-
|
| 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
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 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 |
-
"""
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|