import json import torch from safetensors.torch import load_file, save_file from pathlib import Path import gc import gguf from dequant import dequantize_tensor # https://github.com/city96/ComfyUI-GGUF import os import argparse import gradio as gr # also requires aria, gdown, peft, huggingface_hub, safetensors, transformers, accelerate, pytorch_lightning import subprocess subprocess.run('pip cache purge', shell=True) import spaces @spaces.GPU() def spaces_dummy(): pass flux_dev_repo = "ChuckMcSneed/FLUX.1-dev" flux_schnell_repo = "black-forest-labs/FLUX.1-schnell" system_temp_dir = "temp" device = "cuda" if torch.cuda.is_available() else "cpu" torch.set_grad_enabled(False) GGUF_QTYPE = [gguf.GGMLQuantizationType.Q8_0, gguf.GGMLQuantizationType.Q5_1, gguf.GGMLQuantizationType.Q5_0, gguf.GGMLQuantizationType.Q4_1, gguf.GGMLQuantizationType.Q4_0, gguf.GGMLQuantizationType.F32, gguf.GGMLQuantizationType.F16] TORCH_DTYPE = [torch.float32, torch.float, torch.float64, torch.double, torch.float16, torch.half, torch.bfloat16, torch.complex32, torch.chalf, torch.complex64, torch.cfloat, torch.complex128, torch.cdouble, torch.uint8, torch.uint16, torch.uint32, torch.uint64, torch.int8, torch.int16, torch.short, torch.int32, torch.int, torch.int64, torch.long, torch.bool, torch.float8_e4m3fn, torch.float8_e5m2] TORCH_QUANTIZED_DTYPE = [torch.quint8, torch.qint8, torch.qint32, torch.quint4x2] def list_sub(a, b): return [e for e in a if e not in b] def is_repo_name(s): import re return re.fullmatch(r'^[^/,\s\"\']+/[^/,\s\"\']+$', s) def clear_cache(): torch.cuda.empty_cache() gc.collect() def clear_sd(sd: dict): for k in list(sd.keys()): sd.pop(k) del sd torch.cuda.empty_cache() gc.collect() def clone_sd(sd: dict): from copy import deepcopy print("Cloning state dict.") for k in list(sd.keys()): sd[k] = deepcopy(sd.pop(k)) #sd[k] = sd.pop(k).detach().clone().to(device="cpu") torch.cuda.empty_cache() gc.collect() def print_resource_usage(): import psutil cpu_usage = psutil.cpu_percent() ram_usage = psutil.virtual_memory().used / psutil.virtual_memory().total * 100 print(f"CPU usage: {cpu_usage}% / RAM usage: {ram_usage}%") def download_thing(directory, url, civitai_api_key="", progress=gr.Progress(track_tqdm=True)): progress(0, desc="Start downloading...") url = url.strip() if "drive.google.com" in url: original_dir = os.getcwd() os.chdir(directory) os.system(f"gdown --fuzzy {url}") os.chdir(original_dir) elif "huggingface.co" in url: url = url.replace("?download=true", "") if "/blob/" in url: url = url.replace("/blob/", "/resolve/") os.system(f"aria2c --console-log-level=error --summary-interval=10 -c -x 16 -k 1M -s 16 {url} -d {directory} -o {url.split('/')[-1]}") else: os.system (f"aria2c --optimize-concurrent-downloads --console-log-level=error --summary-interval=10 -c -x 16 -k 1M -s 16 {url} -d {directory} -o {url.split('/')[-1]}") elif "civitai.com" in url: if "?" in url: url = url.split("?")[0] if civitai_api_key: url = url + f"?token={civitai_api_key}" os.system(f"aria2c --console-log-level=error --summary-interval=10 -c -x 16 -k 1M -s 16 -d {directory} {url}") else: print("You need an API key to download Civitai models.") else: os.system(f"aria2c --console-log-level=error --summary-interval=10 -c -x 16 -k 1M -s 16 -d {directory} {url}") def get_local_model_list(dir_path): model_list = [] valid_extensions = ('.safetensors') for file in Path(dir_path).glob("*"): if file.suffix in valid_extensions: file_path = str(Path(f"{dir_path}/{file.name}")) model_list.append(file_path) return model_list def get_download_file(temp_dir, url, civitai_key, progress=gr.Progress(track_tqdm=True)): if not "http" in url and is_repo_name(url) and not Path(url).exists(): print(f"Use HF Repo: {url}") new_file = url elif not "http" in url and Path(url).exists(): print(f"Use local file: {url}") new_file = url elif Path(f"{temp_dir}/{url.split('/')[-1]}").exists(): print(f"File to download alreday exists: {url}") new_file = f"{temp_dir}/{url.split('/')[-1]}" else: print(f"Start downloading: {url}") before = get_local_model_list(temp_dir) try: download_thing(temp_dir, url.strip(), civitai_key) except Exception: print(f"Download failed: {url}") return "" after = get_local_model_list(temp_dir) new_file = list_sub(after, before)[0] if list_sub(after, before) else "" if not new_file: print(f"Download failed: {url}") return "" print(f"Download completed: {url}") return new_file def save_readme_md(dir, url): orig_url = "" if "http" in url: orig_url = url if orig_url: md = f"""--- license: other license_name: flux-1-dev-non-commercial-license license_link: https://huggingface.co/black-forest-labs/FLUX.1-dev/blob/main/LICENSE. language: - en library_name: diffusers pipeline_tag: text-to-image tags: - text-to-image - Flux --- Converted from [{orig_url}]({orig_url}). """ else: md = f"""--- license: other license_name: flux-1-dev-non-commercial-license license_link: https://huggingface.co/black-forest-labs/FLUX.1-dev/blob/main/LICENSE. language: - en library_name: diffusers pipeline_tag: text-to-image tags: - text-to-image - Flux --- """ path = str(Path(dir, "README.md")) with open(path, mode='w', encoding="utf-8") as f: f.write(md) def is_repo_exists(repo_id): from huggingface_hub import HfApi api = HfApi() try: if api.repo_exists(repo_id=repo_id): return True else: return False except Exception as e: print(f"Error: Failed to connect {repo_id}. ") return True # for safe def create_diffusers_repo(new_repo_id, diffusers_folder, is_private, is_overwrite, progress=gr.Progress(track_tqdm=True)): from huggingface_hub import HfApi import os hf_token = os.environ.get("HF_TOKEN") api = HfApi() try: progress(0, desc="Start uploading...") api.create_repo(repo_id=new_repo_id, token=hf_token, private=is_private, exist_ok=is_overwrite) for path in Path(diffusers_folder).glob("*"): if path.is_dir(): api.upload_folder(repo_id=new_repo_id, folder_path=str(path), path_in_repo=path.name, token=hf_token) elif path.is_file(): api.upload_file(repo_id=new_repo_id, path_or_fileobj=str(path), path_in_repo=path.name, token=hf_token) progress(1, desc="Uploaded.") url = f"https://huggingface.co/{new_repo_id}" except Exception as e: print(f"Error: Failed to upload to {new_repo_id}. ") print(e) return "" return url # https://github.com/huggingface/diffusers/blob/main/scripts/convert_flux_to_diffusers.py # in SD3 original implementation of AdaLayerNormContinuous, it split linear projection output into shift, scale; # while in diffusers it split into scale, shift. Here we swap the linear projection weights in order to be able to use diffusers implementation with torch.no_grad(), torch.autocast(device): @torch.jit.script def swap_scale_shift(weight): shift, scale = weight.chunk(2, dim=0) new_weight = torch.cat([scale, shift], dim=0) return new_weight with torch.no_grad(), torch.autocast(device): def convert_flux_transformer_checkpoint_to_diffusers( original_state_dict, num_layers, num_single_layers, inner_dim, mlp_ratio=4.0, progress=gr.Progress(track_tqdm=True)): def conv(cdict: dict, odict: dict, ckey: str, okey: str): if okey in odict.keys(): progress(0, desc=f"Converting {okey} => {ckey}") print(f"Converting {okey} => {ckey}") cdict[ckey] = odict.pop(okey) gc.collect() def convswap(cdict: dict, odict: dict, ckey: str, okey: str): if okey in odict.keys(): progress(0, desc=f"Converting (swap) {okey} => {ckey}") print(f"Converting {okey} => {ckey} (swap)") cdict[ckey] = swap_scale_shift(odict.pop(okey)) gc.collect() def convqkv(cdict: dict, odict: dict, i: int): keys = odict.keys() if (f"double_blocks.{i}.img_attn.qkv.weight" in keys or f"double_blocks.{i}.txt_attn.qkv.weight" in keys\ or f"double_blocks.{i}.img_attn.qkv.bias" in keys or f"double_blocks.{i}.txt_attn.qkv.bias" in keys)\ and (f"double_blocks.{i}.img_attn.qkv.weight" not in keys or f"double_blocks.{i}.txt_attn.qkv.weight" not in keys\ or f"double_blocks.{i}.img_attn.qkv.bias" not in keys or f"double_blocks.{i}.txt_attn.qkv.bias" not in keys): progress(0, desc=f"Key error in converting Q, K, V (double_blocks.{i}).") print(f"Key error in converting Q, K, V (double_blocks.{i}).") return progress(0, desc=f"Converting Q, K, V (double_blocks.{i}).") print(f"Converting Q, K, V (double_blocks.{i}).") sample_q, sample_k, sample_v = torch.chunk( odict.pop(f"double_blocks.{i}.img_attn.qkv.weight"), 3, dim=0 ) context_q, context_k, context_v = torch.chunk( odict.pop(f"double_blocks.{i}.txt_attn.qkv.weight"), 3, dim=0 ) sample_q_bias, sample_k_bias, sample_v_bias = torch.chunk( odict.pop(f"double_blocks.{i}.img_attn.qkv.bias"), 3, dim=0 ) context_q_bias, context_k_bias, context_v_bias = torch.chunk( odict.pop(f"double_blocks.{i}.txt_attn.qkv.bias"), 3, dim=0 ) cdict[f"{block_prefix}attn.to_q.weight"] = torch.cat([sample_q]) cdict[f"{block_prefix}attn.to_q.bias"] = torch.cat([sample_q_bias]) cdict[f"{block_prefix}attn.to_k.weight"] = torch.cat([sample_k]) cdict[f"{block_prefix}attn.to_k.bias"] = torch.cat([sample_k_bias]) cdict[f"{block_prefix}attn.to_v.weight"] = torch.cat([sample_v]) cdict[f"{block_prefix}attn.to_v.bias"] = torch.cat([sample_v_bias]) cdict[f"{block_prefix}attn.add_q_proj.weight"] = torch.cat([context_q]) cdict[f"{block_prefix}attn.add_q_proj.bias"] = torch.cat([context_q_bias]) cdict[f"{block_prefix}attn.add_k_proj.weight"] = torch.cat([context_k]) cdict[f"{block_prefix}attn.add_k_proj.bias"] = torch.cat([context_k_bias]) cdict[f"{block_prefix}attn.add_v_proj.weight"] = torch.cat([context_v]) cdict[f"{block_prefix}attn.add_v_proj.bias"] = torch.cat([context_v_bias]) gc.collect() def convqkvmlp(cdict: dict, odict: dict, i: int, inner_dim: int, mlp_ratio: float): keys = odict.keys() if (f"single_blocks.{i}.linear1.weight" in keys or f"single_blocks.{i}.linear1.bias" in keys)\ and (f"single_blocks.{i}.linear1.weight" not in keys or f"single_blocks.{i}.linear1.bias" not in keys): progress(0, desc=f"Key error in converting Q, K, V, mlp (single_blocks.{i}).") print(f"Key error in converting Q, K, V, mlp (single_blocks.{i}).") return progress(0, desc=f"Converting Q, K, V, mlp (single_blocks.{i}).") print(f"Converting Q, K, V, mlp (single_blocks.{i}).") mlp_hidden_dim = int(inner_dim * mlp_ratio) split_size = (inner_dim, inner_dim, inner_dim, mlp_hidden_dim) q, k, v, mlp = torch.split(odict.pop(f"single_blocks.{i}.linear1.weight"), split_size, dim=0) q_bias, k_bias, v_bias, mlp_bias = torch.split( odict.pop(f"single_blocks.{i}.linear1.bias"), split_size, dim=0 ) cdict[f"{block_prefix}attn.to_q.weight"] = torch.cat([q]) cdict[f"{block_prefix}attn.to_q.bias"] = torch.cat([q_bias]) cdict[f"{block_prefix}attn.to_k.weight"] = torch.cat([k]) cdict[f"{block_prefix}attn.to_k.bias"] = torch.cat([k_bias]) cdict[f"{block_prefix}attn.to_v.weight"] = torch.cat([v]) cdict[f"{block_prefix}attn.to_v.bias"] = torch.cat([v_bias]) cdict[f"{block_prefix}proj_mlp.weight"] = torch.cat([mlp]) cdict[f"{block_prefix}proj_mlp.bias"] = torch.cat([mlp_bias]) gc.collect() converted_state_dict = {} progress(0, desc="Converting FLUX.1 state dict to Diffusers format.") ## time_text_embed.timestep_embedder <- time_in conv(converted_state_dict, original_state_dict, "time_text_embed.timestep_embedder.linear_1.weight", "time_in.in_layer.weight") conv(converted_state_dict, original_state_dict, "time_text_embed.timestep_embedder.linear_1.bias", "time_in.in_layer.bias") conv(converted_state_dict, original_state_dict, "time_text_embed.timestep_embedder.linear_2.weight", "time_in.out_layer.weight") conv(converted_state_dict, original_state_dict, "time_text_embed.timestep_embedder.linear_2.bias", "time_in.out_layer.bias") ## time_text_embed.text_embedder <- vector_in conv(converted_state_dict, original_state_dict, "time_text_embed.text_embedder.linear_1.weight", "vector_in.in_layer.weight") conv(converted_state_dict, original_state_dict, "time_text_embed.text_embedder.linear_1.bias", "vector_in.in_layer.bias") conv(converted_state_dict, original_state_dict, "time_text_embed.text_embedder.linear_2.weight", "vector_in.out_layer.weight") conv(converted_state_dict, original_state_dict, "time_text_embed.text_embedder.linear_2.bias", "vector_in.out_layer.bias") # guidance has_guidance = any("guidance" in k for k in original_state_dict) if has_guidance: conv(converted_state_dict, original_state_dict, "time_text_embed.guidance_embedder.linear_1.weight", "guidance_in.in_layer.weight") conv(converted_state_dict, original_state_dict, "time_text_embed.guidance_embedder.linear_1.bias", "guidance_in.in_layer.bias") conv(converted_state_dict, original_state_dict, "time_text_embed.guidance_embedder.linear_2.weight", "guidance_in.out_layer.weight") conv(converted_state_dict, original_state_dict, "time_text_embed.guidance_embedder.linear_2.bias", "guidance_in.out_layer.bias") # context_embedder conv(converted_state_dict, original_state_dict, "context_embedder.weight", "txt_in.weight") conv(converted_state_dict, original_state_dict, "context_embedder.bias", "txt_in.bias") # x_embedder conv(converted_state_dict, original_state_dict, "x_embedder.weight", "img_in.weight") conv(converted_state_dict, original_state_dict, "x_embedder.bias", "img_in.bias") progress(0.25, desc="Converting FLUX.1 state dict to Diffusers format.") # double transformer blocks for i in range(num_layers): block_prefix = f"transformer_blocks.{i}." # norms. ## norm1 conv(converted_state_dict, original_state_dict, f"{block_prefix}norm1.linear.weight", f"double_blocks.{i}.img_mod.lin.weight") conv(converted_state_dict, original_state_dict, f"{block_prefix}norm1.linear.bias", f"double_blocks.{i}.img_mod.lin.bias") ## norm1_context conv(converted_state_dict, original_state_dict, f"{block_prefix}norm1_context.linear.weight", f"double_blocks.{i}.txt_mod.lin.weight") conv(converted_state_dict, original_state_dict, f"{block_prefix}norm1_context.linear.bias", f"double_blocks.{i}.txt_mod.lin.bias") # Q, K, V convqkv(converted_state_dict, original_state_dict, i) # qk_norm conv(converted_state_dict, original_state_dict, f"{block_prefix}attn.norm_q.weight", f"double_blocks.{i}.img_attn.norm.query_norm.scale") conv(converted_state_dict, original_state_dict, f"{block_prefix}attn.norm_k.weight", f"double_blocks.{i}.img_attn.norm.key_norm.scale") conv(converted_state_dict, original_state_dict, f"{block_prefix}attn.norm_added_q.weight", f"double_blocks.{i}.txt_attn.norm.query_norm.scale") conv(converted_state_dict, original_state_dict, f"{block_prefix}attn.norm_added_k.weight", f"double_blocks.{i}.txt_attn.norm.key_norm.scale") # ff img_mlp conv(converted_state_dict, original_state_dict, f"{block_prefix}ff.net.0.proj.weight", f"double_blocks.{i}.img_mlp.0.weight") conv(converted_state_dict, original_state_dict, f"{block_prefix}ff.net.0.proj.bias", f"double_blocks.{i}.img_mlp.0.bias") conv(converted_state_dict, original_state_dict, f"{block_prefix}ff.net.2.weight", f"double_blocks.{i}.img_mlp.2.weight") conv(converted_state_dict, original_state_dict, f"{block_prefix}ff.net.2.bias", f"double_blocks.{i}.img_mlp.2.bias") conv(converted_state_dict, original_state_dict, f"{block_prefix}ff_context.net.0.proj.weight", f"double_blocks.{i}.txt_mlp.0.weight") conv(converted_state_dict, original_state_dict, f"{block_prefix}ff_context.net.0.proj.bias", f"double_blocks.{i}.txt_mlp.0.bias") conv(converted_state_dict, original_state_dict, f"{block_prefix}ff_context.net.2.weight", f"double_blocks.{i}.txt_mlp.2.weight") conv(converted_state_dict, original_state_dict, f"{block_prefix}ff_context.net.2.bias", f"double_blocks.{i}.txt_mlp.2.bias") # output projections. conv(converted_state_dict, original_state_dict, f"{block_prefix}attn.to_out.0.weight", f"double_blocks.{i}.img_attn.proj.weight") conv(converted_state_dict, original_state_dict, f"{block_prefix}attn.to_out.0.bias", f"double_blocks.{i}.img_attn.proj.bias") conv(converted_state_dict, original_state_dict, f"{block_prefix}attn.to_add_out.weight", f"double_blocks.{i}.txt_attn.proj.weight") conv(converted_state_dict, original_state_dict, f"{block_prefix}attn.to_add_out.bias", f"double_blocks.{i}.txt_attn.proj.bias") progress(0.5, desc="Converting FLUX.1 state dict to Diffusers format.") # single transfomer blocks for i in range(num_single_layers): block_prefix = f"single_transformer_blocks.{i}." # norm.linear <- single_blocks.0.modulation.lin conv(converted_state_dict, original_state_dict, f"{block_prefix}norm.linear.weight", f"single_blocks.{i}.modulation.lin.weight") conv(converted_state_dict, original_state_dict, f"{block_prefix}norm.linear.bias", f"single_blocks.{i}.modulation.lin.bias") # Q, K, V, mlp convqkvmlp(converted_state_dict, original_state_dict, i, inner_dim, mlp_ratio) # qk norm conv(converted_state_dict, original_state_dict, f"{block_prefix}attn.norm_q.weight", f"single_blocks.{i}.norm.query_norm.scale") conv(converted_state_dict, original_state_dict, f"{block_prefix}attn.norm_k.weight", f"single_blocks.{i}.norm.key_norm.scale") # output projections. conv(converted_state_dict, original_state_dict, f"{block_prefix}proj_out.weight", f"single_blocks.{i}.linear2.weight") conv(converted_state_dict, original_state_dict, f"{block_prefix}proj_out.bias", f"single_blocks.{i}.linear2.bias") progress(0.75, desc="Converting FLUX.1 state dict to Diffusers format.") conv(converted_state_dict, original_state_dict, "proj_out.weight", "final_layer.linear.weight") conv(converted_state_dict, original_state_dict, "proj_out.bias", "final_layer.linear.bias") convswap(converted_state_dict, original_state_dict, "norm_out.linear.weight", "final_layer.adaLN_modulation.1.weight") convswap(converted_state_dict, original_state_dict, "norm_out.linear.bias", "final_layer.adaLN_modulation.1.bias") progress(1, desc="Converting FLUX.1 state dict to Diffusers format.") return converted_state_dict # read safetensors metadata def read_safetensors_metadata(path): with open(path, 'rb') as f: header_size = int.from_bytes(f.read(8), 'little') header_json = f.read(header_size).decode('utf-8') header = json.loads(header_json) metadata = header.get('__metadata__', {}) return metadata.copy() def normalize_key(k: str): return k.replace("vae.", "").replace("model.diffusion_model.", "")\ .replace("text_encoders.clip_l.transformer.", "")\ .replace("text_encoders.t5xxl.transformer.", "") def load_json_list(path: str): try: with open(path, encoding='utf-8') as f: return list(json.load(f)) except Exception as e: print(e) return [] # https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/modeling_utils.py # https://huggingface.co/docs/huggingface_hub/v0.24.5/package_reference/serialization # https://huggingface.co/docs/huggingface_hub/index with torch.no_grad(): def to_safetensors(sd: dict, path: str, pattern: str, size: str, progress=gr.Progress(track_tqdm=True)): from huggingface_hub import save_torch_state_dict print(f"Saving a temporary file to disk: {path}") os.makedirs(path, exist_ok=True) try: for k, v in sd.items(): sd[k] = v.to(device="cpu") save_torch_state_dict(sd, path, filename_pattern=pattern, max_shard_size=size) except Exception as e: print(e) # https://discuss.huggingface.co/t/t5forconditionalgeneration-checkpoint-size-mismatch-19418/24119 # https://github.com/huggingface/transformers/issues/13769 # https://github.com/huggingface/optimum-quanto/issues/278 # https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/serialization/_torch.py # https://huggingface.co/docs/accelerate/usage_guides/big_modeling with torch.no_grad(): def to_safetensors_flux_module(sd: dict, path: str, pattern: str, size: str, quantization: bool=False, name: str = "", metadata: dict | None = None, progress=gr.Progress(track_tqdm=True)): from huggingface_hub import save_torch_state_dict, save_torch_model from accelerate import init_empty_weights try: progress(0, desc=f"Preparing to save FLUX.1 {name} to Diffusers format.") print(f"Preparing to save FLUX.1 {name} to Diffusers format.") for k, v in sd.items(): sd[k] = v.to(device="cpu") progress(0, desc=f"Loading FLUX.1 {name}.") print(f"Loading FLUX.1 {name}.") os.makedirs(path, exist_ok=True) if quantization: progress(0.5, desc=f"Saving quantized FLUX.1 {name} to {path}") print(f"Saving quantized FLUX.1 {name} to {path}") else: progress(0.5, desc=f"Saving FLUX.1 {name} to: {path}") if False and path.endswith("/transformer"): from diffusers import FluxTransformer2DModel has_guidance = any("guidance" in k for k in sd) with init_empty_weights(): model = FluxTransformer2DModel(guidance_embeds=has_guidance) model.to("cpu") model.load_state_dict(sd, strict=True) print(f"Saving FLUX.1 {name} to: {path} (FluxTransformer2DModel)") if metadata is not None: progress(0.5, desc=f"Saving FLUX.1 {name} metadata to: {path}") save_torch_model(model=model, save_directory=path, filename_pattern=pattern, max_shard_size=size, metadata=metadata) else: save_torch_model(model=model, save_directory=path, filename_pattern=pattern, max_shard_size=size) else: print(f"Saving FLUX.1 {name} to: {path}") if metadata is not None: progress(0.5, desc=f"Saving FLUX.1 {name} metadata to: {path}") save_torch_state_dict(state_dict=sd, save_directory=path, filename_pattern=pattern, max_shard_size=size, metadata=metadata) else: save_torch_state_dict(state_dict=sd, save_directory=path, filename_pattern=pattern, max_shard_size=size) progress(1, desc=f"Saved FLUX.1 {name} to: {path}") print(f"Saved FLUX.1 {name} to: {path}") except Exception as e: print(e) finally: gc.collect() flux_transformer_json = "flux_transformer_keys.json" flux_t5xxl_json = "flux_t5xxl_keys.json" flux_clip_json = "flux_clip_keys.json" flux_vae_json = "flux_vae_keys.json" keys_flux_t5xxl = set(load_json_list(flux_t5xxl_json)) keys_flux_transformer = set(load_json_list(flux_transformer_json)) keys_flux_clip = set(load_json_list(flux_clip_json)) keys_flux_vae = set(load_json_list(flux_vae_json)) with torch.no_grad(): def dequant_tensor(v: torch.Tensor, dtype: torch.dtype, dequant: bool): try: #print(f"shape: {v.shape} / dim: {v.ndim}") if dequant: qtype = v.tensor_type if v.dtype in TORCH_DTYPE: return v.to(dtype) if v.dtype != dtype else v elif qtype in GGUF_QTYPE: return dequantize_tensor(v, dtype) elif torch.dtype in TORCH_QUANTIZED_DTYPE: return torch.dequantize(v).to(dtype) else: return torch.dequantize(v).to(dtype) else: return v.to(dtype) if v.dtype != dtype else v except Exception as e: print(e) with torch.no_grad(): def normalize_flux_state_dict(path: str, savepath: str, dtype: torch.dtype = torch.bfloat16, dequant: bool = False, progress=gr.Progress(track_tqdm=True)): progress(0, desc=f"Loading and normalizing FLUX.1 safetensors: {path}") print(f"Loading and normalizing FLUX.1 safetensors: {path}") new_sd = dict() state_dict = load_file(path, device="cpu") try: for k in list(state_dict.keys()): v = state_dict.pop(k) nk = normalize_key(k) print(f"{k} => {nk}") # new_sd[nk] = dequant_tensor(v, dtype, dequant) except Exception as e: print(e) return finally: clear_sd(state_dict) new_path = str(Path(savepath, Path(path).stem + "_fixed" + Path(path).suffix)) metadata = read_safetensors_metadata(path) progress(0.5, desc=f"Saving FLUX.1 safetensors: {new_path}") print(f"Saving FLUX.1 safetensors: {new_path}") os.makedirs(savepath, exist_ok=True) save_file(new_sd, new_path, metadata={"format": "pt", **metadata}) progress(1, desc=f"Saved FLUX.1 safetensors: {new_path}") print(f"Saved FLUX.1 safetensors: {new_path}") clear_sd(new_sd) with torch.no_grad(): def extract_norm_flux_module_sd(path: str, dtype: torch.dtype = torch.bfloat16, dequant: bool = False, name: str = "", keys: set = {}, progress=gr.Progress(track_tqdm=True)): progress(0, desc=f"Loading and normalizing FLUX.1 {name} safetensors: {path}") print(f"Loading and normalizing FLUX.1 {name} safetensors: {path}") new_sd = dict() state_dict = load_file(path, device="cpu") try: for k in list(state_dict.keys()): if k not in keys: state_dict.pop(k) gc.collect() for k in list(state_dict.keys()): v = state_dict.pop(k) if k in keys: nk = normalize_key(k) progress(0.5, desc=f"{k} => {nk}") # print(f"{k} => {nk}") # new_sd[nk] = dequant_tensor(v, dtype, dequant) #print_resource_usage() # except Exception as e: print(e) return None finally: progress(1, desc=f"Normalized FLUX.1 {name} safetensors: {path}") print(f"Normalized FLUX.1 {name} safetensors: {path}") clear_sd(state_dict) return new_sd with torch.no_grad(): def convert_flux_transformer_sd_to_diffusers(sd: dict, progress=gr.Progress(track_tqdm=True)): progress(0, desc="Converting FLUX.1 state dict to Diffusers format.") print("Converting FLUX.1 state dict to Diffusers format.") num_layers = 19 num_single_layers = 38 inner_dim = 3072 mlp_ratio = 4.0 try: sd = convert_flux_transformer_checkpoint_to_diffusers( sd, num_layers, num_single_layers, inner_dim, mlp_ratio=mlp_ratio ) except Exception as e: print(e) finally: progress(1, desc="Converted FLUX.1 state dict to Diffusers format.") print("Converted FLUX.1 state dict to Diffusers format.") gc.collect() return sd with torch.no_grad(): def load_sharded_safetensors(path: str): import glob sd = {} try: for filepath in glob.glob(f"{path}/*.safetensors"): sharded_sd = load_file(str(filepath), device="cpu") for k, v in sharded_sd.items(): sharded_sd[k] = v.to(device="cpu") sd = sd | sharded_sd.copy() clear_sd(sharded_sd) except Exception as e: print(e) return sd # https://huggingface.co/docs/safetensors/api/torch with torch.no_grad(): def convert_flux_transformer_sd_to_diffusers_sharded(sd: dict, path: str, pattern: str, size: str, progress=gr.Progress(track_tqdm=True)): from huggingface_hub import save_torch_state_dict#, load_torch_model import glob try: progress(0, desc=f"Saving temporary files to disk: {path}") print(f"Saving temporary files to disk: {path}") os.makedirs(path, exist_ok=True) for k, v in sd.items(): if k in set(keys_flux_transformer): sd[k] = v.to(device="cpu") save_torch_state_dict(sd, path, filename_pattern=pattern, max_shard_size=size) clear_sd(sd) progress(0.25, desc=f"Saved temporary files to disk: {path}") print(f"Saved temporary files to disk: {path}") for filepath in glob.glob(f"{path}/*.safetensors"): progress(0.25, desc=f"Processing temporary files: {str(filepath)}") print(f"Processing temporary files: {str(filepath)}") sharded_sd = load_file(str(filepath), device="cpu") sharded_sd = convert_flux_transformer_sd_to_diffusers(sharded_sd) for k, v in sharded_sd.items(): sharded_sd[k] = v.to(device="cpu") save_file(sharded_sd, str(filepath)) clear_sd(sharded_sd) print(f"Loading temporary files from disk: {path}") sd = load_sharded_safetensors(path) print(f"Loaded temporary files from disk: {path}") except Exception as e: print(e) return sd with torch.no_grad(): def extract_normalized_flux_state_dict_sharded(loadpath: str, dtype: torch.dtype, dequant: bool, path: str, pattern: str, size: str, progress=gr.Progress(track_tqdm=True)): from huggingface_hub import save_torch_state_dict#, load_torch_model import glob try: progress(0, desc=f"Loading model file: {loadpath}") print(f"Loading model file: {loadpath}") sd = load_file(loadpath, device="cpu") progress(0, desc=f"Saving temporary files to disk: {path}") print(f"Saving temporary files to disk: {path}") os.makedirs(path, exist_ok=True) for k, v in sd.items(): sd[k] = v.to(device="cpu") save_torch_state_dict(sd, path, filename_pattern=pattern, max_shard_size=size) clear_sd(sd) progress(0.25, desc=f"Saved temporary files to disk: {path}") print(f"Saved temporary files to disk: {path}") for filepath in glob.glob(f"{path}/*.safetensors"): progress(0.25, desc=f"Processing temporary files: {str(filepath)}") print(f"Processing temporary files: {str(filepath)}") sharded_sd = extract_norm_flux_module_sd(str(filepath), dtype, dequant, "Transformer", keys_flux_transformer) for k, v in sharded_sd.items(): sharded_sd[k] = v.to(device="cpu") save_file(sharded_sd, str(filepath)) clear_sd(sharded_sd) print(f"Processed temporary files: {str(filepath)}") print(f"Loading temporary files from disk: {path}") sd = load_sharded_safetensors(path) print(f"Loaded temporary files from disk: {path}") except Exception as e: print(e) return sd def download_repo(repo_name, path, use_original=["vae", "text_encoder"], progress=gr.Progress(track_tqdm=True)): from huggingface_hub import snapshot_download print(f"Downloading {repo_name}.") try: if "text_encoder_2" in use_original: snapshot_download(repo_id=repo_name, local_dir=path, ignore_patterns=["transformer/diffusion*.*", "*.sft", ".*", "README*", "*.md", "*.index", "*.jpg", "*.png", "*.webp"]) else: snapshot_download(repo_id=repo_name, local_dir=path, ignore_patterns=["transformer/diffusion*.*", "text_encoder_2/model*.*", "*.sft", ".*", "README*", "*.md", "*.index", "*.jpg", "*.png", "*.webp"]) except Exception as e: print(e) def copy_nontensor_files(from_path, to_path, use_original=["vae", "text_encoder"]): import shutil if "text_encoder_2" in use_original: te_from = str(Path(from_path, "text_encoder_2")) te_to = str(Path(to_path, "text_encoder_2")) print(f"Copying Text Encoder 2 files {te_from} to {te_to}") shutil.copytree(te_from, te_to, ignore=shutil.ignore_patterns(".*", "README*", "*.md", "*.jpg", "*.png", "*.webp"), dirs_exist_ok=True) if "text_encoder" in use_original: te1_from = str(Path(from_path, "text_encoder")) te1_to = str(Path(to_path, "text_encoder")) print(f"Copying Text Encoder 1 files {te1_from} to {te1_to}") shutil.copytree(te1_from, te1_to, ignore=shutil.ignore_patterns(".*", "README*", "*.md", "*.jpg", "*.png", "*.webp"), dirs_exist_ok=True) if "vae" in use_original: vae_from = str(Path(from_path, "vae")) vae_to = str(Path(to_path, "vae")) print(f"Copying VAE files {vae_from} to {vae_to}") shutil.copytree(vae_from, vae_to, ignore=shutil.ignore_patterns(".*", "README*", "*.md", "*.jpg", "*.png", "*.webp"), dirs_exist_ok=True) tn2_from = str(Path(from_path, "tokenizer_2")) tn2_to = str(Path(to_path, "tokenizer_2")) print(f"Copying Tokenizer 2 files {tn2_from} to {tn2_to}") shutil.copytree(tn2_from, tn2_to, ignore=shutil.ignore_patterns(".*", "README*", "*.md", "*.jpg", "*.png", "*.webp"), dirs_exist_ok=True) print(f"Copying non-tensor files {from_path} to {to_path}") shutil.copytree(from_path, to_path, ignore=shutil.ignore_patterns("*.safetensors", "*.bin", "*.sft", ".*", "README*", "*.md", "*.index", "*.jpg", "*.png", "*.webp", "*.index.json"), dirs_exist_ok=True) def save_flux_other_diffusers(path: str, model_type: str = "dev", use_original: list = ["vae", "text_encoder"], progress=gr.Progress(track_tqdm=True)): import shutil progress(0, desc="Loading FLUX.1 Components.") print("Loading FLUX.1 Components.") temppath = system_temp_dir if model_type == "schnell": repo = flux_schnell_repo else: repo = flux_dev_repo os.makedirs(temppath, exist_ok=True) os.makedirs(path, exist_ok=True) download_repo(repo, temppath, use_original) progress(0.5, desc="Saving FLUX.1 Components.") print("Saving FLUX.1 Components.") copy_nontensor_files(temppath, path, use_original) shutil.rmtree(temppath) with torch.no_grad(): def fix_flux_safetensors(loadpath: str, savepath: str, dtype: torch.dtype = torch.bfloat16, quantization: bool = False, model_type: str = "dev", dequant: bool = False): save_flux_other_diffusers(savepath, model_type) normalize_flux_state_dict(loadpath, savepath, dtype, dequant) clear_cache() with torch.no_grad(): # Much lower memory consumption, but higher disk load def flux_to_diffusers_lowmem(loadpath: str, savepath: str, dtype: torch.dtype = torch.bfloat16, quantization: bool = False, model_type: str = "dev", dequant: bool = False, use_original: list = ["vae", "text_encoder"], new_repo_id: str = "", local: bool = False, progress=gr.Progress(track_tqdm=True)): unet_sd_path = savepath.removesuffix("/") + "/transformer" unet_sd_pattern = "diffusion_pytorch_model{suffix}.safetensors" unet_sd_size = "9.5GB" te_sd_path = savepath.removesuffix("/") + "/text_encoder_2" te_sd_pattern = "model{suffix}.safetensors" te_sd_size = "5GB" clip_sd_path = savepath.removesuffix("/") + "/text_encoder" clip_sd_pattern = "model{suffix}.safetensors" clip_sd_size = "9.5GB" vae_sd_path = savepath.removesuffix("/") + "/vae" vae_sd_pattern = "diffusion_pytorch_model{suffix}.safetensors" vae_sd_size = "9.5GB" print_resource_usage() # metadata = {"format": "pt", **read_safetensors_metadata(loadpath)} clear_cache() print_resource_usage() # if "vae" not in use_original: vae_sd = extract_norm_flux_module_sd(loadpath, torch.bfloat16, dequant, "VAE", keys_flux_vae) to_safetensors_flux_module(vae_sd, vae_sd_path, vae_sd_pattern, vae_sd_size, quantization, "VAE", None) clear_sd(vae_sd) print_resource_usage() # if "text_encoder" not in use_original: clip_sd = extract_norm_flux_module_sd(loadpath, torch.bfloat16, dequant, "Text Encoder", keys_flux_clip) to_safetensors_flux_module(clip_sd, clip_sd_path, clip_sd_pattern, clip_sd_size, quantization, "Text Encoder", None) clear_sd(clip_sd) print_resource_usage() # if "text_encoder_2" not in use_original: te_sd = extract_norm_flux_module_sd(loadpath, dtype, dequant, "Text Encoder 2", keys_flux_t5xxl) to_safetensors_flux_module(te_sd, te_sd_path, te_sd_pattern, te_sd_size, quantization, "Text Encoder 2", None) clear_sd(te_sd) print_resource_usage() # unet_sd = extract_norm_flux_module_sd(loadpath, dtype, dequant, "Transformer", keys_flux_transformer) clear_cache() print_resource_usage() # if not local: os.remove(loadpath) print("Deleted downloaded file.") clear_cache() print_resource_usage() # unet_sd = convert_flux_transformer_sd_to_diffusers(unet_sd) clear_cache() print_resource_usage() # to_safetensors_flux_module(unet_sd, unet_sd_path, unet_sd_pattern, unet_sd_size, quantization, "Transformer", metadata) clear_sd(unet_sd) print_resource_usage() # save_flux_other_diffusers(savepath, model_type, use_original) print_resource_usage() # with torch.no_grad(): # lowest memory consumption, but higheest disk load def flux_to_diffusers_lowmem2(loadpath: str, savepath: str, dtype: torch.dtype = torch.bfloat16, quantization: bool = False, model_type: str = "dev", dequant: bool = False, use_original: list = ["vae", "text_encoder"], new_repo_id: str = "", progress=gr.Progress(track_tqdm=True)): unet_sd_path = savepath.removesuffix("/") + "/transformer" unet_temp_path = system_temp_dir.removesuffix("/") + "/sharded" unet_sd_pattern = "diffusion_pytorch_model{suffix}.safetensors" unet_sd_size = "10GB" unet_temp_size = "5GB" te_sd_path = savepath.removesuffix("/") + "/text_encoder_2" te_sd_pattern = "model{suffix}.safetensors" te_sd_size = "5GB" clip_sd_path = savepath.removesuffix("/") + "/text_encoder" clip_sd_pattern = "model{suffix}.safetensors" clip_sd_size = "10GB" vae_sd_path = savepath.removesuffix("/") + "/vae" vae_sd_pattern = "diffusion_pytorch_model{suffix}.safetensors" vae_sd_size = "10GB" print_resource_usage() # metadata = {"format": "pt", **read_safetensors_metadata(loadpath)} clear_cache() print_resource_usage() # if "vae" not in use_original: vae_sd = extract_norm_flux_module_sd(loadpath, torch.bfloat16, dequant, "VAE", keys_flux_vae) to_safetensors_flux_module(vae_sd, vae_sd_path, vae_sd_pattern, vae_sd_size, quantization, "VAE", None) clear_sd(vae_sd) print_resource_usage() # if "text_encoder" not in use_original: clip_sd = extract_norm_flux_module_sd(loadpath, torch.bfloat16, dequant, "Text Encoder", keys_flux_clip) to_safetensors_flux_module(clip_sd, clip_sd_path, clip_sd_pattern, clip_sd_size, quantization, "Text Encoder", None) clear_sd(clip_sd) print_resource_usage() # if "text_encoder_2" not in use_original: te_sd = extract_norm_flux_module_sd(loadpath, dtype, dequant, "Text Encoder 2", keys_flux_t5xxl) to_safetensors_flux_module(te_sd, te_sd_path, te_sd_pattern, te_sd_size, quantization, "Text Encoder 2", None) clear_sd(te_sd) print_resource_usage() # unet_sd = extract_normalized_flux_state_dict_sharded(loadpath, dtype, dequant, unet_temp_path, unet_sd_pattern, unet_temp_size) clear_cache() print_resource_usage() # unet_sd = convert_flux_transformer_sd_to_diffusers_sharded(unet_sd, unet_temp_path, unet_sd_pattern, unet_temp_size) clear_cache() print_resource_usage() # to_safetensors_flux_module(unet_sd, unet_sd_path, unet_sd_pattern, unet_sd_size, quantization, "Transformer", metadata) clear_sd(unet_sd) print_resource_usage() # save_flux_other_diffusers(savepath, model_type, use_original) print_resource_usage() # def convert_url_to_diffusers_flux(url, civitai_key="", is_upload_sf=False, data_type="bf16", model_type="dev", dequant=False, use_original=["vae", "text_encoder"], hf_user="", hf_repo="", q=None, progress=gr.Progress(track_tqdm=True)): progress(0, desc="Start converting...") temp_dir = "." print_resource_usage() # new_file = get_download_file(temp_dir, url, civitai_key) if not new_file: print(f"Not found: {url}") return "" new_repo_name = Path(new_file).stem.replace(" ", "_").replace(",", "_").replace(".", "_") # dtype = torch.bfloat16 quantization = False if data_type == "fp8": dtype = torch.float8_e4m3fn elif data_type == "fp16": dtype = torch.float16 elif data_type == "qfloat8": dtype = torch.bfloat16 quantization = True else: dtype = torch.bfloat16 new_repo_id = f"{hf_user}/{Path(new_repo_name).stem}" if hf_repo != "": new_repo_id = f"{hf_user}/{hf_repo}" flux_to_diffusers_lowmem(new_file, new_repo_name, dtype, quantization, model_type, dequant, use_original, new_repo_id) """if is_upload_sf: import shutil shutil.move(str(Path(new_file).resolve()), str(Path(new_repo_name, Path(new_file).name).resolve())) else: os.remove(new_file)""" progress(1, desc="Converted.") q.put(new_repo_name) return new_repo_name def convert_url_to_fixed_flux_safetensors(url, civitai_key="", is_upload_sf=False, data_type="bf16", model_type="dev", dequant=False, q=None, progress=gr.Progress(track_tqdm=True)): progress(0, desc="Start converting...") temp_dir = "." print_resource_usage() # new_file = get_download_file(temp_dir, url, civitai_key) if not new_file: print(f"Not found: {url}") return "" new_repo_name = Path(new_file).stem.replace(" ", "_").replace(",", "_").replace(".", "_") # dtype = torch.bfloat16 quantization = False if data_type == "fp8": dtype = torch.float8_e4m3fn elif data_type == "fp16": dtype = torch.float16 elif data_type == "qfloat8": dtype = torch.bfloat16 quantization = True else: dtype = torch.bfloat16 fix_flux_safetensors(new_file, new_repo_name, dtype, model_type, dequant) os.remove(new_file) progress(1, desc="Converted.") q.put(new_repo_name) return new_repo_name def convert_url_to_diffusers_repo_flux(dl_url, hf_user, hf_repo, hf_token, civitai_key="", is_private=True, is_overwrite=False, is_upload_sf=False, data_type="bf16", model_type="dev", dequant=False, repo_urls=[], fix_only=False, use_original=["vae", "text_encoder"], progress=gr.Progress(track_tqdm=True)): import multiprocessing as mp import shutil if not hf_user: print(f"Invalid user name: {hf_user}") progress(1, desc=f"Invalid user name: {hf_user}") return gr.update(value=repo_urls, choices=repo_urls), gr.update(value="") if hf_token and not os.environ.get("HF_TOKEN"): os.environ['HF_TOKEN'] = hf_token if not civitai_key and os.environ.get("CIVITAI_API_KEY"): civitai_key = os.environ.get("CIVITAI_API_KEY") q = mp.Queue() if fix_only: p = mp.Process(target=convert_url_to_fixed_flux_safetensors, args=(dl_url, civitai_key, is_upload_sf, data_type, model_type, dequant, q)) #new_path = convert_url_to_fixed_flux_safetensors(dl_url, civitai_key, is_upload_sf, data_type, model_type, dequant) else: p = mp.Process(target=convert_url_to_diffusers_flux, args=(dl_url, civitai_key, is_upload_sf, data_type, model_type, dequant, use_original, hf_user, hf_repo, q)) #new_path = convert_url_to_diffusers_flux(dl_url, civitai_key, is_upload_sf, data_type, model_type, dequant) p.start() new_path = q.get() p.join() if not new_path: return "" new_repo_id = f"{hf_user}/{Path(new_path).stem}" if hf_repo != "": new_repo_id = f"{hf_user}/{hf_repo}" if not is_repo_name(new_repo_id): print(f"Invalid repo name: {new_repo_id}") progress(1, desc=f"Invalid repo name: {new_repo_id}") return gr.update(value=repo_urls, choices=repo_urls), gr.update(value="") if not is_overwrite and is_repo_exists(new_repo_id): print(f"Repo already exists: {new_repo_id}") progress(1, desc=f"Repo already exists: {new_repo_id}") return gr.update(value=repo_urls, choices=repo_urls), gr.update(value="") #save_readme_md(new_path, dl_url) repo_url = create_diffusers_repo(new_repo_id, new_path, is_private, is_overwrite) shutil.rmtree(new_path) if not repo_urls: repo_urls = [] repo_urls.append(repo_url) md = "Your new repo:
" for u in repo_urls: md += f"[{str(u).split('/')[-2]}/{str(u).split('/')[-1]}]({str(u)})
" return gr.update(value=repo_urls, choices=repo_urls), gr.update(value=md) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--url", default=None, type=str, required=False, help="URL of the model to convert.") parser.add_argument("--file", default=None, type=str, required=False, help="Filename of the model to convert.") parser.add_argument("--fix", action="store_true", help="Only fix the keys of the local model.") parser.add_argument("--civitai_key", default=None, type=str, required=False, help="Civitai API Key (If you want to download file from Civitai).") parser.add_argument("--dtype", type=str, default="fp8") parser.add_argument("--model", type=str, default="dev") parser.add_argument("--dequant", action="store_true", help="Dequantize model.") args = parser.parse_args() assert (args.url, args.file) != (None, None), "Must provide --url or --file!" dtype = torch.bfloat16 quantization = False if args.dtype == "fp8": dtype = torch.float8_e4m3fn elif args.dtype == "fp16": dtype = torch.float16 elif args.dtype == "qfloat8": dtype = torch.bfloat16 quantization = True else: dtype = torch.bfloat16 use_original = ["vae", "text_encoder"] new_repo_id = "" use_local = True if args.file is not None and Path(args.file).exists(): if args.fix: normalize_flux_state_dict(args.file, ".", dtype, args.dequant) else: flux_to_diffusers_lowmem(args.file, Path(args.file).stem, dtype, quantization, args.model, args.dequant, use_original, new_repo_id, use_local) elif args.url is not None: convert_url_to_diffusers_flux(args.url, args.civitai_key, False, args.dtype, args.model, args.dequant)