OrlandoHugBot commited on
Commit
bdba128
·
verified ·
1 Parent(s): 55db1f6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +77 -108
app.py CHANGED
@@ -1,11 +1,9 @@
1
  """
2
  UniPic-3 DMD Multi-Image Composition
3
- Hugging Face Space - ZeroGPU 优化版本 V2
4
 
5
- 关键修复:使用延迟加载 (Lazy Loading) 方案
6
- - 模型组件在全局 CPU 上加载
7
- - Pipeline 在 @spaces.GPU 函数内首次调用时才创建并移动到 GPU
8
- - 这确保了所有张量都在真实的 GPU 环境中初始化
9
  """
10
 
11
  import gradio as gr
@@ -34,32 +32,27 @@ except ImportError:
34
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
35
 
36
  # Model configuration
37
- MODEL_NAME = os.environ.get("MODEL_NAME", "Skywork/Unipic3-DMD")
38
- TRANSFORMER_PATH = os.environ.get("TRANSFORMER_PATH", "Skywork/Unipic3-DMD/ema_transformer")
39
 
40
  # ============================================================
41
- # 全局变量 - Pipeline 延迟初始化
42
  # ============================================================
 
 
43
 
44
- # 只在全局加载轻量级组件和 CPU 上的模型权重
45
- pipe = None # 延迟初始化
46
- _models_loaded = False
47
 
48
- # 存储 CPU 上的模型组件
49
- _cpu_components = {}
50
-
51
-
52
- def load_models_to_cpu():
53
  """
54
- CPU 上加载所有模型组件
55
- 这一步在全局执行,不需要 GPU
56
  """
57
- global _cpu_components, _models_loaded
58
 
59
- if _models_loaded:
60
- return
61
 
62
- print("🚀 Loading models to CPU...")
63
 
64
  try:
65
  from pipeline_qwenimage_edit import QwenImageEditPipeline
@@ -73,92 +66,92 @@ def load_models_to_cpu():
73
  )
74
  from transformers import AutoModel, AutoTokenizer, Qwen2VLProcessor
75
 
76
- dtype = torch.bfloat16
77
 
78
- # Load scheduler (CPU, 轻量级)
79
  print(" Loading scheduler...")
80
- _cpu_components['scheduler'] = FlowMatchEulerDiscreteScheduler.from_pretrained(
81
  MODEL_NAME, subfolder='scheduler'
82
  )
83
 
84
- # Load tokenizer & processor (CPU, 轻量级)
85
  print(" Loading tokenizer & processor...")
86
- _cpu_components['tokenizer'] = AutoTokenizer.from_pretrained(MODEL_NAME, subfolder='tokenizer')
87
- _cpu_components['processor'] = Qwen2VLProcessor.from_pretrained(MODEL_NAME, subfolder='processor')
88
 
89
- # Load text encoder to CPU
90
- print(" Loading text_encoder to CPU...")
91
- _cpu_components['text_encoder'] = AutoModel.from_pretrained(
92
  MODEL_NAME,
93
  subfolder='text_encoder',
94
  torch_dtype=dtype,
95
- ).eval()
96
 
97
- # Load transformer to CPU
98
- print(" Loading transformer to CPU...")
99
- def load_transformer():
100
- if os.path.exists(TRANSFORMER_PATH):
101
- if os.path.isdir(TRANSFORMER_PATH):
102
- config_path = os.path.join(TRANSFORMER_PATH, "config.json")
103
- if os.path.exists(config_path):
104
- return QwenImageTransformer2DModel.from_pretrained(
105
- TRANSFORMER_PATH,
106
- torch_dtype=dtype,
107
- use_safetensors=False
108
- ).eval()
109
- else:
110
- return QwenImageTransformer2DModel.from_pretrained(
111
- TRANSFORMER_PATH,
112
- subfolder='transformer',
113
- torch_dtype=dtype,
114
- use_safetensors=False
115
- ).eval()
116
- raise ValueError(f"Invalid transformer path: {TRANSFORMER_PATH}")
117
- else:
118
- path_parts = TRANSFORMER_PATH.split('/')
119
- if len(path_parts) >= 3:
120
- repo_id = '/'.join(path_parts[:2])
121
- subfolder = '/'.join(path_parts[2:])
122
- return QwenImageTransformer2DModel.from_pretrained(
123
- repo_id,
124
- subfolder=subfolder,
125
  torch_dtype=dtype,
126
  use_safetensors=False
127
- ).eval()
128
  else:
129
- return QwenImageTransformer2DModel.from_pretrained(
130
  TRANSFORMER_PATH,
131
  subfolder='transformer',
132
  torch_dtype=dtype,
133
  use_safetensors=False
134
- ).eval()
135
-
136
- _cpu_components['transformer'] = load_transformer()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
- # Load VAE to CPU
139
- print(" Loading VAE to CPU...")
140
- _cpu_components['vae'] = AutoencoderKLQwenImage.from_pretrained(
141
  MODEL_NAME,
142
  subfolder='vae',
143
  torch_dtype=dtype,
144
- ).eval()
145
 
