Nekochu commited on
Commit
a3ba5ed
·
1 Parent(s): 551acb3

use pre-quantized INT8 model (no FP8 casting, no LoRA merge at runtime)

Browse files
Files changed (1) hide show
  1. app.py +27 -49
app.py CHANGED
@@ -1,4 +1,4 @@
1
- """FE2E: Depth + Normal estimation from a single image (CPU)"""
2
  from __future__ import annotations
3
 
4
  import gc
@@ -35,64 +35,42 @@ def _download(repo, filename, token=None):
35
 
36
 
37
  def _load_generator():
38
- """Load FP8 model, cast to FP32, merge LoRA, quantize INT8."""
39
- from safetensors.torch import load_file
40
  from modules.model_edit import Step1XParams, Step1XEdit
41
  from modules.autoencoder import AutoEncoder
42
- from infer.inference import ImageGenerator, load_state_dict, equip_dit_with_lora_sd_scripts
43
- import numpy as np
44
 
45
  token = os.environ.get("HF_TOKEN")
46
- dit_path = _download("rkfg/Step1X-Edit-FP8", "step1x-edit-i1258-FP8.safetensors", token)
47
- vae_path = _download("exander/FE2E", "pretrain/vae.safetensors", token)
48
- lora_path = _download("exander/FE2E", "LDRN.safetensors", token)
49
 
50
  args = Args()
51
-
52
- print("[init] Building model on meta device...")
53
- with torch.device("meta"):
54
- ae = AutoEncoder(
55
- resolution=256, in_channels=3, ch=128, out_ch=3,
56
- ch_mult=[1, 2, 4, 4], num_res_blocks=2, z_channels=16,
57
- scale_factor=0.3611, shift_factor=0.1159,
58
- )
59
- step1x_params = Step1XParams(
60
- in_channels=64, out_channels=64, vec_in_dim=768, context_in_dim=4096,
61
- hidden_size=3072, mlp_ratio=4.0, num_heads=24, depth=19,
62
- depth_single_blocks=38, axes_dim=[16, 56, 56], theta=10_000, qkv_bias=True,
63
- )
64
- dit = Step1XEdit(step1x_params)
65
-
66
- # Load weights as FP8, then cast to FP32 layer by layer to avoid 46 GB peak
67
- print("[init] Loading FP8 weights and casting to FP32 (layer by layer)...")
68
  t0 = time.time()
69
- fp8_sd = load_file(dit_path, device="cpu")
70
- dit_sd = {}
71
- for k, v in fp8_sd.items():
72
- dit_sd[k] = v.float()
73
- del fp8_sd
74
- gc.collect()
75
 
76
- dit = dit.to(dtype=torch.float32)
77
- missing, unexpected = dit.load_state_dict(dit_sd, strict=False, assign=True)
78
- del dit_sd
79
- gc.collect()
80
- print(f"[init] DiT loaded in {time.time()-t0:.0f}s (missing={len(missing)}, unexpected={len(unexpected)})")
 
 
 
 
 
81
 
82
  # Load VAE
83
- ae = load_state_dict(ae, vae_path, "cpu")
84
- ae = ae.float()
 
 
 
 
 
85
 
86
- # Merge LoRA in FP32 (full precision merge)
87
- print("[init] Merging LoRA...")
88
- equip_dit_with_lora_sd_scripts(ae, [None], dit, lora_path, device="cpu")
89
-
90
- # Dynamic INT8 quantization
91
- print("[init] Applying dynamic INT8 quantization...")
92
- dit = torch.quantization.quantize_dynamic(dit, {torch.nn.Linear}, dtype=torch.qint8)
93
  gc.collect()
94
 
95
- # Build generator wrapper
96
  generator = ImageGenerator.__new__(ImageGenerator)
97
  generator.device = torch.device("cpu")
98
  generator.args = args
@@ -103,7 +81,7 @@ def _load_generator():
103
  generator.offload = False
104
  generator.lora_module = None
105
 
106
- print("[init] Model ready for inference")
107
  return generator
108
 
109
 
@@ -139,7 +117,7 @@ def generate(image):
139
  print(f"[gen] Input: {image.size}")
140
  t0 = time.time()
141
 
