rootlocalghost commited on
Commit
00eb989
·
verified ·
1 Parent(s): 735ea49

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +74 -70
app.py CHANGED
@@ -18,15 +18,14 @@ def convert_and_upload(token, source_repo, target_repo, precision, target_compon
18
  yield "❌ Error: Please select at least one component to quantize."
19
  return
20
 
21
- # Map precision string to PyTorch dtype
22
- if precision == "FP8":
23
- target_dtype = torch.float8_e4m3fn
24
- elif precision == "FP16":
25
- target_dtype = torch.float16
26
- elif precision == "BF16":
27
- target_dtype = torch.bfloat16
28
- else:
29
- target_dtype = None
30
 
31
  api = HfApi(token=token)
32
  yield f"🔄 Connecting to Hugging Face and verifying target repo: {target_repo}..."
@@ -44,21 +43,21 @@ def convert_and_upload(token, source_repo, target_repo, precision, target_compon
44
  yield f"❌ Error fetching files: {str(e)}"
45
  return
46
 
47
- # Create a unique cache directory for this specific run to prevent collisions
48
  cache_dir = f"./hf_cache_{uuid.uuid4().hex[:8]}"
49
-
50
  success_count = 0
51
  error_count = 0
52
 
 
 
 
53
  for file in files:
54
- # AUTO-DELETE/SKIP LOGIC: Detect large .safetensors files at the root level
55
  is_root_safetensor = "/" not in file and file.endswith(".safetensors")
56
 
57
  if is_root_safetensor:
58
  yield f"🗑️ Auto-skipping massive root model: {file}..."
59
  try:
60
- api.delete_file(path_in_repo=file, repo_id=target_repo, token=token, commit_message=f"Auto-deleted massive root file {file}")
61
- yield f"✅ Ensured {file} is removed from target repository."
62
  except Exception:
63
  pass
64
  continue
@@ -68,7 +67,6 @@ def convert_and_upload(token, source_repo, target_repo, precision, target_compon
68
  try:
69
  os.makedirs(cache_dir, exist_ok=True)
70
 
71
- # CRITICAL FIX: Added token=token here so gated FLUX models don't block the download
72
  local_path = hf_hub_download(
73
  repo_id=source_repo,
74
  filename=file,
@@ -79,21 +77,46 @@ def convert_and_upload(token, source_repo, target_repo, precision, target_compon
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} (This will take a few minutes)..."
83
 
84
  tensors = load_file(local_path)
85
-
86
- if target_dtype:
87
- keys = list(tensors.keys())
88
- for k in keys:
89
- if tensors[k].is_floating_point():
90
- tensors[k] = tensors[k].to(target_dtype)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
  converted_path = "converted.safetensors"
93
- save_file(tensors, converted_path)
94
 
95
  # Aggressive memory flush
96
  del tensors
 
97
  gc.collect()
98
 
99
  yield f"☁️ Uploading {precision} version of {file}..."
@@ -117,92 +140,73 @@ def convert_and_upload(token, source_repo, target_repo, precision, target_compon
117
 
118
  success_count += 1
119
 
120
- # EXTREME DISK CLEANUP: Nuke the cache directory after every file to prevent the 50GB Space Crash
121
  if os.path.exists(cache_dir):
122
  shutil.rmtree(cache_dir)
123
-
124
  gc.collect()
125
 
126
  except Exception as e:
127
  error_count += 1
128
- yield f"⚠️ Error processing {file}: {str(e)}\nSkipping to next file..."
129
 
130
- # Final cleanup sweep
131
  if os.path.exists(cache_dir):
132
  shutil.rmtree(cache_dir)
133
 
134
  yield f"✅ Finished! Successfully processed {success_count} files. Errors encountered: {error_count}."
135
 
136
- # Dynamic UI Update for Target Repo Name
137
  def update_target_repo(username, source, precision):
138
  user_prefix = username.strip() if username.strip() else "your-username"
139
  model_name = source.split("/")[-1] if "/" in source else source
140
  return f"{user_prefix}/{model_name}-{precision}"
141
 
142
- # Build the Gradio UI
 
 
 
 
 
143
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
144
- gr.Markdown("# 🚀 FLUX.2-klein Dedicated Quantizer")
145
  gr.Markdown(
146
- "Convert sharded **FLUX.2-klein** models (4B and 9B) to lower precisions (FP8, FP16, BF16).\n\n"
147
- "**Auto-Delete & Disk Protection:** This tool actively purges Hugging Face's download cache after every single shard. "
148
- "This ensures the 9B model won't crash the free Space by filling up the 50GB hard drive limit."
149
  )
150
 
151
  with gr.Row():
152
  with gr.Column(scale=2):
153
- hf_token = gr.Textbox(
154
- label="Hugging Face Token (Write Access Required)",
155
- type="password",
156
- placeholder="hf_..."
157
- )
158
- hf_username = gr.Textbox(
159
- label="Your Hugging Face Username",
160
- placeholder="e.g., rootlocalghost"
161
- )
162
  source_repo = gr.Dropdown(
163
- choices=[
164
- "black-forest-labs/FLUX.2-klein-9B",
165
- "black-forest-labs/FLUX.2-klein-4B"
166
- ],
167
- value="black-forest-labs/FLUX.2-klein-9B",
168
- label="Source Repository",
169
- allow_custom_value=False
170
  )
171
 
172
  target_components = gr.CheckboxGroup(
173
  choices=["text_encoder", "transformer", "vae"],
174
- value=["text_encoder", "transformer"],
175
- label="Components to Quantize",
176
- info="Select which folders should be cast to the new precision. Unselected folders will be copied as-is."
177
  )
178
 
179
  precision = gr.Dropdown(
180
- choices=["FP8", "FP16", "BF16"],
181
- value="FP8",
182
  label="Target Precision"
183
  )
184
- target_repo = gr.Textbox(
185
- label="Target Repository (Auto-generated)",
186
- value="your-username/FLUX.2-klein-9B-FP8",
187
- interactive=True
188
- )
189
  start_btn = gr.Button("Start Quantization & Upload", variant="primary")
190
 
191
  with gr.Column(scale=3):
192
- output_log = gr.Textbox(
193
- label="Operation Logs",
194
- lines=20,
195
- interactive=False,
196
- max_lines=25
197
- )
198
 
 
199
  inputs_to_watch = [hf_username, source_repo, precision]
200
  for inp in inputs_to_watch:
201
- inp.change(
202
- fn=update_target_repo,
203
- inputs=inputs_to_watch,
204
- outputs=[target_repo]
205
- )
206
 
207
  start_btn.click(
208
  fn=convert_and_upload,
 
18
  yield "❌ Error: Please select at least one component to quantize."
19
  return
20
 
21
+ # Map precision string to PyTorch dtype or INT8 flag
22
+ target_dtype = None
23
+ is_int8 = False
24
+
25
+ if precision == "FP8": target_dtype = torch.float8_e4m3fn
26
+ elif precision == "FP16": target_dtype = torch.float16
27
+ elif precision == "BF16": target_dtype = torch.bfloat16
28
+ elif precision == "INT8": is_int8 = True
 
29
 
30
  api = HfApi(token=token)
31
  yield f"🔄 Connecting to Hugging Face and verifying target repo: {target_repo}..."
 
43
  yield f"❌ Error fetching files: {str(e)}"
44
  return
45
 
46
+ # Unique cache directory to prevent collisions
47
  cache_dir = f"./hf_cache_{uuid.uuid4().hex[:8]}"
 
48
  success_count = 0
49
  error_count = 0
50
 
51
+ # Exclusion list for INT8 (highly sensitive layers)
52
+ exclude_prefixes = ["norm", "ln_", "embed", "time_in", "vector_in", "guidance_in", "txt_in", "img_in"]
53
+
54
  for file in files:
 
55
  is_root_safetensor = "/" not in file and file.endswith(".safetensors")
56
 
57
  if is_root_safetensor:
58
  yield f"🗑️ Auto-skipping massive root model: {file}..."
59
  try:
60
+ api.delete_file(path_in_repo=file, repo_id=target_repo, token=token, commit_message=f"Auto-deleted {file}")
 
61
  except Exception:
62
  pass
63
  continue
 
67
  try:
68
  os.makedirs(cache_dir, exist_ok=True)
69
 
 
70
  local_path = hf_hub_download(
71
  repo_id=source_repo,
72
  filename=file,
 
77
  in_target_component = any(f"{comp}/" in file for comp in target_components)
78
 
79
  if file.endswith(".safetensors") and in_target_component:
80
+ yield f"🧠 Quantizing {file} to {precision}..."
81
 
82
  tensors = load_file(local_path)
83
+ new_tensors = {}
84
+
85
+ for k, v in tensors.items():
86
+ # --- BRANCH 1: INT8 Symmetric Quantization ---
87
+ if is_int8:
88
+ is_2d_weight = "weight" in k and len(v.shape) == 2
89
+ is_excluded = any(ex in k for ex in exclude_prefixes)
90
+
91
+ if is_2d_weight and not is_excluded:
92
+ # Upcast to BF16 for math
93
+ if v.dtype == torch.float8_e4m3fn:
94
+ v = v.to(torch.bfloat16)
95
+
96
+ scale = v.abs().max(dim=1, keepdim=True)[0] / 127.0
97
+ scale = scale.clamp(min=1e-8)
98
+ weight_int8 = torch.round(v / scale).clamp(-127, 127).to(torch.int8)
99
+
100
+ base_name = k.rsplit(".", 1)[0]
101
+ new_tensors[f"{base_name}.weight_int8"] = weight_int8
102
+ new_tensors[f"{base_name}.weight_scale"] = scale.to(torch.bfloat16)
103
+ else:
104
+ # Excluded layers / 1D tensors pass through as BF16
105
+ new_tensors[k] = v.to(torch.bfloat16) if v.is_floating_point() else v
106
+
107
+ # --- BRANCH 2: Standard Floating Point Casting ---
108
+ else:
109
+ if v.is_floating_point():
110
+ new_tensors[k] = v.to(target_dtype)
111
+ else:
112
+ new_tensors[k] = v
113
 
114
  converted_path = "converted.safetensors"
115
+ save_file(new_tensors, converted_path)
116
 
117
  # Aggressive memory flush
118
  del tensors
119
+ del new_tensors
120
  gc.collect()
121
 
122
  yield f"☁️ Uploading {precision} version of {file}..."
 
140
 
141
  success_count += 1
142
 
143
+ # Disk cleanup
144
  if os.path.exists(cache_dir):
145
  shutil.rmtree(cache_dir)
 
146
  gc.collect()
147
 
148
  except Exception as e:
149
  error_count += 1
150
+ yield f"⚠️ Error processing {file}: {str(e)}\nSkipping..."
151
 
 
152
  if os.path.exists(cache_dir):
153
  shutil.rmtree(cache_dir)
154
 
155
  yield f"✅ Finished! Successfully processed {success_count} files. Errors encountered: {error_count}."
156
 
157
+ # UI Updates
158
  def update_target_repo(username, source, precision):
159
  user_prefix = username.strip() if username.strip() else "your-username"
160
  model_name = source.split("/")[-1] if "/" in source else source
161
  return f"{user_prefix}/{model_name}-{precision}"
162
 
163
+ def update_warnings(precision):
164
+ if precision == "INT8":
165
+ return gr.update(value="⚠️ **INT8 Warning:** Modifies layer keys (`weight_int8`, `weight_scale`). Requires custom inference code to run.", visible=True)
166
+ else:
167
+ return gr.update(visible=False)
168
+
169
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
170
+ gr.Markdown("# 🚀 Universal FLUX.2-klein Quantizer")
171
  gr.Markdown(
172
+ "Convert sharded models to floating-point precisions (FP8/FP16/BF16) or dynamically trigger symmetric integer quantization (INT8)."
 
 
173
  )
174
 
175
  with gr.Row():
176
  with gr.Column(scale=2):
177
+ hf_token = gr.Textbox(label="Hugging Face Token (Write Access)", type="password", placeholder="hf_...")
178
+ hf_username = gr.Textbox(label="Hugging Face Username", placeholder="e.g., rootlocalghost")
 
 
 
 
 
 
 
179
  source_repo = gr.Dropdown(
180
+ choices=["black-forest-labs/FLUX.2-klein-9B", "black-forest-labs/FLUX.2-klein-4B"],
181
+ value="black-forest-labs/FLUX.2-klein-9B", label="Source Repository", allow_custom_value=True
 
 
 
 
 
182
  )
183
 
184
  target_components = gr.CheckboxGroup(
185
  choices=["text_encoder", "transformer", "vae"],
186
+ value=["transformer"],
187
+ label="Components to Quantize"
 
188
  )
189
 
190
  precision = gr.Dropdown(
191
+ choices=["FP8", "FP16", "BF16", "INT8"],
192
+ value="INT8",
193
  label="Target Precision"
194
  )
195
+
196
+ int8_warning = gr.Markdown(visible=True, value="⚠️ **INT8 Warning:** Modifies layer keys (`weight_int8`, `weight_scale`). Requires custom inference code to run.")
197
+
198
+ target_repo = gr.Textbox(label="Target Repository (Auto-generated)", value="your-username/FLUX.2-klein-9B-INT8", interactive=True)
 
199
  start_btn = gr.Button("Start Quantization & Upload", variant="primary")
200
 
201
  with gr.Column(scale=3):
202
+ output_log = gr.Textbox(label="Operation Logs", lines=20, interactive=False, max_lines=25)
 
 
 
 
 
203
 
204
+ # Dynamic UI wiring
205
  inputs_to_watch = [hf_username, source_repo, precision]
206
  for inp in inputs_to_watch:
207
+ inp.change(fn=update_target_repo, inputs=inputs_to_watch, outputs=[target_repo])
208
+
209
+ precision.change(fn=update_warnings, inputs=[precision], outputs=[int8_warning])
 
 
210
 
211
  start_btn.click(
212
  fn=convert_and_upload,