moose commited on
Commit
73a98d6
·
1 Parent(s): 6c84590

Revert "Fix OOM by deferring model loading and AOT compilation to runtime"

Browse files

This reverts commit 49afc981b928f696a30599430f66fe00bca60375.

Files changed (2) hide show
  1. app.py +19 -56
  2. optimization.py +5 -21
app.py CHANGED
@@ -24,11 +24,6 @@ from PIL import Image
24
  import os
25
  import gradio as gr
26
 
27
- # --- Lazy Loading State ---
28
- # Pipeline is loaded lazily on first inference to avoid memory issues during build
29
- _pipeline_instance = None
30
- _pipeline_initialized = False
31
-
32
  def turn_into_video(input_images, output_images, prompt, progress=gr.Progress(track_tqdm=True)):
33
  if not input_images or not output_images:
34
  raise gr.Error("Please generate an output image first.")
@@ -93,60 +88,31 @@ def use_history_as_input(evt: gr.SelectData):
93
  return gr.update(value=[evt.value])
94
  return gr.update()
95
 
96
- # --- Model Loading (Lazy) ---
97
  dtype = torch.bfloat16
98
  device = "cuda" if torch.cuda.is_available() else "cpu"
99
 
100
- def get_pipeline():
101
- """
102
- Lazily initialize the pipeline on first use.
103
- This avoids loading ~40GB of models during build, which causes OOM.
104
- The pipeline is loaded and compiled on the first inference request
105
- when GPU resources are available via ZeroGPU.
106
- """
107
- global _pipeline_instance, _pipeline_initialized
108
-
109
- if _pipeline_initialized:
110
- return _pipeline_instance
111
-
112
- print("=== Initializing pipeline (first request) ===")
113
-
114
- # Load the pipeline with transformer
115
- pipe = QwenImageEditPlusPipeline.from_pretrained(
116
- "Qwen/Qwen-Image-Edit-2509",
117
- transformer=QwenImageTransformer2DModel.from_pretrained(
118
- "linoyts/Qwen-Image-Edit-Rapid-AIO",
119
- subfolder='transformer',
120
- torch_dtype=dtype,
121
- device_map='cuda'
122
- ),
123
- torch_dtype=dtype
124
- ).to(device)
125
-
126
- # Load and fuse LoRA weights
127
- pipe.load_lora_weights(
128
- "lovis93/next-scene-qwen-image-lora-2509",
129
- weight_name="next-scene_lora-v2-3000.safetensors",
130
- adapter_name="next-scene"
131
- )
132
- pipe.set_adapters(["next-scene"], adapter_weights=[1.])
133
- pipe.fuse_lora(adapter_names=["next-scene"], lora_scale=1.)
134
- pipe.unload_lora_weights()
135
 
136
- # Apply optimizations
137
- pipe.transformer.__class__ = QwenImageTransformer2DModel
138
- pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3())
139
-
140
- # AOT compilation (already in GPU context from infer())
141
- print("=== Starting AOT compilation ===")
142
- optimize_pipeline_(pipe, image=[Image.new("RGB", (1024, 1024)), Image.new("RGB", (1024, 1024))], prompt="prompt", already_in_gpu_context=True)
143
- print("=== AOT compilation complete ===")
144
 
145
- _pipeline_instance = pipe
146
- _pipeline_initialized = True
147
 
148
- return pipe
 
 
149
 
 
 
150
 
151
  # --- UI Constants and Helpers ---
152
  MAX_SEED = np.iinfo(np.int32).max
@@ -158,7 +124,7 @@ def use_output_as_input(output_images):
158
  return output_images
159
 
160
  # --- Main Inference Function (with hardcoded negative prompt) ---
