Download checkpoint_splitter/split_checkpoint_Illustrious.py from waveshaker/loras_tools: direct link, hf CLI and curl.
- Browser
- Download file 6.02 kB
-
https://huggingface.co/waveshaker/loras_tools/resolve/main/checkpoint_splitter/split_checkpoint_Illustrious.py
- Command line
-
hf download hf://waveshaker/loras_tools/checkpoint_splitter/split_checkpoint_Illustrious.py
-
curl -L -o split_checkpoint_Illustrious.py https://huggingface.co/waveshaker/loras_tools/resolve/main/checkpoint_splitter/split_checkpoint_Illustrious.py
6.02 kB
| import os | |
| import argparse | |
| from safetensors import safe_open | |
| from safetensors.torch import save_file | |
| def split_illustrious_compatible(input_path): | |
| if not os.path.exists(input_path): | |
| print(f"错误: 找不到输入文件 '{input_path}'") | |
| return | |
| base_dir = os.path.dirname(input_path) | |
| base_name = os.path.splitext(os.path.basename(input_path))[0] | |
| clipL_path = os.path.join(base_dir, f"{base_name}_clipL.safetensors") | |
| clipG_path = os.path.join(base_dir, f"{base_name}_clipG.safetensors") | |
| vae_path = os.path.join(base_dir, f"{base_name}_VAE.safetensors") | |
| unet_path = os.path.join(base_dir, f"{base_name}_Diffusion.safetensors") | |
| clipL_tensors = {} | |
| clipG_tensors = {} | |
| vae_tensors = {} | |
| diffusion_tensors = {} | |
| print(f"📦 正在执行切分: {input_path} ...") | |
| with safe_open(input_path, framework="pt", device="cpu") as f: | |
| all_keys = f.keys() | |
| # 判断是否是官方的 transformers 架构 | |
| has_text_model_g = any(k.startswith("conditioner.embedders.1.transformer.") for k in all_keys) | |
| for key in all_keys: | |
| tensor = f.get_tensor(key) | |
| # 1. 提取并清洗 CLIP L (官方和 WAI 都是 transformers 结构) | |
| if key.startswith("conditioner.embedders.0.transformer."): | |
| new_key = key.replace("conditioner.embedders.0.transformer.", "", 1) | |
| clipL_tensors[new_key] = tensor | |
| # 2. 提取并精准重组 CLIP G | |
| elif key.startswith("conditioner.embedders.1."): | |
| if has_text_model_g: | |
| # 官方 Illustrious 格式 (transformers) | |
| new_key = key.replace("conditioner.embedders.1.transformer.", "", 1) | |
| if new_key != "logit_scale": | |
| clipG_tensors[new_key] = tensor | |
| else: | |
| # WAI 格式 (OpenCLIP 原生格式) | |
| old_key = key.replace("conditioner.embedders.1.model.", "", 1) | |
| if old_key == "logit_scale": | |
| continue | |
| new_key = old_key | |
| # 基础结构重命名 | |
| if "transformer.resblocks." in old_key: | |
| new_key = old_key.replace("transformer.resblocks.", "text_model.encoder.layers.", 1) | |
| new_key = new_key.replace(".ln_1.", ".layer_norm1.", 1) | |
| new_key = new_key.replace(".ln_2.", ".layer_norm2.", 1) | |
| new_key = new_key.replace(".mlp.c_fc.", ".mlp.fc1.", 1) | |
| new_key = new_key.replace(".mlp.c_proj.", ".mlp.fc2.", 1) | |
| elif old_key == "positional_embedding": | |
| new_key = "text_model.embeddings.position_embedding.weight" | |
| elif old_key == "token_embedding.weight": | |
| new_key = "text_model.embeddings.token_embedding.weight" | |
| elif old_key == "ln_final.weight": | |
| new_key = "text_model.final_layer_norm.weight" | |
| elif old_key == "ln_final.bias": | |
| new_key = "text_model.final_layer_norm.bias" | |
| elif old_key == "text_projection": | |
| new_key = "text_projection" | |
| # 🛠️ 核心修复:解耦 in_proj 复合权重矩阵为标准的 q/k/v 组件 | |
| if ".attn.in_proj_weight" in old_key: | |
| base_layer = new_key.replace(".attn.in_proj_weight", ".self_attn", 1) | |
| q, k, v = tensor.chunk(3, dim=0) | |
| clipG_tensors[f"{base_layer}.q_proj.weight"] = q | |
| clipG_tensors[f"{base_layer}.k_proj.weight"] = k | |
| clipG_tensors[f"{base_layer}.v_proj.weight"] = v | |
| elif ".attn.in_proj_bias" in old_key: | |
| base_layer = new_key.replace(".attn.in_proj_bias", ".self_attn", 1) | |
| q, k, v = tensor.chunk(3, dim=0) | |
| clipG_tensors[f"{base_layer}.q_proj.bias"] = q | |
| clipG_tensors[f"{base_layer}.k_proj.bias"] = k | |
| clipG_tensors[f"{base_layer}.v_proj.bias"] = v | |
| elif ".attn.out_proj.weight" in old_key: | |
| new_key = new_key.replace(".attn.out_proj.weight", ".self_attn.out_proj.weight", 1) | |
| clipG_tensors[new_key] = tensor | |
| elif ".attn.out_proj.bias" in old_key: | |
| new_key = new_key.replace(".attn.out_proj.bias", ".self_attn.out_proj.bias", 1) | |
| clipG_tensors[new_key] = tensor | |
| else: | |
| clipG_tensors[new_key] = tensor | |
| # 3. 提取 VAE | |
| elif key.startswith("first_stage_model."): | |
| vae_tensors[key.replace("first_stage_model.", "", 1)] = tensor | |
| # 4. 提取 UNet | |
| elif key.startswith("model.diffusion_model."): | |
| diffusion_tensors[key.replace("model.diffusion_model.", "", 1)] = tensor | |
| print("\n💾 正在保存组件...") | |
| if clipL_tensors: | |
| save_file(clipL_tensors, clipL_path) | |
| print(f" --> 独立 CLIP-L 保存成功 (键数: {len(clipL_tensors)})") | |
| if clipG_tensors: | |
| save_file(clipG_tensors, clipG_path) | |
| print(f" --> 独立 CLIP-G 保存成功 (键数: {len(clipG_tensors)})") | |
| if vae_tensors: save_file(vae_tensors, vae_path) | |
| if diffusion_tensors: save_file(diffusion_tensors, unet_path) | |
| print("\n🎉 切分完成!") | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("input_file", type=str) | |
| args = parser.parse_args() | |
| split_illustrious_compatible(args.input_file) |