146
- # 存储 Pipeline 类以便后续使用
147
- _cpu_components['pipeline_class'] = QwenImageEditPipeline
 
 
 
 
 
 
 
 
148
 
149
- _models_loaded = True
150
- print("✅ All models loaded to CPU!")
151
-
152
-
153
- # 立即在全局加载到 CPU
154
- load_models_to_cpu()
155
 
156
 
157
  # ============================================================
158
- # GPU 推理函数 - 在这里初始化 Pipeline
159
  # ============================================================
160
 
161
- @spaces.GPU(duration=120)
162
  def generate_image(
163
  images: list[Image.Image],
164
  prompt: str,
@@ -168,7 +161,7 @@ def generate_image(
168
  ) -> Image.Image:
169
  """
170
  GPU 推理函数
171
- 关键:Pipeline 在这里创建,确保在真实 GPU 环境中初始化
172
  """
173
  global pipe
174
 
@@ -176,33 +169,9 @@ def generate_image(
176
  print(f" Prompt: {prompt[:50]}...")
177
  print(f" Steps: {num_steps}, CFG: {true_cfg_scale}, Seed: {seed}")
178
 
179
- # 关键修复:在真实 GPU 环境中创建 Pipeline
180
  if pipe is None:
181
- print(" [INIT] Creating pipeline on real GPU...")
182
-
183
- # 方法:将 CPU 模型移动到 GPU,然后创建 pipeline
184
- device = 'cuda'
185
-
186
- # 移动模型到 GPU
187
- text_encoder = _cpu_components['text_encoder'].to(device)
188
- transformer = _cpu_components['transformer'].to(device)
189
- vae = _cpu_components['vae'].to(device)
190
-
191
- # 创建 Pipeline
192
- PipelineClass = _cpu_components['pipeline_class']
193
- pipe = PipelineClass(
194
- scheduler=_cpu_components['scheduler'],
195
- vae=vae,
196
- text_encoder=text_encoder,
197
- tokenizer=_cpu_components['tokenizer'],
198
- processor=_cpu_components['processor'],
199
- transformer=transformer
200
- )
201
-
202
- print(" [INIT] Pipeline created successfully!")
203
- else:
204
- # Pipeline 已存在,确保在正确的设备上
205
- pipe.to('cuda')
206
 
207
  # 验证设备
208
  print(f" [DEBUG] text_encoder device: {next(pipe.text_encoder.parameters()).device}")
@@ -527,7 +496,7 @@ def create_demo():
527
 
528
  status_text = gr.Textbox(
529
  label="Status",
530
- value="✨ Ready! Upload images and click Generate.",
531
  interactive=False,
532
  )
533
 
@@ -543,7 +512,7 @@ def create_demo():
543
  <ul style="color: #ffffff; font-size: 0.9rem; margin: 0; padding-left: 1.25rem;">
544
  <li>Reference images as "Image1", "Image2", etc. in your prompt</li>
545
  <li>Use descriptive prompts for better composition</li>
546
- <li>First run may take longer due to model warm-up</li>
547
  </ul>
548
  </div>
549
  """)
 
1
  """
2
  UniPic-3 DMD Multi-Image Composition
3
+ Hugging Face Space - ZeroGPU 优化版本 V3
4
 
5
+ 关键修复:完全在 @spaces.GPU 内部加载模型
6
+ 参考 Qwen 官方的 app.py 实现方式
 
 
7
  """
8
 
9
  import gradio as gr
 
32
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
33
 
34
  # Model configuration
35
+ MODEL_NAME = os.environ.get("MODEL_NAME", "/data_genie/genie/chris/Unipic3-DMD")
36
+ TRANSFORMER_PATH = os.environ.get("TRANSFORMER_PATH", "/data_genie/genie/chris/Unipic3-DMD/ema_transformer")
37
 
38
  # ============================================================
39
+ # 全局变量
40
  # ============================================================
41
+ pipe = None
42
+ dtype = torch.bfloat16
43
 
 
 
 
44
 
45
+ def load_pipeline():
 
 
 
 
46
  """
47
+ 加载完整的 Pipeline
48
+ 这个函数应该在 @spaces.GPU 装饰的函数内部调用
49
  """
50
+ global pipe
51
 
52
+ if pipe is not None:
53
+ return pipe
54
 
55
+ print("🚀 Loading pipeline...")
56
 
57
  try:
58
  from pipeline_qwenimage_edit import QwenImageEditPipeline
 
66
  )
67
  from transformers import AutoModel, AutoTokenizer, Qwen2VLProcessor
68
 
69
+ device = 'cuda'
70
 
71
+ # Load scheduler
72
  print(" Loading scheduler...")
73
+ scheduler = FlowMatchEulerDiscreteScheduler.from_pretrained(
74
  MODEL_NAME, subfolder='scheduler'
75
  )
76
 
77
+ # Load tokenizer & processor
78
  print(" Loading tokenizer & processor...")
79
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, subfolder='tokenizer')
80
+ processor = Qwen2VLProcessor.from_pretrained(MODEL_NAME, subfolder='processor')
81
 
82
+ # Load text encoder - 直接加载到 GPU
83
+ print(" Loading text_encoder...")
84
+ text_encoder = AutoModel.from_pretrained(
85
  MODEL_NAME,
86
  subfolder='text_encoder',
87
  torch_dtype=dtype,
88
+ ).to(device).eval()
89
 
90
+ # Load transformer - 直接加载到 GPU
91
+ print(" Loading transformer...")
92
+ if os.path.exists(TRANSFORMER_PATH):
93
+ if os.path.isdir(TRANSFORMER_PATH):
94
+ config_path = os.path.join(TRANSFORMER_PATH, "config.json")
95
+ if os.path.exists(config_path):
96
+ transformer = QwenImageTransformer2DModel.from_pretrained(
97
+ TRANSFORMER_PATH,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  torch_dtype=dtype,
99
  use_safetensors=False
100
+ ).to(device).eval()
101
  else:
102
+ transformer = QwenImageTransformer2DModel.from_pretrained(
103
  TRANSFORMER_PATH,
104
  subfolder='transformer',
105
  torch_dtype=dtype,
106
  use_safetensors=False
107
+ ).to(device).eval()
108
+ else:
109
+ path_parts = TRANSFORMER_PATH.split('/')
110
+ if len(path_parts) >= 3:
111
+ repo_id = '/'.join(path_parts[:2])
112
+ subfolder = '/'.join(path_parts[2:])
113
+ transformer = QwenImageTransformer2DModel.from_pretrained(
114
+ repo_id,
115
+ subfolder=subfolder,
116
+ torch_dtype=dtype,
117
+ use_safetensors=False
118
+ ).to(device).eval()
119
+ else:
120
+ transformer = QwenImageTransformer2DModel.from_pretrained(
121
+ TRANSFORMER_PATH,
122
+ subfolder='transformer',
123
+ torch_dtype=dtype,
124
+ use_safetensors=False
125
+ ).to(device).eval()
126
 
127
+ # Load VAE - 直接加载到 GPU
128
+ print(" Loading VAE...")
129
+ vae = AutoencoderKLQwenImage.from_pretrained(
130
  MODEL_NAME,
131
  subfolder='vae',
132
  torch_dtype=dtype,
133
+ ).to(device).eval()
134
 
135
+ # Create Pipeline
136
+ print(" Creating pipeline...")
137
+ pipe = QwenImageEditPipeline(
138
+ scheduler=scheduler,
139
+ vae=vae,
140
+ text_encoder=text_encoder,
141
+ tokenizer=tokenizer,
142
+ processor=processor,
143
+ transformer=transformer
144
+ )
145
 
146
+ print("✅ Pipeline loaded successfully!")
147
+ return pipe
 
 
 
 
148
 
149
 
150
  # ============================================================
151
+ # GPU 推理函数 - 模型在这里加载
152
  # ============================================================
153
 
154
+ @spaces.GPU(duration=180) # 增加时间以包含首次加载
155
  def generate_image(
156
  images: list[Image.Image],
157
  prompt: str,
 
161
  ) -> Image.Image:
162
  """
163
  GPU 推理函数
164
+ 关键:Pipeline 完全在这里加载,确保在真实 GPU 环境中初始化
165
  """
166
  global pipe
167
 
 
169
  print(f" Prompt: {prompt[:50]}...")
170
  print(f" Steps: {num_steps}, CFG: {true_cfg_scale}, Seed: {seed}")
171
 
172
+ # 在真实 GPU 环境中加载模型(首次调用时)
173
  if pipe is None:
174
+ load_pipeline()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
  # 验证设备
177
  print(f" [DEBUG] text_encoder device: {next(pipe.text_encoder.parameters()).device}")
 
496
 
497
  status_text = gr.Textbox(
498
  label="Status",
499
+ value="✨ Ready! Upload images and click Generate. First run will take longer to load the model.",
500
  interactive=False,
501
  )
502
 
 
512
  <ul style="color: #ffffff; font-size: 0.9rem; margin: 0; padding-left: 1.25rem;">
513
  <li>Reference images as "Image1", "Image2", etc. in your prompt</li>
514
  <li>Use descriptive prompts for better composition</li>
515
+ <li>First run will take ~60s to load the model</li>
516
  </ul>
517
  </div>
518
  """)