rootlocalghost commited on
Commit
17e0b73
·
verified ·
1 Parent(s): c85e14f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +105 -80
app.py CHANGED
@@ -2,12 +2,11 @@ import os
2
  import gc
3
  import torch
4
  import shutil
 
5
  import gradio as gr
6
  from huggingface_hub import HfApi, hf_hub_download
7
  from safetensors.torch import load_file, save_file
8
 
9
- TEMP_DIR = "temp_processing_dir"
10
-
11
  def convert_and_upload(token, source_repo, target_repo, precision, target_components):
12
  if not token:
13
  yield "❌ Error: Please provide a valid Hugging Face Write Token."
@@ -19,15 +18,13 @@ def convert_and_upload(token, source_repo, target_repo, precision, target_compon
19
  yield "❌ Error: Please select at least one component to quantize."
20
  return
21
 
22
- # Map precision string to PyTorch dtype
23
- if precision == "FP8":
24
- target_dtype = torch.float8_e4m3fn
25
- elif precision == "FP16":
26
- target_dtype = torch.float16
27
- elif precision == "BF16":
28
- target_dtype = torch.bfloat16
29
- else:
30
- target_dtype = None
31
 
32
  api = HfApi(token=token)
33
  yield f"🔄 Connecting to Hugging Face and verifying target repo: {target_repo}..."
@@ -45,41 +42,81 @@ def convert_and_upload(token, source_repo, target_repo, precision, target_compon
45
  yield f"❌ Error fetching files: {str(e)}"
46
  return
47
 
48
- os.makedirs(TEMP_DIR, exist_ok=True)
 
 
 
 
 
 
 
 
 
49
 
50
  for file in files:
 
 
 
 
 
 
 
 
 
 
51
  yield f"⏳ Processing {file}..."
52
 
53
  try:
54
- # Download file locally, bypassing symlink cache to save disk space
 
55
  local_path = hf_hub_download(
56
  repo_id=source_repo,
57
  filename=file,
58
- local_dir=TEMP_DIR,
59
- local_dir_use_symlinks=False
60
  )
61
 
62
- # Check if this file belongs to one of the selected target components
63
  in_target_component = any(f"{comp}/" in file for comp in target_components)
64
 
65
- # Intercept and quantize only if it's a safetensors file in a selected folder
66
  if file.endswith(".safetensors") and in_target_component:
67
  yield f"🧠 Quantizing {file} to {precision}..."
68
 
69
  tensors = load_file(local_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
- # Cast floating point tensors to the selected precision
72
- if target_dtype:
73
- keys = list(tensors.keys())
74
- for k in keys:
75
- if tensors[k].is_floating_point():
76
- tensors[k] = tensors[k].to(target_dtype)
77
-
78
- converted_path = os.path.join(TEMP_DIR, "converted.safetensors")
79
- save_file(tensors, converted_path)
80
-
81
- # Wipe tensors from RAM to prevent OOM
82
  del tensors
 
83
  gc.collect()
84
 
85
  yield f"☁️ Uploading {precision} version of {file}..."
@@ -101,88 +138,76 @@ def convert_and_upload(token, source_repo, target_repo, precision, target_compon
101
  commit_message=f"Copy {file} from original repo"
102
  )
103
 
104
- # Cleanup original downloaded file
105
- if os.path.exists(local_path):
106
- os.remove(local_path)
107
-
108
  gc.collect()
109
 
110
  except Exception as e:
111
- yield f"⚠️ Error processing {file}: {str(e)}\nSkipping to next file..."
 
 
 
 
112
 
113
- if os.path.exists(TEMP_DIR):
114
- shutil.rmtree(TEMP_DIR)
115
 
116
- yield f"✅ All files processed and successfully uploaded to {target_repo}!"
117
 
118
- # Dynamic UI Update for Target Repo Name
119
  def update_target_repo(username, source, precision):
120
  user_prefix = username.strip() if username.strip() else "your-username"
121
- model_name = "Z-Image-Turbo" if "Turbo" in source else "Z-Image-Base"
122
  return f"{user_prefix}/{model_name}-{precision}"
123
 
124
- # Build the Gradio UI
 
 
 
 
 
125
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
126
- gr.Markdown("# 🚀 Z-Image Quantizer & Uploader")
127
  gr.Markdown(
128
- "Convert the **Z-Image** or **Z-Image-Turbo** models to lower precisions (FP8, FP16, BF16) and push them directly to your own Hugging Face account.\n\n"
129
- "**How it works:** This tool sequentially downloads, quantizes the selected files, and uploads everything. "
130
- "It is designed to run safely on free Spaces (16GB RAM) by processing files one at a time."
131
  )
132
 
133
  with gr.Row():
134
  with gr.Column(scale=2):
135
- hf_token = gr.Textbox(
136
- label="Hugging Face Token (Write Access Required)",
137
- type="password",
138
- placeholder="hf_..."
139
- )
140
- hf_username = gr.Textbox(
141
- label="Your Hugging Face Username",
142
- placeholder="e.g., rootlocalghost"
143
- )
144
  source_repo = gr.Dropdown(
145
- choices=["Tongyi-MAI/Z-Image", "Tongyi-MAI/Z-Image-Turbo"],
146
- value="Tongyi-MAI/Z-Image-Turbo",
147
- label="Source Repository"
 
148
  )
149
 
150
- # Added checkbox group for granular component control
151
  target_components = gr.CheckboxGroup(
152
- choices=["text_encoder", "transformer"],
153
- value=["text_encoder", "transformer"],
154
- label="Components to Quantize",
155
- info="Select which parts of the model to convert. Unselected parts will be copied as-is."
156
  )
157
 
158
  precision = gr.Dropdown(
159
- choices=["FP8", "FP16", "BF16"],
160
- value="FP8",
161
- label="Quantization Precision"
162
- )
163
- target_repo = gr.Textbox(
164
- label="Target Repository (Auto-generated)",
165
- value="your-username/Z-Image-Turbo-FP8",
166
- interactive=True
167
  )
 
 
 
 
168
  start_btn = gr.Button("Start Quantization & Upload", variant="primary")
169
 
170
  with gr.Column(scale=3):
171
- output_log = gr.Textbox(
172
- label="Operation Logs",
173
- lines=17,
174
- interactive=False,
175
- max_lines=20
176
- )
177
 
178
- # Automatically update the target repo name when inputs change
179
  inputs_to_watch = [hf_username, source_repo, precision]
180
  for inp in inputs_to_watch:
181
- inp.change(
182
- fn=update_target_repo,
183
- inputs=inputs_to_watch,
184
- outputs=[target_repo]
185
- )
186
 
187
  start_btn.click(
188
  fn=convert_and_upload,
 
2
  import gc
3
  import torch
4
  import shutil
5
+ import uuid
6
  import gradio as gr
7
  from huggingface_hub import HfApi, hf_hub_download
8
  from safetensors.torch import load_file, save_file
9
 
 
 
10
  def convert_and_upload(token, source_repo, target_repo, precision, target_components):
11
  if not token:
12
  yield "❌ Error: Please provide a valid Hugging Face Write Token."
 
18
  yield "❌ Error: Please select at least one component to quantize."
19
  return
20
 
21
+ target_dtype = None
22
+ is_int8 = False
23
+
24
+ if precision == "FP8": target_dtype = torch.float8_e4m3fn
25
+ elif precision == "FP16": target_dtype = torch.float16
26
+ elif precision == "BF16": target_dtype = torch.bfloat16
27
+ elif precision == "INT8": is_int8 = True
 
 
28
 
29
  api = HfApi(token=token)
30
  yield f"🔄 Connecting to Hugging Face and verifying target repo: {target_repo}..."
 
42
  yield f"❌ Error fetching files: {str(e)}"
43
  return
44
 
45
+ cache_dir = f"./hf_cache_{uuid.uuid4().hex[:8]}"
46
+ success_count = 0
47
+ error_count = 0
48
+
49
+ # Z-IMAGE SPECIFIC EXCLUSIONS
50
+ # Protects the DiT's embedders/final layers and the Text Encoder's sensitive norms from INT8 destruction
51
+ exclude_prefixes = [
52
+ "t_embedder", "cap_embedder", "all_x_embedder", "all_final_layer", "rope_embedder",
53
+ "embed_tokens", "norm", "ln_", "shared"
54
+ ]
55
 
56
  for file in files:
57
+ is_root_safetensor = "/" not in file and file.endswith(".safetensors")
58
+
59
+ if is_root_safetensor:
60
+ yield f"🗑️ Auto-skipping root model: {file}..."
61
+ try:
62
+ api.delete_file(path_in_repo=file, repo_id=target_repo, token=token, commit_message=f"Auto-deleted {file}")
63
+ except Exception:
64
+ pass
65
+ continue
66
+
67
  yield f"⏳ Processing {file}..."
68
 
69
  try:
70
+ os.makedirs(cache_dir, exist_ok=True)
71
+
72
  local_path = hf_hub_download(
73
  repo_id=source_repo,
74
  filename=file,
75
+ cache_dir=cache_dir,
76
+ token=token
77
  )
78
 
 
79
  in_target_component = any(f"{comp}/" in file for comp in target_components)
80
 
 
81
  if file.endswith(".safetensors") and in_target_component:
82
  yield f"🧠 Quantizing {file} to {precision}..."
83
 
84
  tensors = load_file(local_path)
85
+ new_tensors = {}
86
+
87
+ for k, v in tensors.items():
88
+ # --- BRANCH 1: INT8 Symmetric Quantization ---
89
+ if is_int8:
90
+ is_2d_weight = "weight" in k and len(v.shape) == 2
91
+ is_excluded = any(ex in k for ex in exclude_prefixes)
92
+
93
+ if is_2d_weight and not is_excluded:
94
+ # Upcast to BF16 for math
95
+ if v.dtype == torch.float8_e4m3fn:
96
+ v = v.to(torch.bfloat16)
97
+
98
+ scale = v.abs().max(dim=1, keepdim=True)[0] / 127.0
99
+ scale = scale.clamp(min=1e-8)
100
+ weight_int8 = torch.round(v / scale).clamp(-127, 127).to(torch.int8)
101
+
102
+ base_name = k.rsplit(".", 1)[0]
103
+ new_tensors[f"{base_name}.weight_int8"] = weight_int8
104
+ new_tensors[f"{base_name}.weight_scale"] = scale.to(torch.bfloat16)
105
+ else:
106
+ new_tensors[k] = v.to(torch.bfloat16) if v.is_floating_point() else v
107
+
108
+ # --- BRANCH 2: Standard Floating Point Casting ---
109
+ else:
110
+ if v.is_floating_point():
111
+ new_tensors[k] = v.to(target_dtype)
112
+ else:
113
+ new_tensors[k] = v
114
+
115
+ converted_path = "converted.safetensors"
116
+ save_file(new_tensors, converted_path)
117
 
 
 
 
 
 
 
 
 
 
 
 
118
  del tensors
119
+ del new_tensors
120
  gc.collect()
121
 
122
  yield f"☁️ Uploading {precision} version of {file}..."
 
138
  commit_message=f"Copy {file} from original repo"
139
  )
140
 
141
+ success_count += 1
142
+
143
+ if os.path.exists(cache_dir):
144
+ shutil.rmtree(cache_dir)
145
  gc.collect()
146
 
147
  except Exception as e:
148
+ error_count += 1
149
+ yield f"⚠️ Error processing {file}: {str(e)}\nSkipping..."
150
+
151
+ if os.path.exists(cache_dir):
152
+ shutil.rmtree(cache_dir)
153
 
154
+ yield f"✅ Finished! Successfully processed {success_count} files. Errors encountered: {error_count}."
 
155
 
 
156
 
 
157
  def update_target_repo(username, source, precision):
158
  user_prefix = username.strip() if username.strip() else "your-username"
159
+ model_name = source.split("/")[-1] if "/" in source else source
160
  return f"{user_prefix}/{model_name}-{precision}"
161
 
162
+ def update_warnings(precision):
163
+ if precision == "INT8":
164
+ return gr.update(value="⚠️ **INT8 Warning:** Modifies layer keys (`weight_int8`, `weight_scale`). Requires the custom `NativeInt8Linear` XPU inference code to run.", visible=True)
165
+ else:
166
+ return gr.update(visible=False)
167
+
168
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
169
+ gr.Markdown("# 🚀 Universal Z-Image Quantizer")
170
  gr.Markdown(
171
+ "Convert sharded Z-Image models directly on Hugging Face to floating-point precisions (FP8/FP16/BF16) or dynamically trigger symmetric integer quantization (INT8)."
 
 
172
  )
173
 
174
  with gr.Row():
175
  with gr.Column(scale=2):
176
+ hf_token = gr.Textbox(label="Hugging Face Token (Write Access)", type="password", placeholder="hf_...")
177
+ hf_username = gr.Textbox(label="Hugging Face Username", placeholder="e.g., rootlocalghost")
178
+
 
 
 
 
 
 
179
  source_repo = gr.Dropdown(
180
+ choices=["your-username/Z-Image-Turbo", "your-username/Z-Image-Base"],
181
+ value="your-username/Z-Image-Turbo",
182
+ label="Source Repository",
183
+ allow_custom_value=True
184
  )
185
 
 
186
  target_components = gr.CheckboxGroup(
187
+ choices=["text_encoder", "transformer", "vae"],
188
+ value=["transformer"],
189
+ label="Components to Quantize"
 
190
  )
191
 
192
  precision = gr.Dropdown(
193
+ choices=["FP8", "FP16", "BF16", "INT8"],
194
+ value="INT8",
195
+ label="Target Precision"
 
 
 
 
 
196
  )
197
+
198
+ int8_warning = gr.Markdown(visible=True, value="⚠️ **INT8 Warning:** Modifies layer keys (`weight_int8`, `weight_scale`). Requires the custom `NativeInt8Linear` XPU inference code to run.")
199
+
200
+ target_repo = gr.Textbox(label="Target Repository (Auto-generated)", value="your-username/Z-Image-Turbo-INT8", interactive=True)
201
  start_btn = gr.Button("Start Quantization & Upload", variant="primary")
202
 
203
  with gr.Column(scale=3):
204
+ output_log = gr.Textbox(label="Operation Logs", lines=20, interactive=False, max_lines=25)
 
 
 
 
 
205
 
 
206
  inputs_to_watch = [hf_username, source_repo, precision]
207
  for inp in inputs_to_watch:
208
+ inp.change(fn=update_target_repo, inputs=inputs_to_watch, outputs=[target_repo])
209
+
210
+ precision.change(fn=update_warnings, inputs=[precision], outputs=[int8_warning])
 
 
211
 
212
  start_btn.click(
213
  fn=convert_and_upload,