161
- @spaces.GPU(duration=600) # Increased duration for first-request initialization
162
  def infer(
163
  images,
164
  prompt,
@@ -174,9 +140,6 @@ def infer(
174
  """
175
  Generates an image using the local Qwen-Image diffusers pipeline.
176
  """
177
- # Get or initialize the pipeline (lazy loading)
178
- pipe = get_pipeline()
179
-
180
  # Hardcode the negative prompt as requested
181
  negative_prompt = " "
182
 
 
24
  import os
25
  import gradio as gr
26
 
 
 
 
 
 
27
  def turn_into_video(input_images, output_images, prompt, progress=gr.Progress(track_tqdm=True)):
28
  if not input_images or not output_images:
29
  raise gr.Error("Please generate an output image first.")
 
88
  return gr.update(value=[evt.value])
89
  return gr.update()
90
 
91
+ # --- Model Loading ---
92
  dtype = torch.bfloat16
93
  device = "cuda" if torch.cuda.is_available() else "cpu"
94
 
95
+ pipe = QwenImageEditPlusPipeline.from_pretrained("Qwen/Qwen-Image-Edit-2509",
96
+ transformer= QwenImageTransformer2DModel.from_pretrained("linoyts/Qwen-Image-Edit-Rapid-AIO",
97
+ subfolder='transformer',
98
+ torch_dtype=dtype,
99
+ device_map='cuda'),torch_dtype=dtype).to(device)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
+ pipe.load_lora_weights(
102
+ "lovis93/next-scene-qwen-image-lora-2509",
103
+ weight_name="next-scene_lora-v2-3000.safetensors", adapter_name="next-scene"
104
+ )
105
+ pipe.set_adapters(["next-scene"], adapter_weights=[1.])
106
+ pipe.fuse_lora(adapter_names=["next-scene"], lora_scale=1.)
107
+ pipe.unload_lora_weights()
 
108
 
 
 
109
 
110
+ # Apply the same optimizations from the first version
111
+ pipe.transformer.__class__ = QwenImageTransformer2DModel
112
+ pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3())
113
 
114
+ # --- Ahead-of-time compilation ---
115
+ optimize_pipeline_(pipe, image=[Image.new("RGB", (1024, 1024)), Image.new("RGB", (1024, 1024))], prompt="prompt")
116
 
117
  # --- UI Constants and Helpers ---
118
  MAX_SEED = np.iinfo(np.int32).max
 
124
  return output_images
125
 
126
  # --- Main Inference Function (with hardcoded negative prompt) ---
127
+ @spaces.GPU(duration=300)
128
  def infer(
129
  images,
130
  prompt,
 
140
  """
141
  Generates an image using the local Qwen-Image diffusers pipeline.
142
  """
 
 
 
143
  # Hardcode the negative prompt as requested
144
  negative_prompt = " "
145
 
optimization.py CHANGED
@@ -45,17 +45,11 @@ INDUCTOR_CONFIGS = {
45
  }
46
 
47
 
48
- def optimize_pipeline_(pipeline: Callable[P, Any], *args: P.args, already_in_gpu_context: bool = False, **kwargs: P.kwargs):
49
- """
50
- Optimize the pipeline transformer with AOT compilation.
51
 
52
- Args:
53
- pipeline: The diffusion pipeline to optimize
54
- already_in_gpu_context: If True, skip the @spaces.GPU decorator since we're
55
- already running in a GPU context (e.g., called from within infer())
56
- """
57
 
58
- def _compile_transformer_impl():
59
  with spaces.aoti_capture(pipeline.transformer) as call:
60
  pipeline(*args, **kwargs)
61
 
@@ -63,7 +57,7 @@ def optimize_pipeline_(pipeline: Callable[P, Any], *args: P.args, already_in_gpu
63
  dynamic_shapes |= TRANSFORMER_DYNAMIC_SHAPES
64
 
65
  # quantize_(pipeline.transformer, Float8DynamicActivationFloat8WeightConfig())
66
-
67
  exported = torch.export.export(
68
  mod=pipeline.transformer,
69
  args=call.args,
@@ -73,14 +67,4 @@ def optimize_pipeline_(pipeline: Callable[P, Any], *args: P.args, already_in_gpu
73
 
74
  return spaces.aoti_compile(exported, INDUCTOR_CONFIGS)
75
 
76
- if already_in_gpu_context:
77
- # We're already in a GPU context, run directly
78
- compiled = _compile_transformer_impl()
79
- else:
80
- # Need to allocate GPU for compilation
81
- @spaces.GPU(duration=1500)
82
- def compile_transformer():
83
- return _compile_transformer_impl()
84
- compiled = compile_transformer()
85
-
86
- spaces.aoti_apply(compiled, pipeline.transformer)
 
45
  }
46
 
47
 
48
+ def optimize_pipeline_(pipeline: Callable[P, Any], *args: P.args, **kwargs: P.kwargs):
 
 
49
 
50
+ @spaces.GPU(duration=1500)
51
+ def compile_transformer():
 
 
 
52
 
 
53
  with spaces.aoti_capture(pipeline.transformer) as call:
54
  pipeline(*args, **kwargs)
55
 
 
57
  dynamic_shapes |= TRANSFORMER_DYNAMIC_SHAPES
58
 
59
  # quantize_(pipeline.transformer, Float8DynamicActivationFloat8WeightConfig())
60
+
61
  exported = torch.export.export(
62
  mod=pipeline.transformer,
63
  args=call.args,
 
67
 
68
  return spaces.aoti_compile(exported, INDUCTOR_CONFIGS)
69
 
70
+ spaces.aoti_apply(compile_transformer(), pipeline.transformer)