142
- with torch.inference_mode(), torch.no_grad():
143
  images, Lpred, Rpred = GENERATOR.generate_image(
144
  prompt="",
145
  negative_prompt="",
@@ -186,7 +164,7 @@ def main():
186
  with gr.Blocks(title="FE2E: Depth + Normal (CPU)") as demo:
187
  gr.Markdown(
188
  "**[FE2E](https://github.com/AMAP-ML/FE2E)** Depth + Normal from a single image (CVPR 2026). "
189
- "Single denoise step, Step1X-Edit DiT + LDRN LoRA, dynamic INT8 on CPU."
190
  )
191
  with gr.Row():
192
  with gr.Column():
 
1
+ """FE2E: Depth + Normal estimation from a single image (CPU, pre-quantized INT8)"""
2
  from __future__ import annotations
3
 
4
  import gc
 
35
 
36
 
37
  def _load_generator():
38
+ """Load pre-quantized INT8 DiT + FP32 VAE. No FP8 casting, no LoRA merge at runtime."""
 
39
  from modules.model_edit import Step1XParams, Step1XEdit
40
  from modules.autoencoder import AutoEncoder
41
+ from infer.inference import ImageGenerator
 
42
 
43
  token = os.environ.get("HF_TOKEN")
44
+ dit_path = _download("WeReCooking2/FE2E-INT8", "dit_int8.pt", token)
45
+ vae_path = _download("WeReCooking2/FE2E-INT8", "vae_fp32.pt", token)
 
46
 
47
  args = Args()
48
+ print("[init] Loading pre-quantized INT8 DiT...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  t0 = time.time()
 
 
 
 
 
 
50
 
51
+ # Build model structure, then quantize empty model, then load pre-quantized weights
52
+ step1x_params = Step1XParams(
53
+ in_channels=64, out_channels=64, vec_in_dim=768, context_in_dim=4096,
54
+ hidden_size=3072, mlp_ratio=4.0, num_heads=24, depth=19,
55
+ depth_single_blocks=38, axes_dim=[16, 56, 56], theta=10_000, qkv_bias=True,
56
+ )
57
+ dit = Step1XEdit(step1x_params).float()
58
+ dit = torch.quantization.quantize_dynamic(dit, {torch.nn.Linear}, dtype=torch.qint8)
59
+ dit.load_state_dict(torch.load(dit_path, map_location="cpu", weights_only=False))
60
+ print(f"[init] DiT loaded in {time.time()-t0:.0f}s")
61
 
62
  # Load VAE
63
+ print("[init] Loading VAE (FP32)...")
64
+ ae = AutoEncoder(
65
+ resolution=256, in_channels=3, ch=128, out_ch=3,
66
+ ch_mult=[1, 2, 4, 4], num_res_blocks=2, z_channels=16,
67
+ scale_factor=0.3611, shift_factor=0.1159,
68
+ ).float()
69
+ ae.load_state_dict(torch.load(vae_path, map_location="cpu", weights_only=False))
70
 
 
 
 
 
 
 
 
71
  gc.collect()
72
 
73
+ # Build generator wrapper (no LoRA needed, already merged)
74
  generator = ImageGenerator.__new__(ImageGenerator)
75
  generator.device = torch.device("cpu")
76
  generator.args = args
 
81
  generator.offload = False
82
  generator.lora_module = None
83
 
84
+ print(f"[init] Ready. Total load: {time.time()-t0:.0f}s")
85
  return generator
86
 
87
 
 
117
  print(f"[gen] Input: {image.size}")
118
  t0 = time.time()
119
 
120
+ with torch.inference_mode():
121
  images, Lpred, Rpred = GENERATOR.generate_image(
122
  prompt="",
123
  negative_prompt="",
 
164
  with gr.Blocks(title="FE2E: Depth + Normal (CPU)") as demo:
165
  gr.Markdown(
166
  "**[FE2E](https://github.com/AMAP-ML/FE2E)** Depth + Normal from a single image (CVPR 2026). "
167
+ "Single denoise step, Step1X-Edit DiT + LDRN LoRA (pre-merged), INT8 quantized on CPU."
168
  )
169
  with gr.Row():
170
  with gr.Column():